1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2025-01-31 22:47:25 +00:00

change formatting (google style, 4 tab size)

This commit is contained in:
jugeeya 2019-06-20 12:12:31 -07:00
parent 1e9762d0d8
commit aa23f87d6b
7 changed files with 971 additions and 984 deletions

View file

@ -3,10 +3,10 @@
#include <switch.h>
#include "imports/app/sv_animcmd.hpp"
#include "imports/app/sv_system.hpp"
#include "imports/app/sv_math.hpp"
#include "imports/app/lua_bind.hpp"
#include "imports/app/sv_animcmd.hpp"
#include "imports/app/sv_math.hpp"
#include "imports/app/sv_system.hpp"
#include "imports/lib/l2c.hpp"
#include <initializer_list>
@ -14,71 +14,71 @@
using namespace lib;
u64 load_module(u64 module_accessor, u64 module_offset) {
return LOAD64(module_accessor + module_offset);
return LOAD64(module_accessor + module_offset);
}
void* load_module_impl(u64 module, u64 function_offset) {
u64 function_impl = LOAD64(module) + function_offset;
return (void*) LOAD64(function_impl);
u64 function_impl = LOAD64(module) + function_offset;
return (void*)LOAD64(function_impl);
}
bool is_before_frame(u64 lua_state, float f) {
u64 acmd_frame_obj = LOAD64(LOAD64(lua_state - 8) + 432LL);
return *(float*)((*((u32 *)acmd_frame_obj + 64) + 15) & 0xFFFFFFF0) < f;
u64 acmd_frame_obj = LOAD64(LOAD64(lua_state - 8) + 432LL);
return *(float*)((*((u32*)acmd_frame_obj + 64) + 15) & 0xFFFFFFF0) < f;
}
struct ACMD {
L2CAgent* l2c_agent;
u64 module_accessor;
L2CAgent* l2c_agent;
u64 module_accessor;
ACMD(L2CAgent* agent) {
l2c_agent = agent;
module_accessor = app::sv_system::battle_object_module_accessor(l2c_agent->lua_state_agent);
}
ACMD(L2CAgent* agent) {
l2c_agent = agent;
module_accessor = app::sv_system::battle_object_module_accessor(
l2c_agent->lua_state_agent);
}
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 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();
}
// attempted reimplementation of sv_animcmd::frame
bool _frame(float f) {
// attempted reimplementation of sv_animcmd::frame
bool _frame(float f) {
u64 acmd_obj = LOAD64(LOAD64(l2c_agent->lua_state_agent - 8) + 432);
if ( !is_before_frame(l2c_agent->lua_state_agent, f) )
return true;
if (!is_before_frame(l2c_agent->lua_state_agent, f)) return true;
*(u8*)(acmd_obj + 47) = 3;
LOAD64(acmd_obj + 48) = (u64)&is_before_frame;
*(float*)(acmd_obj + 56) = f;
u64 acmd_obj_other = LOAD64(acmd_obj);
if (*(u8*)(acmd_obj_other + 664)) {
void (*some_func)(u64) = (void (*)(u64)) LOAD64(LOAD64(LOAD64(acmd_obj_other + 656)) + 16);
void (*some_func)(u64) = (void (*)(u64))LOAD64(
LOAD64(LOAD64(acmd_obj_other + 656)) + 16);
some_func(LOAD64(acmd_obj_other + 656));
return true;
}
bool doThing = false;
if ( !*(u16*)(l2c_agent->lua_state_agent + 196)) {
if (!*(u16*)(l2c_agent->lua_state_agent + 196)) {
u64 v4 = LOAD64(l2c_agent->lua_state_agent + 32);
*(u8*)(l2c_agent->lua_state_agent + 12) = 1;
LOAD64(v4 + 56) = LOAD64(v4) - LOAD64(l2c_agent->lua_state_agent + 56);
if ( *(u8*)(v4 + 66) & 2)
return true;
LOAD64(v4 + 56) =
LOAD64(v4) - LOAD64(l2c_agent->lua_state_agent + 56);
if (*(u8*)(v4 + 66) & 2) return true;
doThing = true;
}
if ( doThing || LOAD64(LOAD64(l2c_agent->lua_state_agent + 24) + 200) == l2c_agent->lua_state_agent) {
// throw
if (doThing || LOAD64(LOAD64(l2c_agent->lua_state_agent + 24) + 200) ==
l2c_agent->lua_state_agent) {
// throw
u64 v4 = LOAD64(l2c_agent->lua_state_agent + 32);
LOAD64(v4 + 32) = 0;
LOAD64(v4) = LOAD64(l2c_agent->lua_state_agent + 16) - 16;
}
return false;
@ -86,132 +86,130 @@ struct ACMD {
// throw
}
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();
}
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 = (bool)(is_excute);
l2c_agent->clear_lua_stack();
return excute;
}
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 = (bool)(is_excute);
l2c_agent->clear_lua_stack();
return excute;
}
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);
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();
}
acmd_func(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
// 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, // Rehite 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("void"), L2CValue("void"), L2CValue("void"), 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)
});
}
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
// 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, // Rehite 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("void"), L2CValue("void"), L2CValue("void"),
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)});
}
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, // Rehite 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)
});
}
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, // Rehite 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)});
}
};
#endif // ACMD_WRAPPER_H
#endif // ACMD_WRAPPER_H

View file

