1
0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2025-07-10 12:56:42 +00:00

Merge pull request from TheSmartKid/master

Code cleanup and improved hitbox visualization
This commit is contained in:
jugeeya
2019-05-28 18:19:39 -07:00
committed by GitHub
23 changed files with 1000 additions and 892 deletions

@ -3,12 +3,12 @@
#include <switch.h>
namespace app::sv_animcmd
{
namespace app::sv_animcmd {
extern void wait(u64, float) asm("_ZN3app10sv_animcmd4waitEP9lua_Statef") LINKABLE;
extern void frame(u64, float) asm("_ZN3app10sv_animcmd5frameEP9lua_Statef") LINKABLE;
extern void is_excute(u64) asm("_ZN3app10sv_animcmd9is_excuteEP9lua_State") LINKABLE;
extern u64 ATTACK(u64) asm("_ZN3app10sv_animcmd6ATTACKEP9lua_State") LINKABLE;
extern u64 EFFECT(u64) asm("_ZN3app10sv_animcmd6EFFECTEP9lua_State") LINKABLE;
extern u64 CATCH(u64) asm("_ZN3app10sv_animcmd5CATCHEP9lua_State") LINKABLE;
extern u64 EFFECT(u64) asm("_ZN3app10sv_animcmd6EFFECTEP9lua_State") LINKABLE;
extern u64 EFFECT_FOLLOW_NO_SCALE(u64) asm("_ZN3app10sv_animcmd22EFFECT_FOLLOW_NO_SCALEEP9lua_State") LINKABLE;

@ -5,7 +5,6 @@
#include "acmd_imports.hpp"
#include "l2c_imports.hpp"
#include "lua_helper.hpp"
#include <initializer_list>
@ -20,17 +19,18 @@ void* load_module_impl(u64 module, u64 function_offset) {
return (void*) LOAD64(function_impl);
}
namespace app::sv_system
{
u64 battle_object_module_accessor(u64) asm("_ZN3app9sv_system29battle_object_module_accessorEP9lua_State") LINKABLE;
}
namespace app::sv_math {
int rand(u64 hash, int endIndex) asm("_ZN3app7sv_math4randEN3phx6Hash40Ei") LINKABLE;
int rand(u64, int) asm("_ZN3app7sv_math4randEN3phx6Hash40Ei") LINKABLE;
}
namespace app::lua_bind
{
namespace app::sv_system {
u64 battle_object(u64) asm("_ZN3app9sv_system13battle_objectEP9lua_State") LINKABLE;
u64 battle_object_module_accessor(u64) asm("_ZN3app9sv_system29battle_object_module_accessorEP9lua_State") LINKABLE;
u8 battle_object_category(u64) asm("_ZN3app9sv_system22battle_object_categoryEP9lua_State") LINKABLE;
int battle_object_kind(u64) asm("_ZN3app9sv_system18battle_object_kindEP9lua_State") LINKABLE;
}
namespace app::lua_bind {
namespace AttackModule {
void clear_all(u64) asm("_ZN3app8lua_bind28AttackModule__clear_all_implEPNS_26BattleObjectModuleAccessorE") LINKABLE;
}
@ -44,8 +44,7 @@ namespace app::lua_bind
uint req_on_joint(u64, u64, u64, const Vector3f*, const Vector3f*, float a6, const Vector3f*, const Vector3f*, bool, uint, int, int)
asm("_ZN3app8lua_bind31EffectModule__req_on_joint_implEPNS_26BattleObjectModuleAccessorEN3phx6Hash40ES4_RKNS3_8Vector3fES7_fS7_S7_bjii") LINKABLE;
void kill_kind(u64, u64, bool, bool)
asm("_ZN3app8lua_bind28EffectModule__kill_kind_implEPNS_26BattleObjectModuleAccessorEN3phx6Hash40Ebb") LINKABLE;
void kill_kind(u64, u64, bool, bool) asm("_ZN3app8lua_bind28EffectModule__kill_kind_implEPNS_26BattleObjectModuleAccessorEN3phx6Hash40Ebb") LINKABLE;
}
namespace FighterManager {
@ -75,15 +74,19 @@ namespace app::lua_bind
}
namespace WorkModule {
// PT crashes if this doesn't return a bool?
bool get_int(u64, int) asm("_ZN3app8lua_bind24WorkModule__get_int_implEPNS_26BattleObjectModuleAccessorEi") LINKABLE;
int get_param_int(u64, u64, u64) asm("_ZN3app8lua_bind30WorkModule__get_param_int_implEPNS_26BattleObjectModuleAccessorEmm") LINKABLE;
float get_float(u64, int) asm("_ZN3app8lua_bind26WorkModule__get_float_implEPNS_26BattleObjectModuleAccessorEi") LINKABLE;
bool get_int(u64, int) asm("_ZN3app8lua_bind24WorkModule__get_int_implEPNS_26BattleObjectModuleAccessorEi") LINKABLE; // this function must return a bool, otherwise Pokemon Trainer fails to load
void inc_int(u64, int) asm("_ZN3app8lua_bind24WorkModule__inc_int_implEPNS_26BattleObjectModuleAccessorEi") LINKABLE;
float get_param_float(u64, u64, u64) asm("_ZN3app8lua_bind32WorkModule__get_param_float_implEPNS_26BattleObjectModuleAccessorEmm") LINKABLE;
int get_param_int(u64, u64, u64) asm("_ZN3app8lua_bind30WorkModule__get_param_int_implEPNS_26BattleObjectModuleAccessorEmm") LINKABLE;
void on_flag(u64, int) asm("_ZN3app8lua_bind24WorkModule__on_flag_implEPNS_26BattleObjectModuleAccessorEi") LINKABLE;
void off_flag(u64, int) asm("_ZN3app8lua_bind25WorkModule__off_flag_implEPNS_26BattleObjectModuleAccessorEi") LINKABLE;
float set_float(u64, float, int) asm("_ZN3app8lua_bind26WorkModule__set_float_implEPNS_26BattleObjectModuleAccessorEfi") LINKABLE;
int set_int(u64, int, int) asm("_ZN3app8lua_bind24WorkModule__set_int_implEPNS_26BattleObjectModuleAccessorEii") LINKABLE;
}
}
struct ACMD
{
struct ACMD {
L2CAgent* l2c_agent;
u64 module_accessor;
@ -93,112 +96,137 @@ struct ACMD
}
void frame(float f) {
l2c_agent->clear_lua_stack();
L2CValue frame_val(f);
l2c_agent->push_lua_stack(&frame_val);
app::sv_animcmd::frame(l2c_agent->lua_state_agent, f);
l2c_agent->clear_lua_stack();
}
void wait(float f) {
l2c_agent->clear_lua_stack();
L2CValue frame_val(f);
l2c_agent->push_lua_stack(&frame_val);
app::sv_animcmd::wait(l2c_agent->lua_state_agent, f);
l2c_agent->clear_lua_stack();
}
bool is_excute() {
l2c_agent->clear_lua_stack();
app::sv_animcmd::is_excute(l2c_agent->lua_state_agent);
L2CValue is_excute;
l2c_agent->get_lua_stack(1, &is_excute);
bool excute = is_excute.raw;
bool excute = (bool)(is_excute);
l2c_agent->clear_lua_stack();
return excute;
}
void wrap( u64 (*acmd_func)(u64), std::initializer_list<L2CValue> list )
{
void wrap(u64 (*acmd_func)(u64), std::initializer_list<L2CValue> list) {
l2c_agent->clear_lua_stack();
for (L2CValue elem : list)
l2c_agent->push_lua_stack(&elem);
acmd_func(l2c_agent->lua_state_agent);
l2c_agent->clear_lua_stack();
}
void ATTACK(
u64 i1,
u64 i2,
u64 h1,
float f1,
u64 i3,
u64 i4,
u64 i5,
u64 i6,
float f2,
float f3,
float f4,
float f5,
//void,
//void,
//void,
float f6,
float f7,
u64 i7,
u64 i8,
u64 i9,
u64 i10,
float f8,
u64 i11,
u64 i12,
u64 i13,
u64 i14,
u64 i15,
u64 i16,
u64 i17,
u64 i18,
u64 i19,
u64 i20,
u64 h2,
u64 i21,
u64 i22,
u64 i23
) {
L2CValue hitbox_params[36] = {
L2CValue(i1), // ID
L2CValue(i2), // Unk
L2CValue(h1), // Joint
L2CValue(f1), // Damage
L2CValue(i3), // Angle
L2CValue(i4), // KBG
L2CValue(i5), // WBKB
L2CValue(i6), // BKB
L2CValue(f2), // Size
L2CValue(f3), // X
L2CValue(f4), // Y
L2CValue(f5), // Z
L2CValue("void"), // X2
L2CValue("void"), // Y2
L2CValue("void"), // Z2
L2CValue(f6), // Hitlag
L2CValue(f7), // SDI
L2CValue(i7),
L2CValue(i8),
L2CValue(i9),
L2CValue(i10),
L2CValue(f8),
L2CValue(i11),
L2CValue(i12),
L2CValue(i13),
L2CValue(i14),
L2CValue(i15),
L2CValue(i16),
L2CValue(i17),
L2CValue(i18),
L2CValue(i19),
L2CValue(i20),
L2CValue(h2),
L2CValue(i21),
L2CValue(i22),
L2CValue(i23)
};
u64 i1, // ID
u64 i2, // Part
u64 h1, // Bone
float f1, // Damage
u64 i3, // Angle
u64 i4, // KBG
u64 i5, // FKB
u64 i6, // BKB
float f2, // Size
float f3, // X
float f4, // Y
float f5, // Z
// X2
// Y2
// Z2
float f6, // Hitlag
float f7, // SDI
u64 i7, // Clang/Rebound
u64 i8, // Facing Restriction
u64 i9, // Fixed Weight
u64 i10, // Shield Damage
float f8, // Trip Chance
u64 i11, // Rehit Rate
u64 i12, // Reflectable
u64 i13, // Absorbable
u64 i14, // Flinchless
u64 i15, // Disable Hitlag
u64 i16, // Direct
u64 i17, // Ground/Air
u64 i18, // Hit Bits
u64 i19, // Collision Bits
u64 i20, // Friendly Fire
u64 h2, // Effect
u64 i21, // SFX Level
u64 i22, // SFX Type
u64 i23) { // Move Type
wrap(app::sv_animcmd::ATTACK, {
L2CValue(i1), L2CValue(i2), L2CValue(h1), L2CValue(f1),
L2CValue(i3), L2CValue(i4), L2CValue(i5), L2CValue(i6),
L2CValue(f2), L2CValue(f3), L2CValue(f4), L2CValue(f5),
L2CValue(), L2CValue(), L2CValue(), L2CValue(f6),
L2CValue(f7), L2CValue(i7), L2CValue(i8), L2CValue(i9),
L2CValue(i10), L2CValue(f8), L2CValue(i11), L2CValue(i12),
L2CValue(i13), L2CValue(i14), L2CValue(i15), L2CValue(i16),
L2CValue(i17), L2CValue(i18), L2CValue(i19), L2CValue(i20),
L2CValue(h2), L2CValue(i21), L2CValue(i22), L2CValue(i23)
});
}
for (size_t i = 0; i < 36; i++)
l2c_agent->push_lua_stack(&hitbox_params[i]);
app::sv_animcmd::ATTACK(l2c_agent->lua_state_agent);
l2c_agent->clear_lua_stack();
void ATTACK(
u64 i1, // ID
u64 i2, // Part
u64 h1, // Bone
float f1, // Damage
u64 i3, // Angle
u64 i4, // KBG
u64 i5, // FKB
u64 i6, // BKB
float f2, // Size
float f3, // X
float f4, // Y
float f5, // Z
float fX2, // X2
float fY2, // Y2
float fZ2, // Z2
float f6, // Hitlag
float f7, // SDI
u64 i7, // Clang/Rebound
u64 i8, // Facing Restriction
u64 i9, // Fixed Weight
u64 i10, // Shield Damage
float f8, // Trip Chance
u64 i11, // Rehit Rate
u64 i12, // Reflectable
u64 i13, // Absorbable
u64 i14, // Flinchless
u64 i15, // Disable Hitlag
u64 i16, // Direct
u64 i17, // Ground/Air
u64 i18, // Hit Bits
u64 i19, // Collision Bits
u64 i20, // Friendly Fire
u64 h2, // Effect
u64 i21, // SFX Level
u64 i22, // SFX Type
u64 i23) { // Move Type
wrap(app::sv_animcmd::ATTACK, {
L2CValue(i1), L2CValue(i2), L2CValue(h1), L2CValue(f1),
L2CValue(i3), L2CValue(i4), L2CValue(i5), L2CValue(i6),
L2CValue(f2), L2CValue(f3), L2CValue(f4), L2CValue(f5),
L2CValue(fX2), L2CValue(fY2), L2CValue(fZ2), L2CValue(f6),
L2CValue(f7), L2CValue(i7), L2CValue(i8), L2CValue(i9),
L2CValue(i10), L2CValue(f8), L2CValue(i11), L2CValue(i12),
L2CValue(i13), L2CValue(i14), L2CValue(i15), L2CValue(i16),
L2CValue(i17), L2CValue(i18), L2CValue(i19), L2CValue(i20),
L2CValue(h2), L2CValue(i21), L2CValue(i22), L2CValue(i23)
});
}
};

@ -1,5 +1,6 @@
#ifndef CONST_VALUE_TABLE_H
#define CONST_VALUE_TABLE_H
#define LUA_SCRIPT_LINE_MAX lua_const("LUA_SCRIPT_LINE_MAX")
#define LUA_SCRIPT_LINE_SYSTEM lua_const("LUA_SCRIPT_LINE_SYSTEM")
#define LUA_SCRIPT_LINE_SYSTEM_POST lua_const("LUA_SCRIPT_LINE_SYSTEM_POST")
@ -15877,4 +15878,5 @@
#define ITEM_ZOROARK_INSTANCE_WORK_INT_TARGET_STATE lua_const("ITEM_ZOROARK_INSTANCE_WORK_INT_TARGET_STATE")
#define ITEM_ZOROARK_INSTANCE_WORK_FLOAT_THROWUP_LR lua_const("ITEM_ZOROARK_INSTANCE_WORK_FLOAT_THROWUP_LR")
#define ITEM_ZOROARK_INSTANCE_WORK_FLAG_THROW_UP_GRAVITY_START lua_const("ITEM_ZOROARK_INSTANCE_WORK_FLAG_THROW_UP_GRAVITY_START")
#endif // CONST_VALUE_TABLE_H

@ -2,7 +2,6 @@
* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
* code or tables extracted from it, as desired without restriction.
*/
#ifndef CRC32_H
#define CRC32_H
@ -55,8 +54,7 @@ const uint32_t crc32_tab[] = {
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
uint32_t crc32_part(const void *buf, size_t size, uint32_t crc)
{
uint32_t crc32_part(const void *buf, size_t size, uint32_t crc) {
const uint8_t* p = (const uint8_t*)buf;
crc = crc ^ ~0U;
while (size--)
@ -64,8 +62,7 @@ uint32_t crc32_part(const void *buf, size_t size, uint32_t crc)
return crc ^ ~0U;
}
uint32_t crc32(const void *buf, size_t size)
{
uint32_t crc32(const void *buf, size_t size) {
const uint8_t* p = (const uint8_t*)buf;
uint32_t crc;
crc = ~0U;
@ -74,8 +71,7 @@ uint32_t crc32(const void *buf, size_t size)
return crc ^ ~0U;
}
uint64_t hash40(const char* data)
{
uint64_t hash40(const char* data) {
size_t len = strlen(data);
return crc32(data, len) | (len & 0xFF) << 32;
}

@ -1,7 +1,12 @@
#include "l2c.hpp"
#include "saltysd_helper.hpp"
#include <math.h>
#include "useful.h"
#include "useful_visual.h"
#include "l2c_imports.hpp"
#include "acmd_imports.hpp"
#include "acmd_wrapper.hpp"
#include "saltysd_helper.hpp"
#include "const_value_table.h"
#include "taunt_toggles.h"
using namespace lib;
@ -10,18 +15,23 @@ using namespace app::sv_animcmd;
void (*AttackModule_set_attack_lua_state)(u64, u64);
Vector3f id_colors[8] = {
{1.0f, 0.0f, 0.0f}, {0.7843f, 0.3529f, 1.0f},
{1.0f, 0.7843f, 0.7843f}, {0.0f, 1.0f, 0.8431f},
{1.0f, 0.4706f, 0.0f}, {0.7843f, 0.7059f, 0.0f},
{0.7843f, 0.0f, 1.0f}, {0.3765f, 0.2863f, 0.5294f},
Vector3f ID_COLORS[8] = { // used to tint the hitbox effects -- make sure that at least one component is equal to 1.0
{ 1.0f, 0.0f, 0.0f }, // #ff0000 (red)
{ 1.0f, 0.4f, 0.0f }, // #ff9900 (orange)
{ 0.8f, 1.0f, 0.0f }, // #ccff00 (yellow)
{ 0.2f, 1.0f, 0.2f }, // #00ff33 (green)
{ 0.0f, 0.8f, 1.0f }, // #00ccff (sky blue)
{ 0.4f, 0.4f, 1.0f }, // #6666ff (blue)
{ 0.8f, 0.0f, 1.0f }, // #cc00ff (purple)
{ 1.0f, 0.2f, 0.8f }, // #ff33cc (pink)
};
int MAX_EFFECTS_PER_HITBOX = 16; // max # of circles drawn for an extended hitbox
namespace app::lua_bind::AttackModule {
// Clear graphics every time we clear all hitboxes
// clear graphics every time we clear all hitboxes
void clear_all_replace(u64 module_accessor) {
if (is_training_mode()) {
// Only if we're not shielding
// only if we're not shielding
int status_kind = StatusModule::status_kind(module_accessor);
if (!(status_kind >= FIGHTER_STATUS_KIND_GUARD_ON && status_kind <= FIGHTER_STATUS_KIND_GUARD_OFF)) {
Hash40 shieldEffectHash = { .hash = 0xAFAE75F05LL };
@ -37,87 +47,106 @@ namespace app::lua_bind::AttackModule {
}
}
void generate_hitbox_effects(L2CAgent *l2c_agent, L2CValue *id, L2CValue *bone,
L2CValue *size, L2CValue *x, L2CValue *y,
L2CValue *z, L2CValue *x2, L2CValue *y2,
L2CValue *z2) {
Vector3f color = id_colors[id->raw % 8];
L2CValue red(color.x);
L2CValue green(color.y);
L2CValue blue(color.z);
void generate_hitbox_effects(L2CAgent *l2c_agent, L2CValue *bone, L2CValue *size,
L2CValue *x, L2CValue *y, L2CValue *z, L2CValue *x2, L2CValue *y2, L2CValue *z2,
Vector3f *color) {
L2CValue red(color->x);
L2CValue green(color->y);
L2CValue blue(color->z);
float size_mult = 19.0f / 200.0f;
Hash40 shield_effect_hash = { .hash = 0xAFAE75F05LL };
float sizeMult = 19.0 / 200.0;
Hash40 shieldEffectHash = {.hash = 0xAFAE75F05LL};
L2CValue shieldEffect(shieldEffectHash.hash);
L2CValue xRot(0.0f);
L2CValue yRot(0.0f);
L2CValue zRot(0.0f);
L2CValue shieldEffect(shield_effect_hash.hash);
L2CValue x_rot(0.0f);
L2CValue y_rot(0.0f);
L2CValue z_rot(0.0f);
L2CValue terminate(true);
L2CValue effectSize((float)size->raw_float * sizeMult);
L2CValue effect_size((float)size->raw_float * size_mult);
L2CValue rate(8.0f);
// Extended Hitboxes if x2, y2, z2 are not L2CValue::nil
int num_effects;
if (x2->type != L2C_void && y2->type != L2C_void && z2->type != L2C_void) {
num_effects = 4;
} else {
*x2 = *x;
*y2 = *y;
*z2 = *z;
num_effects = 1;
float x_dist, y_dist, z_dist;
int n_effects;
if (x2->type != L2C_void && y2->type != L2C_void && z2->type != L2C_void) { // extended hitbox
x_dist = x2->raw_float - x->raw_float;
y_dist = y2->raw_float - y->raw_float;
z_dist = z2->raw_float - z->raw_float;
float dist = sqrtf(x_dist * x_dist + y_dist * y_dist + z_dist * z_dist);
n_effects = (int)ceilf(dist / (size->raw_float * 1.75f)) + 1; // just enough effects to form a continuous line
if (n_effects < 2)
n_effects = 2;
if (n_effects > MAX_EFFECTS_PER_HITBOX)
n_effects = MAX_EFFECTS_PER_HITBOX;
} else { // non-extended hitbox
x_dist = y_dist = z_dist = 0;
n_effects = 1;
}
for (int i = 0; i < num_effects; i++) {
L2CValue currX(x->raw_float + ((x2->raw_float - x->raw_float) / 3 * i));
L2CValue currY(y->raw_float + ((y2->raw_float - y->raw_float) / 3 * i));
L2CValue currZ(z->raw_float + ((z2->raw_float - z->raw_float) / 3 * i));
for (int i = 0; i < n_effects; i++) {
float t = n_effects <= 1 ? 0 : (float)i / (n_effects - 1);
L2CValue x_curr(x->raw_float + x_dist * t);
L2CValue y_curr(y->raw_float + y_dist * t);
L2CValue z_curr(z->raw_float + z_dist * t);
ACMD acmd(l2c_agent);
acmd.wrap(EFFECT_FOLLOW_NO_SCALE,
{shieldEffect, *bone, currX, currY, currZ, xRot, yRot, zRot, effectSize, terminate}
);
acmd.wrap(EFFECT_FOLLOW_NO_SCALE, { shieldEffect, *bone, x_curr, y_curr, z_curr, x_rot, y_rot, z_rot, effect_size, terminate });
// Set to hitbox ID color
// set to hitbox ID color
acmd.wrap(LAST_EFFECT_SET_COLOR, { red, green, blue });
// Speed up animation by rate to remove pulsing effect
// LAST_EFFECT_SET_RATE(Rate)
// speed up animation by rate to remove pulsing effect
acmd.wrap(LAST_EFFECT_SET_RATE, { rate });
}
}
namespace app::sv_animcmd {
void ATTACK_replace(u64 a1) {
// Instantiate our own L2CAgent with the given lua_State
// instantiate our own L2CAgent with the given lua_State
L2CAgent l2c_agent;
l2c_agent.L2CAgent_constr(a1);
// Get all necessary hitbox params
L2CValue id, bone, damage, angle, kbg, wkb, bkb, size, x, y, z, x2, y2, z2;
l2c_agent.get_lua_stack(1, &id);
l2c_agent.get_lua_stack(3, &bone);
l2c_agent.get_lua_stack(4, &damage);
l2c_agent.get_lua_stack(5, &angle);
l2c_agent.get_lua_stack(6, &kbg);
l2c_agent.get_lua_stack(7, &wkb);
l2c_agent.get_lua_stack(8, &bkb);
l2c_agent.get_lua_stack(9, &size);
l2c_agent.get_lua_stack(10, &x);
l2c_agent.get_lua_stack(11, &y);
l2c_agent.get_lua_stack(12, &z);
l2c_agent.get_lua_stack(13, &x2);
l2c_agent.get_lua_stack(14, &y2);
l2c_agent.get_lua_stack(15, &z2);
// get all necessary hitbox params
L2CValue id, bone, damage, angle, kbg, fkb, bkb, size, x, y, z, x2, y2, z2;
l2c_agent.get_lua_stack(1, &id); // int
l2c_agent.get_lua_stack(3, &bone); // hash40
l2c_agent.get_lua_stack(4, &damage); // float
l2c_agent.get_lua_stack(5, &angle); // int
l2c_agent.get_lua_stack(6, &kbg); // int
l2c_agent.get_lua_stack(7, &fkb); // int
l2c_agent.get_lua_stack(8, &bkb); // int
l2c_agent.get_lua_stack(9, &size); // float
l2c_agent.get_lua_stack(10, &x); // float
l2c_agent.get_lua_stack(11, &y); // float
l2c_agent.get_lua_stack(12, &z); // float
l2c_agent.get_lua_stack(13, &x2); // float or void
l2c_agent.get_lua_stack(14, &y2); // float or void
l2c_agent.get_lua_stack(15, &z2); // float or void
// original code: parse lua stack and call AttackModule::set_attack()
AttackModule_set_attack_lua_state(LOAD64(LOAD64(a1 - 8) + 416LL), a1);
if (HITBOX_VIS && is_training_mode()) {
// Generate hitbox effect(s)
generate_hitbox_effects(&l2c_agent, &id, &bone, &size, &x, &y, &z, &x2, &y2, &z2);
if (HITBOX_VIS && is_training_mode()) { // generate hitbox effect(s)
float color_scale;
if (false) { // color intensity scales with damage
color_scale = unlerp_bounded(1.0f, 18.0f, damage.raw_float);
} else { // color intensity scales with total KB
// calculate the expected KB a character with 95 weight will receive at 80% pre-hit
float TARGET_PERCENT = 80.0f;
int TARGET_WEIGHT = 95;
float percent_component;
if (fkb.raw > 0) {
percent_component = (10.0f + fkb.raw) * 0.1f * (1.0f + fkb.raw * 0.5f);
} else {
percent_component = (TARGET_PERCENT + damage.raw_float) * 0.1f * (1.0f + damage.raw_float * 0.5f);
}
float weight_component = 200.0f / (TARGET_WEIGHT + 100);
float kb = (percent_component * weight_component * 1.4f + 18.0f) * (kbg.raw * 0.01f) + bkb.raw;
color_scale = unlerp_bounded(50.0f, 200.0f, kb);
}
float color_t = 0.6f + 0.4f * powf(color_scale, 0.5f); // non-linear scaling to magnify differences at lower values
Vector3f color = color_lerp({ 1.0f, 1.0f, 1.0f }, ID_COLORS[id.raw % 8], color_t);
generate_hitbox_effects(&l2c_agent, &bone, &size, &x, &y, &z, &x2, &y2, &z2, &color);
}
u64 v1, v2, i;
@ -134,13 +163,10 @@ namespace app::sv_animcmd {
}
void hitbox_vis_main() {
AttackModule_set_attack_lua_state =
(void (*)(u64, u64))SaltySDCore_FindSymbol("_ZN3app10sv_animcmd6ATTACKEP9lua_State") + 0xD0 - 0x70;
AttackModule_set_attack_lua_state = (void (*)(u64, u64))SaltySDCore_FindSymbol("_ZN3app10sv_animcmd6ATTACKEP9lua_State") + 0xD0 - 0x70;
SaltySD_function_replace_sym(
"_ZN3app10sv_animcmd6ATTACKEP9lua_State",
(u64)&ATTACK_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind28AttackModule__clear_all_implEPNS_26BattleObjectModuleAccessorE",
(u64)&AttackModule::clear_all_replace);

@ -1,16 +0,0 @@
#ifndef L2C_H
#define L2C_H
typedef struct Hash40
{
uint64_t hash : 40;
} Hash40;
typedef struct Vector3f
{
float x;
float y;
float z;
} Vector3f;
#endif // L2C_H

@ -1,18 +1,18 @@
#ifndef L2C_IMPORTS_H
#define L2C_IMPORTS_H
#include <switch.h>
#include "l2c.hpp"
#include "lua_bind_hash.hpp"
#include <math.h>
#define LOAD64 *(u64 *)
#include "useful.h"
#include "lua_bind_hash.hpp"
u64 is_training_mode(void) asm("_ZN3app9smashball16is_training_modeEv") LINKABLE;
namespace lib
{
enum L2CVarType
{
namespace lib {
enum L2CVarType {
L2C_void = 0,
L2C_bool = 1,
L2C_integer = 2,
@ -24,16 +24,14 @@ namespace lib
L2C_string = 8,
};
struct L2CTable_meta
{
typedef struct L2CTable_meta {
uint64_t a;
uint64_t b;
uint64_t c;
uint64_t d;
};
} L2CTable_meta;
struct L2CTable
{
typedef struct L2CTable {
uint32_t refcnt;
uint32_t unk;
@ -42,53 +40,68 @@ namespace lib
uint64_t also_end; // L2CValue*
struct L2CTable_meta meta;
uint64_t unk_ptr;
};
} L2CTable;
struct L2CInnerFunctionBase
{
typedef struct L2CInnerFunctionBase {
uint64_t unk;
uint32_t refcnt;
} L2CInnerFunctionBase;
struct L2CValue
{
struct L2CValue {
uint32_t type;
uint32_t unk;
union
{
union {
uint64_t raw;
float raw_float;
void* raw_pointer;
struct L2CTable* raw_table;
struct L2CInnerFunctionBase* raw_innerfunc;
// void* raw_pointer;
// struct L2CTable* raw_table;
// struct L2CInnerFunctionBase* raw_innerfunc;
//std::string* raw_string;
};
L2CValue() {}
L2CValue() {
type = L2C_void;
}
L2CValue(bool val) {
type = L2C_bool;
raw = val;
}
L2CValue(int val) {
type = L2C_integer;
raw = val;
}
L2CValue(u64 val) {
type = L2C_integer;
raw = val;
}
L2CValue(float val) {
if (isnan(val)) {
type = L2C_void;
} else {
type = L2C_number;
raw_float = val;
}
L2CValue(const char* str) {
type = L2C_void;
}
L2CValue(double val) {
if (isnan(val)) {
type = L2C_void;
} else {
type = L2C_number;
raw_float = val;
}
}
operator bool() asm("_ZNK3lib8L2CValuecvbEv") LINKABLE;
void push_variadic(u64, const char*, void*) asm("_ZN3lib8L2CValue13push_variadicEmPKcRNS_7utility8VariadicE") LINKABLE;
};
struct L2CAgent
{
struct L2CAgent {
uint64_t vtable;
uint64_t lua_state_agent;
uint64_t unk10;

@ -1,3 +1,6 @@
#ifndef LUA_BIND_HASH_H
#define LUA_BIND_HASH_H
#include <stdio.h>
#include <string.h>
#include <stdint.h>
@ -67,8 +70,7 @@ uint64_t lua_bind_hash(const void* data_, size_t len) {
hash += hash_add;
for (; data + 1 <= data_end; data++) {
hash = (*data * MULT5 | (*data * MULT4) >> 0x21) *
MULT3 ^ hash;
hash = (*data * MULT5 | (*data * MULT4) >> 0x21) * MULT3 ^ hash;
hash = (hash >> 0x25 | hash << 0x1b) * MULT3 + ADD1;
}
@ -95,3 +97,5 @@ uint64_t lua_bind_hash(const void* data_, size_t len) {
uint64_t lua_bind_hash_str(const char* str) {
return lua_bind_hash(str, strlen(str));
}
#endif // LUA_BIND_HASH_H

@ -1,8 +0,0 @@
#ifndef LUA_HELPER_H
#define LUA_HELPER_H
#include <switch.h>
#include "l2c_imports.hpp"
#endif // LUA_HELPER_H

@ -1,7 +1,6 @@
#include <switch.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/iosupport.h>
@ -15,9 +14,11 @@
#include "saltysd_dynamic.h"
#include "saltysd_helper.hpp"
#include "l2c_imports.hpp"
#include "acmd_imports.hpp"
#include "script_replacement.hpp"
#include "hitbox_visualizer.hpp"
#include "script_replacement.hpp"
#include "training_mods.hpp"
extern "C" {
@ -38,8 +39,7 @@ Handle orig_main_thread;
void* orig_ctx;
void* orig_saved_lr;
void __libnx_init(void* ctx, Handle main_thread, void* saved_lr)
{
void __libnx_init(void* ctx, Handle main_thread, void* saved_lr) {
extern char* fake_heap_start;
extern char* fake_heap_end;
@ -55,8 +55,7 @@ void __libnx_init(void* ctx, Handle main_thread, void* saved_lr)
__libc_init_array();
}
void __attribute__((weak)) NORETURN __libnx_exit(int rc)
{
void __attribute__((weak)) NORETURN __libnx_exit(int rc) {
// Call destructors.
//void __libc_fini_array(void);
__libc_fini_array();
@ -67,8 +66,7 @@ void __attribute__((weak)) NORETURN __libnx_exit(int rc)
while (true);
}
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
SaltySD_printf("SaltySD Plugin: alive\n");
// Get anchor for imports
@ -90,9 +88,9 @@ int main(int argc, char *argv[])
// Necessary for script replacement
SaltySD_function_replace_sym("_ZN3lib8L2CAgent15clear_lua_stackEv", (u64) &clear_lua_stack_replace);
// Add function replacements here
hitbox_vis_main();
training_mods_main();
__libnx_exit(0);
}

@ -1,3 +1,6 @@
#ifndef NN_RO_H
#define NN_RO_H
#include <switch.h>
#define nn_ro_LoadModule _ZN2nn2ro10LoadModuleEPNS0_6ModuleEPKvPvmi
@ -76,3 +79,4 @@ typedef struct SmashModule { /* PlaceHolder Structure */
undefined4 is_loaded; // bool
} SmashModule;
# endif // NN_RO_H

@ -150,9 +150,9 @@ void print_char( u64 module_accessor, char to_print, int line_num, float horiz_o
const float* segment;
int index = segment_str[i] - 'a';
if (facing_left == -1)
if (facing_left == -1) {
index = segment_rev[index] - 'a';
}
segment = segment_dict[index];
float z = segment[0];
@ -179,8 +179,9 @@ void print_string( u64 module_accessor, const char* print_str) {
float facing_left = PostureModule::lr(module_accessor);
if (strlen(print_str) <= 8 && strchr(print_str, '\n') == NULL)
if (strlen(print_str) <= 8 && strchr(print_str, '\n') == NULL) {
line_num = 1;
}
horiz_offset = 0;
char_num = 0;
for (size_t i = 0; i < strlen(print_str); i++) {
@ -196,10 +197,11 @@ void print_string( u64 module_accessor, const char* print_str) {
char_num++;
// short characters
if (curr_char == 'D' || curr_char == '1' )
if (curr_char == 'D' || curr_char == '1') {
horiz_offset += facing_left * (RAYGUN_LENGTH/2 + 3);
else
} else {
horiz_offset += facing_left * (RAYGUN_LENGTH + 3);
}
if (char_num > 8) {
horiz_offset = 0;

36
source/saltysd_helper.cpp Normal file

@ -0,0 +1,36 @@
#include <switch.h>
#include "saltysd_core.h"
#include "saltysd_ipc.h"
#include "saltysd_dynamic.h"
#include "nn_ro.h"
void (*SaltySD_installed_hook)(char*, u64) = NULL;
int SaltySD_function_replace(u64 addr, u64 new_func) {
if (addr) {
SaltySD_Memcpy(addr, (u64) "\x49\x00\x00\x58", 4); // LDR X9, .+8
SaltySD_Memcpy(addr+4, (u64) "\x20\x01\x1F\xD6", 4); // BR X9
SaltySD_Memcpy(addr+8, (u64) &new_func, 8); // .dword newaddr
return 0;
}
return -1;
}
int SaltySD_function_replace_sym(char* function_sym, u64 new_func) {
u64 addr = SaltySDCore_FindSymbol(function_sym);
return SaltySD_function_replace(addr, new_func);
}
void LoadModule(SmashModule *module, void *param_2, void *param_3, unsigned long param_4, int param_5) {
nn_ro_LoadModule(module, param_2, param_3, param_4, param_5);
if (SaltySD_installed_hook != NULL) {
SaltySD_installed_hook((char*)&module->name, (u64)module->module.module->module_base);
}
}
void SaltySD_install_nro_hook(u64 LoadModule_thunk_addr, void hook_main(char*, u64)) {
SaltySD_installed_hook = hook_main;
SaltySD_function_replace(LoadModule_thunk_addr, (u64) LoadModule);
}

@ -2,43 +2,12 @@
#define SALTYSD_HELPER_H
#include <switch.h>
#include "saltysd_core.h"
#include "saltysd_ipc.h"
#include "saltysd_dynamic.h"
#include "nn_ro.h"
#define ANCHOR_REL 0x70ffffc000
u64 ANCHOR_ABS;
#define IMPORT(x) (x - ANCHOR_REL + ANCHOR_ABS)
void (*SaltySD_installed_hook)(char*, u64) = NULL;
int SaltySD_function_replace(u64 addr, u64 new_func) {
if (addr) {
SaltySD_Memcpy(addr, (u64) "\x49\x00\x00\x58", 4); // LDR X9, .+8
SaltySD_Memcpy(addr+4, (u64) "\x20\x01\x1F\xD6", 4); // BR X9
SaltySD_Memcpy(addr+8, (u64) &new_func, 8); // .dword newaddr
return 0;
}
return -1;
}
int SaltySD_function_replace_sym(char* function_sym, u64 new_func) {
u64 addr = SaltySDCore_FindSymbol(function_sym);
return SaltySD_function_replace(addr, new_func);
}
void LoadModule(SmashModule *module, void *param_2, void *param_3, unsigned long param_4, int param_5) {
nn_ro_LoadModule(module, param_2, param_3, param_4, param_5);
if(SaltySD_installed_hook != NULL) {
SaltySD_installed_hook((char*)&module->name, (u64)module->module.module->module_base);
}
}
void SaltySD_install_nro_hook(u64 LoadModule_thunk_addr, void hook_main(char*, u64)) {
SaltySD_installed_hook = hook_main;
SaltySD_function_replace(LoadModule_thunk_addr, (u64) LoadModule);
}
int SaltySD_function_replace(u64 addr, u64 new_func);
int SaltySD_function_replace_sym(char* function_sym, u64 new_func);
#endif // SALTYSD_HELPER_H

@ -3,11 +3,10 @@
#include <stdint.h>
#include "crc32.h"
#include "useful.h"
#include "l2c.hpp"
#include "l2c_imports.hpp"
#include "acmd_wrapper.hpp"
#include "lua_helper.hpp"
#include "raygun_printer.hpp"
@ -81,21 +80,6 @@ u64 appeal_s_replace(L2CAgent* l2c_agent, void* variadic) {
return 0;
}
// AnimCMD replacement function
u64 shine_replace(L2CAgent* l2c_agent, void* variadic) {
ACMD acmd = ACMD(l2c_agent);
acmd.frame(1);
if (acmd.is_excute()) {
acmd.ATTACK(0, 0, hash40("top"), 10.0, 10, 32, 0, 66, 7.5, 0, 6.5,
// 0, 0, 0, //L2C_voids: no X2, Y2, Z2
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 63, 31, 0,
0x13462FCFE4LL, 2, 7, 25);
}
return 0;
}
void* sv_get_status_func(u64 l2c_agentbase, int status_kind, u64 key) {
u64 unk48 = LOAD64(l2c_agentbase + 0x48);
u64 unk50 = LOAD64(l2c_agentbase + 0x50);
@ -115,7 +99,7 @@ void sv_replace_status_func(u64 l2c_agentbase, int status_kind, u64 key, void* f
u64 clear_lua_stack_replace(u64 l2c_agent) {
u64 lua_state = LOAD64(l2c_agent + 8);
if (lua_state-8 && LOAD64(lua_state-8) && LOAD64(LOAD64(lua_state - 8) + 416LL)) {
if ((lua_state - 8) && LOAD64(lua_state - 8) && (LOAD64(LOAD64(lua_state - 8) + 416LL))) {
u8 battle_object_category = *(u8 *)(LOAD64(lua_state - 8) + 404LL);
int battle_object_kind = *(int *)(LOAD64(lua_state - 8) + 408LL);
replace_scripts((L2CAgent*)l2c_agent, battle_object_category, battle_object_kind);

@ -1,6 +1,6 @@
#include "l2c.hpp"
#include "saltysd_helper.hpp"
#include "useful.h"
#include "l2c_imports.hpp"
#include "saltysd_helper.hpp"
#include "acmd_imports.hpp"
#include "taunt_toggles.h"
#include "raygun_printer.hpp"
@ -32,8 +32,9 @@ void perform_jump(u64 module_accessor) {
StatusModule::change_status_request_from_script(module_accessor, FIGHTER_STATUS_KIND_JUMP_AERIAL, 1);
else
StatusModule::change_status_request_from_script(module_accessor, FIGHTER_STATUS_KIND_FLY, 1);
} else if (StatusModule::situation_kind(module_accessor) == SITUATION_KIND_GROUND)
} else if (StatusModule::situation_kind(module_accessor) == SITUATION_KIND_GROUND) {
StatusModule::change_status_request_from_script(module_accessor, FIGHTER_STATUS_KIND_JUMP_SQUAT, 1);
}
WorkModule::inc_int(module_accessor, FIGHTER_INSTANCE_WORK_ID_INT_JUMP_COUNT);
}
}
@ -43,13 +44,10 @@ namespace app::lua_bind::WorkModule {
u64 enable_transition_term_group_replace(u64 module_accessor, int transition_group) {
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (is_in_hitstun(module_accessor)) {
// Airdodge
if (TOGGLE_STATE == MASH_AIRDODGE) {
if (TOGGLE_STATE == MASH_AIRDODGE) { // airdodge
if (transition_group == FIGHTER_STATUS_TRANSITION_GROUP_CHK_AIR_ESCAPE)
StatusModule::change_status_request_from_script(module_accessor, FIGHTER_STATUS_KIND_ESCAPE_AIR, 1);
}
// Jump
else if (TOGGLE_STATE == MASH_JUMP) {
} else if (TOGGLE_STATE == MASH_JUMP) { // jump
if (transition_group == FIGHTER_STATUS_TRANSITION_GROUP_CHK_AIR_JUMP_AERIAL)
perform_jump(module_accessor);
else if (transition_group == FIGHTER_STATUS_TRANSITION_GROUP_CHK_GROUND_JUMP)
@ -100,17 +98,14 @@ namespace app::lua_bind::WorkModule {
namespace app::lua_bind::MotionModule {
void change_motion_replace(u64 module_accessor, u64 motion_kind, float start_frame, float frame_speed_mult, bool unk1, float unk2, bool unk3, bool unk4) {
u64 curr_motion_kind = MotionModule::motion_kind(module_accessor);
if ((curr_motion_kind == hash40("damage_air_1") ||
curr_motion_kind == hash40("damage_air_2") ||
curr_motion_kind == hash40("damage_air_3")) && motion_kind == hash40("fall")) {
if (is_training_mode() && is_operation_cpu(module_accessor)) {
// Airdodge
if (TOGGLE_STATE == MASH_AIRDODGE)
if (TOGGLE_STATE == MASH_AIRDODGE) // airdodge
StatusModule::change_status_request_from_script(module_accessor, FIGHTER_STATUS_KIND_ESCAPE_AIR, 1);
// Jump
else if (TOGGLE_STATE == MASH_JUMP)
else if (TOGGLE_STATE == MASH_JUMP) // jump
perform_jump(module_accessor);
}
}
@ -126,15 +121,12 @@ namespace app::lua_bind::MotionModule {
void training_mods_main() {
fighter_manager_addr = SaltySDCore_FindSymbol("_ZN3lib9SingletonIN3app14FighterManagerEE9instance_E");
SaltySD_function_replace_sym(
"_ZN3app8lua_bind45WorkModule__enable_transition_term_group_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&WorkModule::enable_transition_term_group_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind26WorkModule__get_float_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&WorkModule::get_float_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind32MotionModule__change_motion_implEPNS_26BattleObjectModuleAccessorEN3phx6Hash40Effbfbb",
(u64)&MotionModule::change_motion_replace);

@ -7,8 +7,20 @@
#define LINKABLE __attribute__ ((weak))
#define debug_log(...) \
{char log_buf[0x200]; snprintf(log_buf, 0x200, __VA_ARGS__); \
#define LOAD64 *(u64 *)
#define debug_log(...) {\
char log_buf[0x200]; snprintf(log_buf, 0x200, __VA_ARGS__); \
svcOutputDebugString(log_buf, strlen(log_buf)); }
typedef struct Hash40 {
uint64_t hash : 40;
} Hash40;
typedef struct Vector3f {
float x;
float y;
float z;
} Vector3f;
#endif // USEFUL_H

35
source/useful_visual.cpp Normal file

@ -0,0 +1,35 @@
#include "useful_visual.h"
#include <math.h>
#include "useful.h"
float round_to(float val, float align) {
return roundf(val / align) * align;
}
float lerp(float min, float max, float t) {
return min + (max - min) * t;
}
float unlerp(float min, float max, float val) {
return (val - min) / (max - min);
}
float lerp_bounded(float min, float max, float t) {
return t <= 0 ? min : t >= 1 ? max : lerp(min, max, t);
}
float unlerp_bounded(float min, float max, float val) {
return val <= min ? 0 : val >= max ? 1 : unlerp(min, max, val);
}
Vector3f color_lerp(Vector3f min_color, Vector3f max_color, float t, float gamma) {
float gamma_inv = 1.0f / gamma;
float align = 1.0f / 255.0f; // color components must be a multiple of 1/255
return {
round_to(powf(lerp_bounded(powf(min_color.x, gamma), powf(max_color.x, gamma), t), gamma_inv), align),
round_to(powf(lerp_bounded(powf(min_color.y, gamma), powf(max_color.y, gamma), t), gamma_inv), align),
round_to(powf(lerp_bounded(powf(min_color.z, gamma), powf(max_color.z, gamma), t), gamma_inv), align)
};
}

31
source/useful_visual.h Normal file

@ -0,0 +1,31 @@
#ifndef USEFUL_VISUAL_H
#define USEFUL_VISUAL_H
#include "useful.h"
/**
* Rounds a number to the nearest multiple of another number.
*/
float round_to(float val, float align);
/**
* Linearly interpolates between two numbers, without bounds checking.
*/
float lerp(float min, float max, float t);
float unlerp(float min, float max, float val);
/**
* Linearly interpolates between two numbers, with bounds checking.
*/
float lerp_bounded(float min, float max, float t);
float unlerp_bounded(float min, float max, float val);
/**
* Linearly nterpolates between two colors, with bounds checking, accounting for gamma.
* arguments:
* - min_color (Vector3f) -- xyz maps to rgb, components are usually in the range [0.0f, 1.0f] but can go beyond to account for super-bright or super-dark colors
* - max_Color (Vector3f) -- same as minColor
* - t (float) -- how far to interpolate between the colors
* - gamma (float = 2.0f) -- used for color correction, helps avoid ugly dark colors when interpolating b/t bright colors
*/
Vector3f color_lerp(Vector3f min_color, Vector3f max_color, float t, float gamma = 2.0f);
#endif // USEFUL_VISUAL_H