@ -6,11 +6,11 @@
#include "useful/useful.h"
#include "useful/visual.h"
#include "imports/lib/l2c.hpp"
#include "acmd_wrapper.hpp"
#include "imports/lib/l2c.hpp"
#include "saltysd/saltysd_helper.hpp"
#include "useful/const_value_table.h"
#include "taunt_toggles.h"
#include "useful/const_value_table.h"
#include "useful/raygun_printer.hpp"
@ -23,273 +23,286 @@ u64 effect_manager_addr;
void (*AttackModule_set_attack_lua_state)(u64, u64);
u64 Catch_jumpback;
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)
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
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
void clear_all_replace(u64 module_accessor) {
if (is_training_mode()) {
// 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 };
EffectModule::kill_kind(module_accessor, shieldEffectHash.hash, 0, 1);
}
}
// 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
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};
EffectModule::kill_kind(module_accessor, shieldEffectHash.hash, 0, 1);
}
}
// call original AttackModule::clear_all_impl
u64 attack_module = load_module(module_accessor, 0xA0);
void (*clear_all)(u64) = (void(*)(u64))(load_module_impl(attack_module, 0x50));
// call original AttackModule::clear_all_impl
u64 attack_module = load_module(module_accessor, 0xA0);
void (*clear_all)(u64) =
(void (*)(u64))(load_module_impl(attack_module, 0x50));
return clear_all(attack_module);
}
return clear_all(attack_module);
}
} // namespace app::lua_bind::AttackModule
namespace app::lua_bind::GrabModule {
// clear graphics every time we clear rebound
void set_rebound_replace(u64 module_accessor, bool rebound) {
if (is_training_mode() && rebound == false) {
// 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 };
EffectModule::kill_kind(module_accessor, shieldEffectHash.hash, 0, 1);
}
}
// call original GrabModule::set_rebound_impl
u64 grab_module = load_module(module_accessor, 0x158);
void (*set_rebound)(u64, bool) = (void(*)(u64, bool))(load_module_impl(grab_module, 0x100));
return set_rebound(grab_module, rebound);
}
}
Vector3f EffectModule_last_get_scale_w(u64 effect_module)
{
Vector3f ret;
uint handle = *(uint *)(effect_module + 36);
if ( handle && (signed int)handle >= 1 )
{
u64 effect = LOAD64(effect_manager_addr) + 768 * (handle >> 24);
bool is_exist_effect = effect && *(uint *)(effect + 4) == handle;
if ( is_exist_effect )
{
float* scale = (float*)(effect + 256);
ret.x = *(float *)(scale);
ret.y = *(float *)(scale+1);
ret.z = *(float *)(scale+2);
// clear graphics every time we clear rebound
void set_rebound_replace(u64 module_accessor, bool rebound) {
if (is_training_mode() && rebound == false) {
// 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};
EffectModule::kill_kind(module_accessor, shieldEffectHash.hash, 0, 1);
}
}
}
return ret;
// call original GrabModule::set_rebound_impl
u64 grab_module = load_module(module_accessor, 0x158);
void (*set_rebound)(u64, bool) =
(void (*)(u64, bool))(load_module_impl(grab_module, 0x100));
return set_rebound(grab_module, rebound);
}
} // namespace app::lua_bind::GrabModule
Vector3f EffectModule_last_get_scale_w(u64 effect_module) {
Vector3f ret;
uint handle = *(uint *)(effect_module + 36);
if (handle && (signed int)handle >= 1) {
u64 effect = LOAD64(effect_manager_addr) + 768 * (handle >> 24);
bool is_exist_effect = effect && *(uint *)(effect + 4) == handle;
if (is_exist_effect) {
float *scale = (float *)(effect + 256);
ret.x = *(float *)(scale);
ret.y = *(float *)(scale + 1);
ret.z = *(float *)(scale + 2);
}
}
return ret;
}
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);
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 size_mult = 19.0f / 200.0f;
Hash40 shield_effect_hash = {.hash = 0xAFAE75F05LL};
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 effect_size((float)size->raw_float * size_mult);
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 effect_size((float)size->raw_float * size_mult);
L2CValue rate(8.0f);
L2CValue rate(8.0f);
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;
}
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 < 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);
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, x_curr, y_curr, z_curr, x_rot, y_rot, z_rot, effect_size, terminate });
// set to hitbox ID color
acmd.wrap(LAST_EFFECT_SET_COLOR, { red, green, blue });
ACMD acmd(l2c_agent);
acmd.wrap(EFFECT_FOLLOW_NO_SCALE,
{shieldEffect, *bone, x_curr, y_curr, z_curr, x_rot, y_rot,
z_rot, effect_size, terminate});
// speed up animation by rate to remove pulsing effect
acmd.wrap(LAST_EFFECT_SET_RATE, { rate });
}
// set to hitbox ID color
acmd.wrap(LAST_EFFECT_SET_COLOR, {red, green, blue});
// 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
L2CAgent l2c_agent;
l2c_agent.L2CAgent_constr(a1);
void ATTACK_replace(u64 a1) {
// 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, 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
// 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
// hacky way of forcing no shield damage on all hitboxes
if (is_training_mode() && TOGGLE_STATE == INFINITE_SHIELD) {
L2CValue hitbox_params[36];
for (size_t i = 0; i < 36; i++)
l2c_agent.get_lua_stack(i+1, &hitbox_params[i]);
l2c_agent.clear_lua_stack();
// hacky way of forcing no shield damage on all hitboxes
if (is_training_mode() && TOGGLE_STATE == INFINITE_SHIELD) {
L2CValue hitbox_params[36];
for (size_t i = 0; i < 36; i++)
l2c_agent.get_lua_stack(i + 1, &hitbox_params[i]);
for (size_t i = 0; i < 36; i++) {
if (i == 20) {
L2CValue no_shield_damage(-999);
l2c_agent.push_lua_stack(&no_shield_damage);
}
else
l2c_agent.push_lua_stack(&hitbox_params[i]);
}
}
l2c_agent.clear_lua_stack();
// original code: parse lua stack and call AttackModule::set_attack()
AttackModule_set_attack_lua_state(LOAD64(LOAD64(a1 - 8) + 416LL), a1);
for (size_t i = 0; i < 36; i++) {
if (i == 20) {
L2CValue no_shield_damage(-999);
l2c_agent.push_lua_stack(&no_shield_damage);
} else
l2c_agent.push_lua_stack(&hitbox_params[i]);
}
}
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.8f + 0.2f * 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);
}
// original code: parse lua stack and call AttackModule::set_attack()
AttackModule_set_attack_lua_state(LOAD64(LOAD64(a1 - 8) + 416LL), a1);
u64 v1, v2, i;
v1 = a1;
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.8f +
0.2f * 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);
}
// original code: clear_lua_stack section
v2 = LOAD64(v1 + 16);
for (i = **(u64 **)(v1 + 32) + 16LL; v2 < i; v2 = LOAD64(v1 + 16)) {
LOAD64(v1 + 16) = v2 + 16;
*(__int32_t *)(v2 + 8) = 0;
}
LOAD64(v1 + 16) = i;
}
u64 v1, v2, i;
v1 = a1;
void CATCH_replace(u64 a1) {
L2CAgent l2c_agent;
l2c_agent.L2CAgent_constr(a1);
// get all necessary grabbox params
L2CValue id, joint, size, x, y, z, x2, y2, z2;
l2c_agent.get_lua_stack(1, &id); // int
l2c_agent.get_lua_stack(2, &joint); // hash40
l2c_agent.get_lua_stack(3, &size); // float
l2c_agent.get_lua_stack(4, &x); // float
l2c_agent.get_lua_stack(5, &y); // float
l2c_agent.get_lua_stack(6, &z); // float
l2c_agent.get_lua_stack(7, &x2); // float or void
l2c_agent.get_lua_stack(8, &y2); // float or void
l2c_agent.get_lua_stack(9, &z2); // float or void
// SaltySD_function_replace_sym works by replacing the
// first four instructions of a function with instructions
// to jump to a replacement function like CATCH_replace.
// if we want to jump back, we just need to reimplement the
// first four instructions and jump back to the original address
// + the length of 4 instructions
// load address, along with lua_state as first arg
asm("MOV X9, %x0" : : "r"(Catch_jumpback));
asm("MOV X0, %x0" : : "r"(l2c_agent.lua_state_agent));
// the first four instructions: they will usually be
// setting up the stack for local variables
asm("SUB SP, SP, #0xC0");
asm("STR X25, [SP, #0x70]");
asm("STP X24, X23, [SP, #0x80]");
asm("STP X22, X21, [SP, #0x90]");
// jump to Catch_jumpback
asm("BLR X9");
if (HITBOX_VIS && is_training_mode()) {
Vector3f color = ID_COLORS[(id.raw + 3) % 8];
generate_hitbox_effects(&l2c_agent, &joint, &size, &x, &y, &z, &x2, &y2, &z2, &color);
}
}
// original code: clear_lua_stack section
v2 = LOAD64(v1 + 16);
for (i = **(u64 **)(v1 + 32) + 16LL; v2 < i; v2 = LOAD64(v1 + 16)) {
LOAD64(v1 + 16) = v2 + 16;
*(__int32_t *)(v2 + 8) = 0;
}
LOAD64(v1 + 16) = i;
}
void CATCH_replace(u64 a1) {
L2CAgent l2c_agent;
l2c_agent.L2CAgent_constr(a1);
// get all necessary grabbox params
L2CValue id, joint, size, x, y, z, x2, y2, z2;
l2c_agent.get_lua_stack(1, &id); // int
l2c_agent.get_lua_stack(2, &joint); // hash40
l2c_agent.get_lua_stack(3, &size); // float
l2c_agent.get_lua_stack(4, &x); // float
l2c_agent.get_lua_stack(5, &y); // float
l2c_agent.get_lua_stack(6, &z); // float
l2c_agent.get_lua_stack(7, &x2); // float or void
l2c_agent.get_lua_stack(8, &y2); // float or void
l2c_agent.get_lua_stack(9, &z2); // float or void
// SaltySD_function_replace_sym works by replacing the
// first four instructions of a function with instructions
// to jump to a replacement function like CATCH_replace.
// if we want to jump back, we just need to reimplement the
// first four instructions and jump back to the original address
// + the length of 4 instructions
// load address, along with lua_state as first arg
asm("MOV X9, %x0" : : "r"(Catch_jumpback));
asm("MOV X0, %x0" : : "r"(l2c_agent.lua_state_agent));
// the first four instructions: they will usually be
// setting up the stack for local variables
asm("SUB SP, SP, #0xC0");
asm("STR X25, [SP, #0x70]");
asm("STP X24, X23, [SP, #0x80]");
asm("STP X22, X21, [SP, #0x90]");
// jump to Catch_jumpback
asm("BLR X9");
if (HITBOX_VIS && is_training_mode()) {
Vector3f color = ID_COLORS[(id.raw + 3) % 8];
generate_hitbox_effects(&l2c_agent, &joint, &size, &x, &y, &z, &x2, &y2, &z2, &color);
}
}
} // namespace app::sv_animcmd
void hitbox_vis_main() {
effect_manager_addr = SaltySDCore_FindSymbol("_ZN3lib9SingletonINS_13EffectManagerEE9instance_E");
effect_manager_addr = SaltySDCore_FindSymbol(
"_ZN3lib9SingletonINS_13EffectManagerEE9instance_E");
AttackModule_set_attack_lua_state = (void (*)(u64, u64))SaltySDCore_FindSymbol("_ZN3app10sv_animcmd6ATTACKEP9lua_State") + 0xD0 - 0x70;
Catch_jumpback = SaltySDCore_FindSymbol("_ZN3app10sv_animcmd5CATCHEP9lua_State") + (4*4);
SaltySD_function_replace_sym(
"_ZN3app10sv_animcmd6ATTACKEP9lua_State",
(u64)&ATTACK_replace);
AttackModule_set_attack_lua_state = (void (*)(u64, u64))SaltySDCore_FindSymbol("_ZN3app10sv_animcmd6ATTACKEP9lua_State") +
0xD0 - 0x70;
Catch_jumpback = SaltySDCore_FindSymbol("_ZN3app10sv_animcmd5CATCHEP9lua_State") + (4 * 4);
SaltySD_function_replace_sym("_ZN3app10sv_animcmd6ATTACKEP9lua_State",
(u64)&ATTACK_replace);
SaltySD_function_replace_sym(
"_ZN3app10sv_animcmd5CATCHEP9lua_State",
(u64)&CATCH_replace);
SaltySD_function_replace_sym("_ZN3app10sv_animcmd5CATCHEP9lua_State",
(u64)&CATCH_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind28AttackModule__clear_all_implEPNS_26BattleObjectModuleAccessorE",
(u64)&AttackModule::clear_all_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind28AttackModule__clear_all_implEPNS_26BattleObjectModuleAccessorE",
(u64)&AttackModule::clear_all_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind28GrabModule__set_rebound_implEPNS_26BattleObjectModuleAccessorEb",
(u64)&GrabModule::set_rebound_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind28GrabModule__set_rebound_implEPNS_26BattleObjectModuleAccessorEb",
(u64)&GrabModule::set_rebound_replace);
}
#endif // HITBOX_VISUALIZER_H
#endif // HITBOX_VISUALIZER_H

View file

@ -1,36 +1,36 @@
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <switch/kernel/ipc.h>
#include <sys/iosupport.h>
#include <sys/reent.h>
#include <switch/kernel/ipc.h>
#include "useful/useful.h"
#include "saltysd/saltysd_core.h"
#include "saltysd/saltysd_ipc.h"
#include "saltysd/saltysd_dynamic.h"
#include "saltysd/saltysd_ipc.h"
#include "saltysd/saltysd_helper.hpp"
#include "imports/lib/l2c.hpp"
#include "imports/app/sv_animcmd.hpp"
#include "imports/lib/l2c.hpp"
#include "saltysd/saltysd_helper.hpp"
#include "hitbox_visualizer.hpp"
#include "script_replacement.hpp"
#include "training_mods.hpp"
extern "C" {
extern u32 __start__;
extern u32 __start__;
static char g_heap[0x8000];
static char g_heap[0x8000];
void __libnx_init(void* ctx, Handle main_thread, void* saved_lr);
void __attribute__((weak)) NORETURN __libnx_exit(int rc);
void __nx_exit(int, void*);
void __libc_fini_array(void);
void __libc_init_array(void);
void __libnx_init(void* ctx, Handle main_thread, void* saved_lr);
void __attribute__((weak)) NORETURN __libnx_exit(int rc);
void __nx_exit(int, void*);
void __libc_fini_array(void);
void __libc_init_array(void);
}
u32 __nx_applet_type = AppletType_None;
@ -40,57 +40,59 @@ void* orig_ctx;
void* orig_saved_lr;
void __libnx_init(void* ctx, Handle main_thread, void* saved_lr) {
extern char* fake_heap_start;
extern char* fake_heap_end;
extern char* fake_heap_start;
extern char* fake_heap_end;
fake_heap_start = &g_heap[0];
fake_heap_end = &g_heap[sizeof g_heap];
orig_ctx = ctx;
orig_main_thread = main_thread;
orig_saved_lr = saved_lr;
// Call constructors.
//void __libc_init_array(void);
__libc_init_array();
fake_heap_start = &g_heap[0];
fake_heap_end = &g_heap[sizeof g_heap];
orig_ctx = ctx;
orig_main_thread = main_thread;
orig_saved_lr = saved_lr;
// Call constructors.
// void __libc_init_array(void);
__libc_init_array();
}
void __attribute__((weak)) NORETURN __libnx_exit(int rc) {
// Call destructors.
//void __libc_fini_array(void);
__libc_fini_array();
// Call destructors.
// void __libc_fini_array(void);
__libc_fini_array();
SaltySD_printf("SaltySD Plugin: jumping to %p\n", orig_saved_lr);
SaltySD_printf("SaltySD Plugin: jumping to %p\n", orig_saved_lr);
__nx_exit(0, orig_saved_lr);
while (true);
__nx_exit(0, orig_saved_lr);
while (true)
;
}
int main(int argc, char *argv[]) {
SaltySD_printf("SaltySD Plugin: alive\n");
// Get anchor for imports
// do not remove if you plan on using IMPORT
ANCHOR_ABS = SaltySDCore_getCodeStart();
int main(int argc, char* argv[]) {
SaltySD_printf("SaltySD Plugin: alive\n");
/*
Example of string replacement:
replaces the title screen's version number with the string
below.
*/
const char *ver = "Ver. %d.%d.%d";
u64 version_string = SaltySDCore_findCode((u8 *)ver, strlen(ver));
if (version_string) {
SaltySD_Memcpy(version_string, (u64) "Salty v%d%d%d", 13);
}
// Get anchor for imports
// do not remove if you plan on using IMPORT
ANCHOR_ABS = SaltySDCore_getCodeStart();
// 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();
/*
Example of string replacement:
replaces the title screen's version number with the string
below.
*/
__libnx_exit(0);
const char* ver = "Ver. %d.%d.%d";
u64 version_string = SaltySDCore_findCode((u8*)ver, strlen(ver));
if (version_string) {
SaltySD_Memcpy(version_string, (u64) "Salty v%d%d%d", 13);
}
// 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);
}

View file

@ -8,15 +8,16 @@
#include "useful/const_value_table.h"
#include "useful/raygun_printer.hpp"
#include "imports/lib/l2c.hpp"
#include "acmd_wrapper.hpp"
#include "imports/lib/l2c.hpp"
#include "taunt_toggles.h"
using namespace lib;
using namespace app::lua_bind;
void sv_replace_status_func(u64 l2c_agentbase, int status_kind, u64 key, void* func);
void sv_replace_status_func(u64 l2c_agentbase, int status_kind, u64 key,
void* func);
u64 appeal_lw_replace(L2CAgent* l2c_agent, void* variadic);
u64 appeal_hi_replace(L2CAgent* l2c_agent, void* variadic);
@ -26,146 +27,128 @@ u64 pre_GuardDamage_replace(L2CAgent* l2c_fighter, L2CAgent* l2c_agent);
void replace_scripts(L2CAgent* l2c_agent, u8 category, int kind) {
// fighter
if (category == BATTLE_OBJECT_CATEGORY_FIGHTER) {
// taunt toggles
l2c_agent->sv_set_function_hash(&appeal_lw_replace, hash40("effect_appeallwl"));
l2c_agent->sv_set_function_hash(&appeal_lw_replace, hash40("effect_appeallwr"));
l2c_agent->sv_set_function_hash(&appeal_hi_replace, hash40("effect_appealhil"));
l2c_agent->sv_set_function_hash(&appeal_hi_replace, hash40("effect_appealhir"));
l2c_agent->sv_set_function_hash(&appeal_s_replace, hash40("effect_appealsl"));
l2c_agent->sv_set_function_hash(&appeal_s_replace, hash40("effect_appealsr"));
// taunt toggles
l2c_agent->sv_set_function_hash(&appeal_lw_replace,
hash40("effect_appeallwl"));
l2c_agent->sv_set_function_hash(&appeal_lw_replace,
hash40("effect_appeallwr"));
l2c_agent->sv_set_function_hash(&appeal_hi_replace,
hash40("effect_appealhil"));
l2c_agent->sv_set_function_hash(&appeal_hi_replace,
hash40("effect_appealhir"));
l2c_agent->sv_set_function_hash(&appeal_s_replace,
hash40("effect_appealsl"));
l2c_agent->sv_set_function_hash(&appeal_s_replace,
hash40("effect_appealsr"));
}
}
u64 appeal_lw_replace(L2CAgent* l2c_agent, void* variadic) {
ACMD acmd = ACMD(l2c_agent);
acmd.frame(1);
if (acmd.is_excute()) {
if (is_training_mode()) {
TOGGLE_STATE = (TOGGLE_STATE + 1) % NUM_TOGGLE_STATES;
const char* toggle_strings[NUM_TOGGLE_STATES] = {
"NONE",
"MASH\nAIRDODGE",
"MASH\nJUMP",
"MASH\nATTACK",
"MASH\nRANDOM",
"INFINITE\nSHIELD",
"HOLD\nSHIELD",
"LEDGE\nOPTION"
};
print_string(acmd.module_accessor, toggle_strings[TOGGLE_STATE]);
}
if (is_training_mode()) {
TOGGLE_STATE = (TOGGLE_STATE + 1) % NUM_TOGGLE_STATES;
const char* toggle_strings[NUM_TOGGLE_STATES] = {
"NONE", "MASH\nAIRDODGE", "MASH\nJUMP",
"MASH\nATTACK", "MASH\nRANDOM", "INFINITE\nSHIELD",
"HOLD\nSHIELD", "LEDGE\nOPTION"};
print_string(acmd.module_accessor, toggle_strings[TOGGLE_STATE]);
}
}
return 0;
}
u64 appeal_hi_replace(L2CAgent* l2c_agent, void* variadic) {
ACMD acmd = ACMD(l2c_agent);
ACMD acmd = ACMD(l2c_agent);
acmd.frame(1);
if (acmd.is_excute()) {
if (is_training_mode()) {
HITBOX_VIS = !HITBOX_VIS;
if (HITBOX_VIS)
print_string(acmd.module_accessor, "HITBOX\nVIS");
else
print_string(acmd.module_accessor, "NO\nHITBOX");
}
}
acmd.frame(1);
if (acmd.is_excute()) {
if (is_training_mode()) {
HITBOX_VIS = !HITBOX_VIS;
if (HITBOX_VIS)
print_string(acmd.module_accessor, "HITBOX\nVIS");
else
print_string(acmd.module_accessor, "NO\nHITBOX");
}
}
return 0;
return 0;
}
u64 appeal_s_replace(L2CAgent* l2c_agent, void* variadic) {
ACMD acmd = ACMD(l2c_agent);
ACMD acmd = ACMD(l2c_agent);
acmd.frame(1);
if (acmd.is_excute()) {
if (is_training_mode()) {
if (TOGGLE_STATE == LEDGE_OPTION) {
LEDGE_STATE = (LEDGE_STATE + 1) % NUM_LEDGE_STATES;
const char* LEDGE_strings[NUM_LEDGE_STATES] = {
"RANDOM",
"NORMAL",
"ROLL",
"JUMP",
"ATTACK"
};
acmd.frame(1);
if (acmd.is_excute()) {
if (is_training_mode()) {
if (TOGGLE_STATE == LEDGE_OPTION) {
LEDGE_STATE = (LEDGE_STATE + 1) % NUM_LEDGE_STATES;
const char* LEDGE_strings[NUM_LEDGE_STATES] = {
"RANDOM", "NORMAL", "ROLL", "JUMP", "ATTACK"};
print_string(acmd.module_accessor, LEDGE_strings[LEDGE_STATE]);
} else if (TOGGLE_STATE == MASH_ATTACK) {
ATTACK_STATE = (ATTACK_STATE + 1) % NUM_ATTACK_STATES;
const char* ATTACK_strings[NUM_ATTACK_STATES] = {
"NAIR",
"FAIR",
"BAIR",
"UPAIR",
"DAIR",
"NEUTRAL B",
"SIDE B",
"UP B",
"DOWN B"
};
print_string(acmd.module_accessor, LEDGE_strings[LEDGE_STATE]);
} else if (TOGGLE_STATE == MASH_ATTACK) {
ATTACK_STATE = (ATTACK_STATE + 1) % NUM_ATTACK_STATES;
const char* ATTACK_strings[NUM_ATTACK_STATES] = {
"NAIR", "FAIR", "BAIR", "UPAIR", "DAIR",
"NEUTRAL B", "SIDE B", "UP B", "DOWN B"};
print_string(acmd.module_accessor, ATTACK_strings[ATTACK_STATE]);
} else {
DI_STATE = (DI_STATE + 1) % NUM_DI_STATES;
const char* DI_strings[NUM_DI_STATES] = {
"NONE",
"AWAY",
"DOWN AWAY",
"DOWN",
"DOWN IN",
"IN",
"UP IN",
"UP",
"UP AWAY",
"RANDOM\nIN AWAY"
};
print_string(acmd.module_accessor,
ATTACK_strings[ATTACK_STATE]);
} else {
DI_STATE = (DI_STATE + 1) % NUM_DI_STATES;
const char* DI_strings[NUM_DI_STATES] = {
"NONE", "AWAY", "DOWN AWAY", "DOWN", "DOWN IN",
"IN", "UP IN", "UP", "UP AWAY", "RANDOM\nIN AWAY"};
print_string(acmd.module_accessor, DI_strings[DI_STATE]);
}
}
}
print_string(acmd.module_accessor, DI_strings[DI_STATE]);
}
}
}
return 0;
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);
if (0x2E8BA2E8BA2E8BA3LL * ((unk50 - unk48) >> 4) > (u64)status_kind)
return *(void **)(unk48 + 0xB0LL * status_kind + (key << 32 >> 29));
u64 unk48 = LOAD64(l2c_agentbase + 0x48);
u64 unk50 = LOAD64(l2c_agentbase + 0x50);
if (0x2E8BA2E8BA2E8BA3LL * ((unk50 - unk48) >> 4) > (u64)status_kind)
return *(void**)(unk48 + 0xB0LL * status_kind + (key << 32 >> 29));
return 0;
return 0;
}
void sv_replace_status_func(u64 l2c_agentbase, int status_kind, u64 key, void* func) {
u64 unk48 = LOAD64(l2c_agentbase + 0x48);
u64 unk50 = LOAD64(l2c_agentbase + 0x50);
if (0x2E8BA2E8BA2E8BA3LL * ((unk50 - unk48) >> 4) > (u64)status_kind) {
*(void **)(unk48 + 0xB0LL * status_kind + (key << 32 >> 29)) = func;
}
void sv_replace_status_func(u64 l2c_agentbase, int status_kind, u64 key,
void* func) {
u64 unk48 = LOAD64(l2c_agentbase + 0x48);
u64 unk50 = LOAD64(l2c_agentbase + 0x50);
if (0x2E8BA2E8BA2E8BA3LL * ((unk50 - unk48) >> 4) > (u64)status_kind) {
*(void**)(unk48 + 0xB0LL * status_kind + (key << 32 >> 29)) = func;
}
}
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))) {
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);
}
u64 lua_state = LOAD64(l2c_agent + 8);
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);
}
// Original clear_lua_stack:
u64 v1 = LOAD64(l2c_agent + 8);
u64 v2 = LOAD64(v1 + 16);
u64 i = LOAD64(LOAD64(v1 + 32)) + 16LL;
for (; v2 < i; v2 = LOAD64(v1 + 16)) {
LOAD64(v1 + 16) = v2 + 16;
*(u32 *)(v2 + 8) = 0;
}
LOAD64(v1 + 16) = i;
return l2c_agent;
// Original clear_lua_stack:
u64 v1 = LOAD64(l2c_agent + 8);
u64 v2 = LOAD64(v1 + 16);
u64 i = LOAD64(LOAD64(v1 + 32)) + 16LL;
for (; v2 < i; v2 = LOAD64(v1 + 16)) {
LOAD64(v1 + 16) = v2 + 16;
*(u32*)(v2 + 8) = 0;
}
LOAD64(v1 + 16) = i;
return l2c_agent;
}

View file

@ -2,18 +2,18 @@
#define TRAINING_MODS_H
#ifndef M_PI
#define M_PI 3.14159265358979323846
#define M_PI 3.14159265358979323846
#endif
#include "useful/useful.h"
#include "useful/crc32.h"
#include "useful/const_value_table.h"
#include "useful/crc32.h"
#include "useful/useful.h"
#include "useful/raygun_printer.hpp"
#include "acmd_wrapper.hpp"
#include "imports/lib/l2c.hpp"
#include "saltysd/saltysd_dynamic.h"
#include "saltysd/saltysd_helper.hpp"
#include "acmd_wrapper.hpp"
#include "taunt_toggles.h"
using namespace lib;
@ -23,357 +23,385 @@ using namespace app::sv_animcmd;
u64 fighter_manager_addr;
bool is_operation_cpu(u64 module_accessor) {
int entry_id = WorkModule::get_int(module_accessor, FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID);
u64 fighter_information = FighterManager::get_fighter_information(LOAD64(fighter_manager_addr), entry_id);
int entry_id = WorkModule::get_int(module_accessor,
FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID);
u64 fighter_information = FighterManager::get_fighter_information(
LOAD64(fighter_manager_addr), entry_id);
return FighterInformation::is_operation_cpu(fighter_information);
return FighterInformation::is_operation_cpu(fighter_information);
}
bool is_in_hitstun(u64 module_accessor) {
int status_kind = StatusModule::status_kind(module_accessor);
return status_kind >= FIGHTER_STATUS_KIND_DAMAGE && status_kind <= FIGHTER_STATUS_KIND_DAMAGE_FALL;
int status_kind = StatusModule::status_kind(module_accessor);
return status_kind >= FIGHTER_STATUS_KIND_DAMAGE &&
status_kind <= FIGHTER_STATUS_KIND_DAMAGE_FALL;
}
namespace app::lua_bind {
namespace WorkModule {
// Force DI
float get_float_replace(u64 module_accessor, int var) {
// call original WorkModule::get_float_impl
u64 work_module = load_module(module_accessor, 0x50);
float (*get_float)(u64, int) =
(float (*)(u64, int))(load_module_impl(work_module, 0x58));
namespace WorkModule {
// Force DI
float get_float_replace(u64 module_accessor, int var) {
// call original WorkModule::get_float_impl
u64 work_module = load_module(module_accessor, 0x50);
float (*get_float)(u64, int) =
(float (*)(u64, int))(load_module_impl(work_module, 0x58));
float ret_val = get_float(work_module, var);
float ret_val = get_float(work_module, var);
if (var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_X || var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_Y) {
if (is_training_mode() && is_operation_cpu(module_accessor) && is_in_hitstun(module_accessor)) {
if (DI_STATE != NONE) {
float angle = (DI_STATE - 1) * M_PI / 4.0;
if (var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_X ||
var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_Y) {
if (is_training_mode() && is_operation_cpu(module_accessor) &&
is_in_hitstun(module_accessor)) {
if (DI_STATE != NONE) {
float angle = (DI_STATE - 1) * M_PI / 4.0;
// Either 0 (right) or PI (left)
if (DI_STATE == DI_RANDOM_IN_AWAY) {
angle = app::sv_math::rand(hash40("fighter"), 2) * M_PI;
}
// If facing left, reverse angle
if (PostureModule::lr(module_accessor) != -1.0) angle -= M_PI;
// Either 0 (right) or PI (left)
if (DI_STATE == DI_RANDOM_IN_AWAY) {
angle = app::sv_math::rand(hash40("fighter"), 2) * M_PI;
}
// If facing left, reverse angle
if (PostureModule::lr(module_accessor) != -1.0) angle -= M_PI;
if (var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_X)
return cos(angle);
if (var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_X)
return cos(angle);
if (var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_Y)
return sin(angle);
}
}
}
return ret_val;
}
if (var == FIGHTER_STATUS_DAMAGE_WORK_FLOAT_VECOR_CORRECT_STICK_Y)
return sin(angle);
}
}
}
float get_param_float_replace(u64 module_accessor, u64 param_type, u64 param_hash) {
if (is_training_mode()) {
if (TOGGLE_STATE == INFINITE_SHIELD) {
if (param_type == hash40("common")) {
if (param_hash == hash40("shield_dec1")) return 0.0;
if (param_hash == hash40("shield_recovery1")) return 999.0;
// doesn't work, somehow. This parameter isn't checked?
if (param_hash == hash40("shield_damage_mul")) return 0.0;
}
}
}
return ret_val;
}
// call original
u64 work_module = load_module(module_accessor, 0x50);
float (*get_param_float)(u64, u64, u64) =
(float (*)(u64, u64, u64))(load_module_impl(work_module, 0x240));
float get_param_float_replace(u64 module_accessor, u64 param_type,
u64 param_hash) {
if (is_training_mode()) {
if (TOGGLE_STATE == INFINITE_SHIELD) {
if (param_type == hash40("common")) {
if (param_hash == hash40("shield_dec1")) return 0.0;
if (param_hash == hash40("shield_recovery1")) return 999.0;
// doesn't work, somehow. This parameter isn't checked?
if (param_hash == hash40("shield_damage_mul")) return 0.0;
}
}
}
return get_param_float(work_module, param_type, param_hash);
}
// call original
u64 work_module = load_module(module_accessor, 0x50);
float (*get_param_float)(u64, u64, u64) =
(float (*)(u64, u64, u64))(load_module_impl(work_module, 0x240));
// Force ledge option
u64 enable_transition_term_replace(u64 module_accessor, int transition_id) {
if (TOGGLE_STATE == LEDGE_OPTION && is_training_mode() && is_operation_cpu(module_accessor)) {
if (StatusModule::status_kind(module_accessor) == FIGHTER_STATUS_KIND_CLIFF_WAIT) {
if (transition_id == FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_CLIFF_CLIMB) {
int status = 0;
int ledge_case = LEDGE_STATE;
if (LEDGE_STATE == RANDOM_LEDGE)
ledge_case = app::sv_math::rand(hash40("fighter"), 4) + 1;
return get_param_float(work_module, param_type, param_hash);
}
switch (ledge_case) {
case NEUTRAL_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_CLIMB; break;
case ROLL_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_ESCAPE; break;
case JUMP_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_JUMP1; break;
case ATTACK_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_ATTACK; break;
}
StatusModule::change_status_request_from_script(module_accessor, status, 1);
}
}
}
// Force ledge option
u64 enable_transition_term_replace(u64 module_accessor, int transition_id) {
if (TOGGLE_STATE == LEDGE_OPTION && is_training_mode() &&
is_operation_cpu(module_accessor)) {
if (StatusModule::status_kind(module_accessor) == FIGHTER_STATUS_KIND_CLIFF_WAIT) {
if (transition_id == FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_CLIFF_CLIMB) {
int status = 0;
int ledge_case = LEDGE_STATE;
// call original WorkModule::enable_transition_term_group_impl
u64 work_module = load_module(module_accessor, 0x50);
u64 (*enable_transition_term)(u64, int) =
(u64(*)(u64, int))(load_module_impl(work_module, 0x188));
if (LEDGE_STATE == RANDOM_LEDGE)
ledge_case = app::sv_math::rand(hash40("fighter"), 4) + 1;
return enable_transition_term(work_module, transition_id);
}
} // namespace WorkModule
switch (ledge_case) {
case NEUTRAL_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_CLIMB; break;
case ROLL_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_ESCAPE; break;
case JUMP_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_JUMP1; break;
case ATTACK_LEDGE:
status = FIGHTER_STATUS_KIND_CLIFF_ATTACK; break;
}
namespace ControlModule {
int get_command_flag_cat_replace(u64 module_accessor, int category) {
// call original
u64 control_module = load_module(module_accessor, 0x48);
int (*get_command_flag_cat)(u64, int) =
(int (*)(u64, int))load_module_impl(control_module, 0x350);
int flag = get_command_flag_cat(control_module, category);
StatusModule::change_status_request_from_script(module_accessor, status, 1);
}
}
}
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (is_in_hitstun(module_accessor)) {
if (TOGGLE_STATE == MASH_AIRDODGE)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1)
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE;
// call original WorkModule::enable_transition_term_group_impl
u64 work_module = load_module(module_accessor, 0x50);
u64 (*enable_transition_term)(u64, int) =
(u64(*)(u64, int))(load_module_impl(work_module, 0x188));
if (TOGGLE_STATE == MASH_JUMP)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1)
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_JUMP_BUTTON;
return enable_transition_term(work_module, transition_id);
}
} // namespace WorkModule
if (TOGGLE_STATE == MASH_ATTACK)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1) {
switch (ATTACK_STATE) {
case MASH_NAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_N"); break;
case MASH_FAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_F"); break;
case MASH_BAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_B"); break;
case MASH_UPAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_HI"); break;
case MASH_DAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_LW"); break;
case MASH_NEUTRAL_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_N; break;
case MASH_SIDE_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_S; break;
case MASH_UP_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_HI; break;
case MASH_DOWN_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_LW; break;
}
}
namespace ControlModule {
int get_command_flag_cat_replace(u64 module_accessor, int category) {
// call original
u64 control_module = load_module(module_accessor, 0x48);
int (*get_command_flag_cat)(u64, int) =
(int (*)(u64, int))load_module_impl(control_module, 0x350);
int flag = get_command_flag_cat(control_module, category);
if (TOGGLE_STATE == MASH_RANDOM)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1) {
int random_commands[] = {
FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE,
FIGHTER_PAD_CMD_CAT1_FLAG_JUMP_BUTTON,
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_N"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_F"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_B"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_HI"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_LW"),
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_N,
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_S,
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_HI,
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_LW,
};
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (is_in_hitstun(module_accessor)) {
if (TOGGLE_STATE == MASH_AIRDODGE)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1)
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE;
int random_cmd_index = app::sv_math::rand(hash40("fighter"), 11);
if (TOGGLE_STATE == MASH_JUMP)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1)
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_JUMP_BUTTON;
flag |= random_commands[random_cmd_index];
}
}
}
if (TOGGLE_STATE == MASH_ATTACK)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1) {
switch (ATTACK_STATE) {
case MASH_NAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_N"); break;
case MASH_FAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_F"); break;
case MASH_BAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_B"); break;
case MASH_UPAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_HI"); break;
case MASH_DAIR:
flag |= lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_LW"); break;
case MASH_NEUTRAL_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_N; break;
case MASH_SIDE_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_S; break;
case MASH_UP_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_HI; break;
case MASH_DOWN_B:
flag |= FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_LW; break;
}
}
return flag;
}
if (TOGGLE_STATE == MASH_RANDOM)
if (category == FIGHTER_PAD_COMMAND_CATEGORY1) {
int random_commands[] = {
FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE,
FIGHTER_PAD_CMD_CAT1_FLAG_JUMP_BUTTON,
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_N"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_F"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_B"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_HI"),
lua_const("FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_AIR_LW"),
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_N,
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_S,
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_HI,
FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_LW,
};
bool check_button_on_replace(u64 module_accessor, int button) {
if (button == CONTROL_PAD_BUTTON_GUARD_HOLD ||
button == CONTROL_PAD_BUTTON_GUARD) {
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (TOGGLE_STATE == HOLD_SHIELD || TOGGLE_STATE == INFINITE_SHIELD)
return true;
if (TOGGLE_STATE == MASH_AIRDODGE && is_in_hitstun(module_accessor))
return true;
}
}
int random_cmd_index =
app::sv_math::rand(hash40("fighter"), 11);
// call original
u64 control_module = load_module(module_accessor, 0x48);
bool (*check_button_on)(u64, int) =
(bool (*)(u64, int))load_module_impl(control_module, 0x260);
return check_button_on(control_module, button);
}
flag |= random_commands[random_cmd_index];
}
}
}
bool check_button_off_replace(u64 module_accessor, int button) {
if (button == CONTROL_PAD_BUTTON_GUARD_HOLD ||
button == CONTROL_PAD_BUTTON_GUARD) {
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (TOGGLE_STATE == HOLD_SHIELD || TOGGLE_STATE == INFINITE_SHIELD)
return false;
}
}
return flag;
}
// call original
u64 control_module = load_module(module_accessor, 0x48);
bool (*check_button_off)(u64, int) =
(bool (*)(u64, int))load_module_impl(control_module, 0x268);
return check_button_off(control_module, button);
}
bool check_button_on_replace(u64 module_accessor, int button) {
if (button == CONTROL_PAD_BUTTON_GUARD_HOLD ||
button == CONTROL_PAD_BUTTON_GUARD) {
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (TOGGLE_STATE == HOLD_SHIELD || TOGGLE_STATE == INFINITE_SHIELD)
return true;
if (TOGGLE_STATE == MASH_AIRDODGE && is_in_hitstun(module_accessor))
return true;
}
}
#define SAVE_STATE 1
#define DEFAULT 2
#define CAMERA_MOVE 3
#define POS_MOVE 4
// call original
u64 control_module = load_module(module_accessor, 0x48);
bool (*check_button_on)(u64, int) =
(bool (*)(u64, int))load_module_impl(control_module, 0x260);
return check_button_on(control_module, button);
}
int save_state_player_state = DEFAULT;
int save_state_cpu_state = DEFAULT;
bool save_state_move_alert = false;
bool check_button_off_replace(u64 module_accessor, int button) {
if (button == CONTROL_PAD_BUTTON_GUARD_HOLD ||
button == CONTROL_PAD_BUTTON_GUARD) {
if (is_training_mode() && is_operation_cpu(module_accessor)) {
if (TOGGLE_STATE == HOLD_SHIELD || TOGGLE_STATE == INFINITE_SHIELD)
return false;
}
}
float save_state_x_player = 0;
float save_state_y_player = 0;
float save_state_percent_player = 0;
float save_state_lr_player = 1.0;
int save_state_situation_kind_player = 0;
// call original
u64 control_module = load_module(module_accessor, 0x48);
bool (*check_button_off)(u64, int) =
(bool (*)(u64, int))load_module_impl(control_module, 0x268);
return check_button_off(control_module, button);
}
float save_state_x_cpu = 0;
float save_state_y_cpu = 0;
float save_state_percent_cpu = 0;
float save_state_lr_cpu = 1.0;
int save_state_situation_kind_cpu = 0;
#define SAVE_STATE 1
#define DEFAULT 2
#define CAMERA_MOVE 3
#define POS_MOVE 4
int get_pad_flag_replace(u64 module_accessor) {
if (is_training_mode()) {
float* save_state_x;
float* save_state_y;
float* save_state_percent;
float* save_state_lr;
int* save_state_situation_kind;
int* save_state;
if (is_operation_cpu(module_accessor)) {
save_state_x = &save_state_x_cpu;
save_state_y = &save_state_y_cpu;
save_state_percent = &save_state_percent_cpu;
save_state_lr = &save_state_lr_cpu;
save_state_situation_kind = &save_state_situation_kind_cpu;
save_state = &save_state_cpu_state;
} else {
save_state_x = &save_state_x_player;
save_state_y = &save_state_y_player;
save_state_percent = &save_state_percent_player;
save_state_lr = &save_state_lr_player;
save_state_situation_kind = &save_state_situation_kind_player;
save_state = &save_state_player_state;
}
int save_state_player_state = DEFAULT;
int save_state_cpu_state = DEFAULT;
bool save_state_move_alert = false;
// Grab + Dpad up: reset state
if (ControlModule::check_button_on(module_accessor, CONTROL_PAD_BUTTON_CATCH) &&
ControlModule::check_button_trigger(module_accessor, CONTROL_PAD_BUTTON_APPEAL_HI)) {
if (*save_state == DEFAULT) {
save_state_player_state = CAMERA_MOVE;
save_state_cpu_state = CAMERA_MOVE;
}
}
float save_state_x_player = 0;
float save_state_y_player = 0;
float save_state_percent_player = 0;
float save_state_lr_player = 1.0;
int save_state_situation_kind_player = 0;
// move to camera bounds
if (*save_state == CAMERA_MOVE) {
*save_state = POS_MOVE;
float save_state_x_cpu = 0;
float save_state_y_cpu = 0;
float save_state_percent_cpu = 0;
float save_state_lr_cpu = 1.0;
int save_state_situation_kind_cpu = 0;
float left_right = (*save_state_x > 0) - (*save_state_x < 0);
float y_pos = 0;
if (*save_state_situation_kind == SITUATION_KIND_GROUND) y_pos = -50;
int get_pad_flag_replace(u64 module_accessor) {
if (is_training_mode()) {
float* save_state_x;
float* save_state_y;
float* save_state_percent;
float* save_state_lr;
int* save_state_situation_kind;
int* save_state;
if (is_operation_cpu(module_accessor)) {
save_state_x = &save_state_x_cpu;
save_state_y = &save_state_y_cpu;
save_state_percent = &save_state_percent_cpu;
save_state_lr = &save_state_lr_cpu;
save_state_situation_kind = &save_state_situation_kind_cpu;
save_state = &save_state_cpu_state;
} else {
save_state_x = &save_state_x_player;
save_state_y = &save_state_y_player;
save_state_percent = &save_state_percent_player;
save_state_lr = &save_state_lr_player;
save_state_situation_kind = &save_state_situation_kind_player;
save_state = &save_state_player_state;
}
Vector3f pos = {.x = left_right * 50, .y = y_pos, .z = 0};
PostureModule::set_pos(module_accessor, &pos);
StatusModule::set_situation_kind(module_accessor, SITUATION_KIND_AIR, 0);
}
// Grab + Dpad up: reset state
if (ControlModule::check_button_on(module_accessor,
CONTROL_PAD_BUTTON_CATCH) &&
ControlModule::check_button_trigger(module_accessor,
CONTROL_PAD_BUTTON_APPEAL_HI)) {
if (*save_state == DEFAULT) {
save_state_player_state = CAMERA_MOVE;
save_state_cpu_state = CAMERA_MOVE;
}
}
// move to correct pos
if (*save_state == POS_MOVE) {
*save_state = DEFAULT;
// move to camera bounds
if (*save_state == CAMERA_MOVE) {
*save_state = POS_MOVE;
Vector3f pos = {.x = *save_state_x, .y = *save_state_y, .z = 0};
PostureModule::set_pos(module_accessor, &pos);
PostureModule::set_lr(module_accessor, *save_state_lr);
DamageModule::add_damage(module_accessor, -1.0 * DamageModule::damage(module_accessor, 0), 0);
DamageModule::add_damage(module_accessor, *save_state_percent, 0);
StatusModule::set_situation_kind(module_accessor, *save_state_situation_kind, 0);
float left_right = (*save_state_x > 0) - (*save_state_x < 0);
float y_pos = 0;
if (*save_state_situation_kind == SITUATION_KIND_GROUND)
y_pos = -50;
// Doesn't work, and I don't know why yet.
/*if (*save_state_situation_kind == SITUATION_KIND_GROUND)
StatusModule::change_status_request(module_accessor, FIGHTER_STATUS_KIND_WAIT, 0);
else if (*save_state_situation_kind == SITUATION_KIND_AIR)
StatusModule::change_status_request(module_accessor, FIGHTER_STATUS_KIND_FALL, 0);
else if (*save_state_situation_kind == SITUATION_KIND_CLIFF)
StatusModule::change_status_request(module_accessor, FIGHTER_STATUS_KIND_CLIFF_CATCH, 0);
*/
}
Vector3f pos = {.x = left_right * 50, .y = y_pos, .z = 0};
PostureModule::set_pos(module_accessor, &pos);
StatusModule::set_situation_kind(module_accessor,
SITUATION_KIND_AIR, 0);
}
// Grab + Dpad down: Save state
if (ControlModule::check_button_on(module_accessor, CONTROL_PAD_BUTTON_CATCH) &&
ControlModule::check_button_trigger(module_accessor, CONTROL_PAD_BUTTON_APPEAL_LW)) {
save_state_player_state = SAVE_STATE;
save_state_cpu_state = SAVE_STATE;
}
// move to correct pos
if (*save_state == POS_MOVE) {
*save_state = DEFAULT;
if (*save_state == SAVE_STATE) {
*save_state = DEFAULT;
Vector3f pos = {.x = *save_state_x, .y = *save_state_y, .z = 0};
PostureModule::set_pos(module_accessor, &pos);
PostureModule::set_lr(module_accessor, *save_state_lr);
DamageModule::add_damage(
module_accessor,
-1.0 * DamageModule::damage(module_accessor, 0), 0);
DamageModule::add_damage(module_accessor, *save_state_percent, 0);
StatusModule::set_situation_kind(module_accessor,
*save_state_situation_kind, 0);
*save_state_x = PostureModule::pos_x(module_accessor);
*save_state_y = PostureModule::pos_y(module_accessor);
*save_state_lr = PostureModule::lr(module_accessor);
*save_state_percent = DamageModule::damage(module_accessor, 0);
*save_state_situation_kind = StatusModule::situation_kind(module_accessor);
}
}
// Doesn't work, and I don't know why yet.
/*if (*save_state_situation_kind == SITUATION_KIND_GROUND)
StatusModule::change_status_request(module_accessor,
FIGHTER_STATUS_KIND_WAIT, 0); else if (*save_state_situation_kind ==
SITUATION_KIND_AIR)
StatusModule::change_status_request(module_accessor,
FIGHTER_STATUS_KIND_FALL, 0); else if (*save_state_situation_kind ==
SITUATION_KIND_CLIFF)
StatusModule::change_status_request(module_accessor,
FIGHTER_STATUS_KIND_CLIFF_CATCH, 0);
*/
}
// call original
u64 control_module = load_module(module_accessor, 0x48);
int (*get_pad_flag)(u64) =
(int (*)(u64))load_module_impl(control_module, 0x348);
return get_pad_flag(control_module);
}
} // namespace ControlModule
// Grab + Dpad down: Save state
if (ControlModule::check_button_on(module_accessor,
CONTROL_PAD_BUTTON_CATCH) &&
ControlModule::check_button_trigger(module_accessor,
CONTROL_PAD_BUTTON_APPEAL_LW)) {
save_state_player_state = SAVE_STATE;
save_state_cpu_state = SAVE_STATE;
}
if (*save_state == SAVE_STATE) {
*save_state = DEFAULT;
*save_state_x = PostureModule::pos_x(module_accessor);
*save_state_y = PostureModule::pos_y(module_accessor);
*save_state_lr = PostureModule::lr(module_accessor);
*save_state_percent = DamageModule::damage(module_accessor, 0);
*save_state_situation_kind =
StatusModule::situation_kind(module_accessor);
}
}
// call original
u64 control_module = load_module(module_accessor, 0x48);
int (*get_pad_flag)(u64) =
(int (*)(u64))load_module_impl(control_module, 0x348);
return get_pad_flag(control_module);
}
} // namespace ControlModule
} // namespace app::lua_bind
void training_mods_main() {
fighter_manager_addr = SaltySDCore_FindSymbol("_ZN3lib9SingletonIN3app14FighterManagerEE9instance_E");
// Mash airdodge/jump
SaltySD_function_replace_sym(
"_ZN3app8lua_bind40ControlModule__get_command_flag_cat_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&ControlModule::get_command_flag_cat_replace);
// Set DI
SaltySD_function_replace_sym(
"_ZN3app8lua_bind26WorkModule__get_float_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&WorkModule::get_float_replace);
fighter_manager_addr = SaltySDCore_FindSymbol(
"_ZN3lib9SingletonIN3app14FighterManagerEE9instance_E");
// Mash airdodge/jump
SaltySD_function_replace_sym(
"_ZN3app8lua_bind40ControlModule__get_command_flag_cat_implEPNS_"
"26BattleObjectModuleAccessorEi",
(u64)&ControlModule::get_command_flag_cat_replace);
// Hold/Infinite shield
SaltySD_function_replace_sym(
"_ZN3app8lua_bind35ControlModule__check_button_on_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&ControlModule::check_button_on_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind36ControlModule__check_button_off_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&ControlModule::check_button_off_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind32WorkModule__get_param_float_implEPNS_26BattleObjectModuleAccessorEmm",
(u64)&WorkModule::get_param_float_replace);
// Set DI
SaltySD_function_replace_sym(
"_ZN3app8lua_bind26WorkModule__get_float_implEPNS_"
"26BattleObjectModuleAccessorEi",
(u64)&WorkModule::get_float_replace);
// Ledge options
SaltySD_function_replace_sym(
"_ZN3app8lua_bind39WorkModule__enable_transition_term_implEPNS_26BattleObjectModuleAccessorEi",
(u64)&WorkModule::enable_transition_term_replace);
// Hold/Infinite shield
SaltySD_function_replace_sym(
"_ZN3app8lua_bind35ControlModule__check_button_on_implEPNS_"
"26BattleObjectModuleAccessorEi",
(u64)&ControlModule::check_button_on_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind36ControlModule__check_button_off_implEPNS_"
"26BattleObjectModuleAccessorEi",
(u64)&ControlModule::check_button_off_replace);
SaltySD_function_replace_sym(
"_ZN3app8lua_bind32WorkModule__get_param_float_implEPNS_"
"26BattleObjectModuleAccessorEmm",
(u64)&WorkModule::get_param_float_replace);
// Save states: in beta
/*SaltySD_function_replace_sym(
"_ZN3app8lua_bind32ControlModule__get_pad_flag_implEPNS_26BattleObjectModuleAccessorE",
(u64)&ControlModule::get_pad_flag_replace);*/
// Ledge options
SaltySD_function_replace_sym(
"_ZN3app8lua_bind39WorkModule__enable_transition_term_implEPNS_"
"26BattleObjectModuleAccessorEi",
(u64)&WorkModule::enable_transition_term_replace);
// Save states: in beta
/*SaltySD_function_replace_sym(
"_ZN3app8lua_bind32ControlModule__get_pad_flag_implEPNS_26BattleObjectModuleAccessorE",
(u64)&ControlModule::get_pad_flag_replace);*/
}
#endif // TRAINING_MODS_H
#endif // TRAINING_MODS_H

View file

@ -1,8 +1,8 @@
#ifndef RAYGUN_PRINTER_H
#define RAYGUN_PRINTER_H
#include <switch.h>
#include <ctype.h>
#include <switch.h>
#include "useful.h"
@ -18,106 +18,62 @@ using namespace app::lua_bind;
segment data list : {Z, Y, X, ZRot, Size}
segment labels :
_
|_| from top to top left, clockwise: a->f + g mid + \|/ from top mid to top left, clockwise: h->m + --two half g's: n, o
|_| from top to top left, clockwise: a->f + g mid + \|/ from top mid to
top left, clockwise: h->m + --two half g's: n, o
|_| /|\
*/
const float segment_dict[15][5] = {
{0, RAYGUN_HEIGHT*2, 0, 0, 0.25}, // a
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH, 90, 0.25}, // b
{0, 0, RAYGUN_LENGTH, 90, 0.25}, // c
{0, 0, 0, 0, 0.25}, // d
{0, 0, 0, 90, 0.25}, // e
{0, RAYGUN_HEIGHT, 0, 90, 0.25}, // f
{0, RAYGUN_HEIGHT, 0, 0, 0.25}, // g mid
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH/2, 90, 0.25}, // h
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH/2, 52, 0.2}, // i
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH/2, -52, 0.2}, // j
{0, 0, RAYGUN_LENGTH/2, 90, 0.25}, // k
{0, RAYGUN_HEIGHT/2, RAYGUN_LENGTH*3/16, 52, 0.2}, // l
{0, RAYGUN_HEIGHT*3/2, RAYGUN_LENGTH*3/16, -52, 0.2}, // m
{0, RAYGUN_HEIGHT, 0, 0, 0.15}, // n
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH/2, 0, 0.15}, // o
};
{0, RAYGUN_HEIGHT * 2, 0, 0, 0.25}, // a
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH, 90, 0.25}, // b
{0, 0, RAYGUN_LENGTH, 90, 0.25}, // c
{0, 0, 0, 0, 0.25}, // d
{0, 0, 0, 90, 0.25}, // e
{0, RAYGUN_HEIGHT, 0, 90, 0.25}, // f
{0, RAYGUN_HEIGHT, 0, 0, 0.25}, // g mid
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2, 90, 0.25}, // h
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2, 52, 0.2}, // i
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2, -52, 0.2}, // j
{0, 0, RAYGUN_LENGTH / 2, 90, 0.25}, // k
{0, RAYGUN_HEIGHT / 2, RAYGUN_LENGTH * 3 / 16, 52, 0.2}, // l
{0, RAYGUN_HEIGHT * 3 / 2, RAYGUN_LENGTH * 3 / 16, -52, 0.2}, // m
{0, RAYGUN_HEIGHT, 0, 0, 0.15}, // n
{0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2, 0, 0.15}, // o
};
/*
/*
Segments making up each character, each index corresponding to:
'A' through 'Z', '0' through '9', ' ', '-', '+', '#' (where '#' is all segments)
'A' through 'Z', '0' through '9', ' ', '-', '+', '#' (where '#' is all
segments)
*/
const char* alphabet[] = {
"abcefg",
"adefijn",
"adef",
"eflm",
"adefn",
"aefn",
"acdefo",
"bcefg",
"adhk",
"bcd",
"efnij",
"def",
"bcefim",
"bcefjm",
"abcdef",
"abefg",
"abcdefj",
"aefijn",
"acdfg",
"ahk",
"bcdef",
"efil",
"bcefjl",
"ijlm",
"ikm",
"adil",
"abcdef",
"ef",
"abdeg",
"abcdg",
"bcfg",
"acdfg",
"acdefg",
"abc",
"abcdefg",
"abcdfg",
"",
"g",
"ghk",
"abcdefhijklmno",
const char* alphabet[] = {
"abcefg", "adefijn", "adef", "eflm", "adefn",
"aefn", "acdefo", "bcefg", "adhk", "bcd",
"efnij", "def", "bcefim", "bcefjm", "abcdef",
"abefg", "abcdefj", "aefijn", "acdfg", "ahk",
"bcdef", "efil", "bcefjl", "ijlm", "ikm",
"adil", "abcdef", "ef", "abdeg", "abcdg",
"bcfg", "acdfg", "acdefg", "abc", "abcdefg",
"abcdfg", "", "g", "ghk", "abcdefhijklmno",
};
// Each index is a segment's corresponding flipped segment, for when facing left
const char segment_rev[15] = {
'a',
'f',
'e',
'd',
'c',
'b',
'g',
'h',
'm',
'l',
'k',
'j',
'i',
'o',
'n',
'a', 'f', 'e', 'd', 'c', 'b', 'g', 'h', 'm', 'l', 'k', 'j', 'i', 'o', 'n',
};
void show_segment(u64 battle_object_module_accessor, float z, float y, float x, float zrot, float size) {
Hash40 raygunShot = { .hash = 0x11e470b07fLL };
Hash40 top = { .hash = 0x031ed91fcaLL };
void show_segment(u64 battle_object_module_accessor, float z, float y, float x,
float zrot, float size) {
Hash40 raygunShot = {.hash = 0x11e470b07fLL};
Hash40 top = {.hash = 0x031ed91fcaLL};
Vector3f pos = { .x = x, .y = y, .z = z };
Vector3f rot = { .x = 0, .y = 90, .z = zrot };
Vector3f random = { .x = 0, .y = 0, .z = 0 };
Vector3f pos = {.x = x, .y = y, .z = z};
Vector3f rot = {.x = 0, .y = 90, .z = zrot};
Vector3f random = {.x = 0, .y = 0, .z = 0};
EffectModule::req_on_joint(battle_object_module_accessor, raygunShot.hash, top.hash,
&pos, &rot, size,
&random, &random,
0, 0, 0, 0);
EffectModule::req_on_joint(battle_object_module_accessor, raygunShot.hash,
top.hash, &pos, &rot, size, &random, &random, 0,
0, 0, 0);
}
int alphabet_index(char to_print) {
@ -137,10 +93,10 @@ int alphabet_index(char to_print) {
return -1;
}
void print_char( u64 module_accessor, char to_print, int line_num, float horiz_offset, float facing_left) {
void print_char(u64 module_accessor, char to_print, int line_num,
float horiz_offset, float facing_left) {
int alph_index = alphabet_index(to_print);
if (alph_index < 0 || alph_index >= 40)
return;
if (alph_index < 0 || alph_index >= 40) return;
const char* segment_str = alphabet[alph_index];
int num_segments = strlen(segment_str);
@ -152,7 +108,7 @@ void print_char( u64 module_accessor, char to_print, int line_num, float horiz_o
if (facing_left == -1) {
index = segment_rev[index] - 'a';
}
}
segment = segment_dict[index];
float z = segment[0];
@ -160,17 +116,16 @@ void print_char( u64 module_accessor, char to_print, int line_num, float horiz_o
float x = segment[2] + horiz_offset;
float zrot = segment[3];
if (facing_left == -1)
zrot *= -1;
if (facing_left == -1) zrot *= -1;
float size = segment[4];
show_segment(module_accessor, z, y, x, zrot, size);
}
}
void print_string( u64 module_accessor, const char* print_str) {
void print_string(u64 module_accessor, const char* print_str) {
// Delete any previous strings
Hash40 raygunShot = { .hash = 0x11e470b07fLL };
Hash40 raygunShot = {.hash = 0x11e470b07fLL};
EffectModule::kill_kind(module_accessor, raygunShot.hash, 0, 1);
int line_num = 0;
@ -181,7 +136,7 @@ void print_string( u64 module_accessor, const char* print_str) {
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++) {
@ -193,15 +148,16 @@ void print_string( u64 module_accessor, const char* print_str) {
continue;
}
print_char(module_accessor, toupper(curr_char), line_num, horiz_offset, facing_left);
print_char(module_accessor, toupper(curr_char), line_num, horiz_offset,
facing_left);
char_num++;
// short characters
if (curr_char == 'D' || curr_char == '1') {
horiz_offset += facing_left * (RAYGUN_LENGTH/2 + 3);
horiz_offset += facing_left * (RAYGUN_LENGTH / 2 + 3);
} else {
horiz_offset += facing_left * (RAYGUN_LENGTH + 3);
}
}
if (char_num > 8) {
horiz_offset = 0;
@ -211,4 +167,4 @@ void print_string( u64 module_accessor, const char* print_str) {
}
}
#endif // RAYGUN_PRINTER_H
#endif // RAYGUN_PRINTER_H

View file

@ -1,56 +1,63 @@
#ifndef USEFUL_VISUAL_H
#define USEFUL_VISUAL_H
#include "useful.h"
#include <math.h>
#include "useful.h"
/**
* Rounds a number to the nearest multiple of another number.
*/
float round_to(float val, float align) {
return roundf(val / align) * align;
}
float round_to(float val, float align) { return roundf(val / align) * align; }
/**
* Linearly interpolates between two numbers, without bounds checking.
*/
float lerp(float min, float max, float t) {
return min + (max - min) * t;
}
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);
return (val - min) / (max - min);
}
/**
* Linearly interpolates between two numbers, with bounds checking.
*/
float lerp_bounded(float min, float max, float t) {
return t <= 0 ? min : t >= 1 ? max : lerp(min, max, 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);
return val <= min ? 0 : val >= max ? 1 : unlerp(min, max, 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
* 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
* - 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) {
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)
};
Vector3f color_lerp(Vector3f min_color, Vector3f max_color, float t,
float gamma = 2.0f) {
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)};
}
#endif // USEFUL_VISUAL_H
#endif // USEFUL_VISUAL_H