1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-10-02 17:24:28 +00:00
* continued testing

* Scuffed Acceleratle Implementation

* Cleanup and Groundwork, Only Accel

* Wii Fit complete, Joker, Hero and Cloud WIP.

* Preparing for multiple spells

* Implemented Multi Buffing

* Cloud Hook, Hero Improvements

* Nearly complete, still messing around with KO Punch

* Works for both fighters now

* Add buff icon

* Wii Fit sound cut, Sepiroth darken fixed

* Cleanup

* Fix Hero Screen Shake

* Clean up buff a bit more

* slight clean up

* Update README.md

Add Buff Options info/file
This commit is contained in:
GradualSyrup 2022-01-27 15:59:41 -06:00 committed by GitHub
parent 35a80f83c5
commit b598aaeee2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 431 additions and 2 deletions

View file

@ -115,6 +115,7 @@ When multiple options are selected, one of the selected options will be chosen a
| Throw Options | Throw to be performed when a grab is landed | None, Forward Throw, Back Throw, Up Throw, Down Throw |
| Throw Delay | How many frames to delay the throw option | 0 to 150 frames (2.5 seconds) in increments of 5 frames |
| Pummel Delay | How many frames after a grab to wait before starting to pummel | 0 to 150 frames (2.5 seconds) in increments of 5 frames |
| Buff Options | Buffs to be applied to respective character when loading save states | Acceleratle, Oomph, Psyche Up, Bounce, Arsene, Deep Breathing, Limit Break, KO Punch, One-Winged Angel |
| Input Delay | Frames to delay player inputs by | 0 to 10 frames (0.167 seconds) |
| Save Damage | Should save states retain player/CPU damage | Yes, No |
| Hitbox Visualization | Should hitboxes be displayed, hiding other visual effects | Yes, No |
@ -144,6 +145,7 @@ SD Card Root
│ ├── aerial_delay.svg
│ ├── air_dodge_dir.svg
│ ├── attack_angle.svg
│ ├── buff_state.svg
│ ├── check.svg
│ ├── defensive_state.svg
│ ├── di_state.svg
@ -231,4 +233,4 @@ To build the entire modpack including supporting files, use the steps in [`full_
## Prerequisites
- Stable Rust environment with [cargo-skyline](https://github.com/jam1garner/cargo-skyline)
- [DEVKITPRO](https://devkitpro.org/wiki/Getting_Started) `switch-dev` installation
- [DEVKITPRO](https://devkitpro.org/wiki/Getting_Started) `switch-dev` installation

View file

@ -502,6 +502,56 @@ impl ThrowOption {
extra_bitflag_impls! {ThrowOption}
// Buff Option
bitflags! {
pub struct BuffOption : u32
{
const ACCELERATLE = 0x1;
const OOMPH = 0x2;
const PSYCHE = 0x4;
const BOUNCE = 0x8;
const ARSENE = 0x10;
const BREATHING = 0x20;
const LIMIT = 0x40;
const KO = 0x80;
const WING = 0x100;
}
}
impl BuffOption {
pub fn into_int(self) -> Option<i32> {
Some(match self {
BuffOption::ACCELERATLE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND11_SPEED_UP,
BuffOption::OOMPH => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND12_ATTACK_UP,
BuffOption::PSYCHE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND21_CHARGE,
BuffOption::BOUNCE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND13_REFLECT,
BuffOption::BREATHING => 1,
BuffOption::ARSENE => 1,
BuffOption::LIMIT => 1,
BuffOption::KO => 1,
BuffOption::WING => 1,
_ => return None,
})
}
fn as_str(self) -> Option<&'static str> {
Some(match self {
BuffOption::ACCELERATLE => "Acceleratle",
BuffOption::OOMPH => "Oomph",
BuffOption::BOUNCE => "Bounce",
BuffOption::PSYCHE => "Psyche Up",
BuffOption::BREATHING => "Deep Breathing",
BuffOption::ARSENE => "Arsene",
BuffOption::LIMIT => "Limit Break",
BuffOption::KO => "KO Punch",
BuffOption::WING => "One-Winged Angel",
_ => return None,
})
}
}
extra_bitflag_impls! {BuffOption}
impl Delay {
pub fn as_str(self) -> Option<&'static str> {
Some(match self {
@ -836,6 +886,7 @@ url_params! {
pub throw_state: ThrowOption,
pub throw_delay: MedDelay,
pub pummel_delay: MedDelay,
pub buff_state: BuffOption,
}
}
@ -886,6 +937,7 @@ impl TrainingModpackMenu {
throw_state = ThrowOption::from_bits(val),
throw_delay = MedDelay::from_bits(val),
pummel_delay = MedDelay::from_bits(val),
buff_state = BuffOption::from_bits(val),
);
}
}

View file

@ -503,6 +503,13 @@ pub unsafe fn write_menu() {
MedDelay,
"Pummel Delay: How many frames after a grab to wait before starting to pummel"
);
add_bitflag_submenu!(
overall_menu,
"Buff Options",
buff_state,
BuffOption,
"Buff Options: Buff(s) to be applied to respective character when loading save states"
);
// Slider menus
overall_menu.add_sub_menu(

View file

@ -42,6 +42,7 @@ pub static BASE_MENU: consts::TrainingModpackMenu = consts::TrainingModpackMenu
throw_state: ThrowOption::NONE,
throw_delay: MedDelay::empty(),
pummel_delay: MedDelay::empty(),
buff_state: BuffOption::empty(),
};
pub static mut DEFAULT_MENU: TrainingModpackMenu = BASE_MENU;

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80.0px"
height="80.0px"
viewBox="0 0 80.0 80.0"
version="1.1"
id="SVGRoot"
sodipodi:docname="buff_state.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs5503" />
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="52.625"
inkscape:cy="33.6875"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="true"
inkscape:window-width="1431"
inkscape:window-height="1041"
inkscape:window-x="256"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:pagecheckerboard="1">
<inkscape:grid
type="xygrid"
id="grid6073"
spacingx="0.1"
spacingy="0.1" />
</sodipodi:namedview>
<metadata
id="metadata5506">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g243"
style="fill:#ffffea;fill-opacity:1"
transform="matrix(0.54490954,0,0,-0.44245854,-24.388508,217.32339)">
<g
id="g245"
style="fill:#ffffea;fill-opacity:1">
<g
id="g251"
style="fill:#ffffea;fill-opacity:1">
<g
id="g253"
style="fill:#ffffea;fill-opacity:1">
<path
d="m 58.348,376.03 c 3.332,-50.936 45.698,-55.695 45.698,-55.695 v 0 c -36.178,20.944 -24.437,58.551 -1.666,79.495 v 0 c 13.625,12.533 9.71,21.5 9.313,22.314 v 0 c 7.57,-14.95 1.868,-19.957 -2.411,-37.546 v 0 c -4.284,-17.614 6.188,-36.178 17.613,-33.44 v 0 c 11.424,2.737 5.95,17.969 5.95,17.969 v 0 c 26.419,-26.896 0.952,-44.746 0.952,-44.746 v 0 c 0,0 39.034,10.948 42.604,47.365 v 0 c 3.571,36.415 -25.705,51.411 -25.705,51.411 v 0 c 9.758,-8.093 9.283,-23.327 -1.904,-24.279 v 0 c -11.187,-0.951 -18.089,3.333 -10.71,35.464 v 0 c 7.378,32.132 -34.036,50.457 -34.036,50.457 v 0 C 124.039,438.387 55.015,426.963 58.348,376.03 m 53.314,46.174 c 0,0 0,0 0,0 v 0 c 0,0 0,0 0,0"
style="fill:#ffffea;stroke:none;fill-opacity:1"
id="path267" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

220
src/training/buff.rs Normal file
View file

@ -0,0 +1,220 @@
use crate::common::consts::*;
use crate::common::*;
use smash::app::{self, lua_bind::*};
use smash::lib::lua_const::*;
use crate::training::handle_add_limit;
use crate::training::frame_counter;
use crate::is_operation_cpu;
static mut BUFF_DELAY_COUNTER: usize = 0;
static mut BUFF_REMAINING_PLAYER: i32 = 0;
static mut BUFF_REMAINING_CPU: i32 = 0;
static mut IS_BUFFING_PLAYER: bool = false;
static mut IS_BUFFING_CPU: bool = false;
pub fn init() {
unsafe {
BUFF_DELAY_COUNTER = frame_counter::register_counter();
}
}
pub unsafe fn restart_buff(module_accessor: &mut app::BattleObjectModuleAccessor) {
if is_operation_cpu(module_accessor) {
IS_BUFFING_CPU = false;
return;
}
IS_BUFFING_PLAYER = false;
}
pub unsafe fn start_buff(module_accessor: &mut app::BattleObjectModuleAccessor) {
if is_operation_cpu(module_accessor) {
IS_BUFFING_CPU = true;
return;
}
IS_BUFFING_PLAYER = true;
}
pub unsafe fn is_buffing(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
if is_operation_cpu(module_accessor) {
return IS_BUFFING_CPU;
}
return IS_BUFFING_PLAYER;
}
pub unsafe fn set_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor, new_value: i32) {
if is_operation_cpu(module_accessor) {
BUFF_REMAINING_CPU = new_value;
return;
}
BUFF_REMAINING_PLAYER = new_value;
}
pub unsafe fn get_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 {
if is_operation_cpu(module_accessor) {
return BUFF_REMAINING_CPU;
}
return BUFF_REMAINING_PLAYER;
}
fn get_spell_vec() -> Vec<BuffOption> {
unsafe {
let menu_buff = MENU.buff_state.to_vec();
let menu_iter = menu_buff.iter();
let mut spell_buff: Vec<BuffOption> = Vec::new();
for buff in menu_iter {
if buff.into_int().unwrap_or(1) != 1 {
// all non-spells into_int as 1
spell_buff.push(*buff);
}
}
return spell_buff;
}
}
pub unsafe fn handle_buffs(module_accessor: &mut app::BattleObjectModuleAccessor, fighter_kind: i32, status: i32, percent: f32) -> bool {
SoundModule::stop_all_sound(module_accessor); // silences buff sfx other than KO Punch
ControlModule::stop_rumble(module_accessor, false);
MotionAnimcmdModule::set_sleep(module_accessor, false);
let menu_vec = MENU.buff_state.to_vec();
if fighter_kind == *FIGHTER_KIND_BRAVE {
return buff_hero(module_accessor,status);
} else if fighter_kind == *FIGHTER_KIND_JACK && menu_vec.contains(&BuffOption::ARSENE) {
return buff_joker(module_accessor);
} else if fighter_kind == *FIGHTER_KIND_WIIFIT && menu_vec.contains(&BuffOption::BREATHING) {
return buff_wiifit(module_accessor,status);
} else if fighter_kind == *FIGHTER_KIND_CLOUD && menu_vec.contains(&BuffOption::LIMIT) {
return buff_cloud(module_accessor);
} else if fighter_kind == *FIGHTER_KIND_LITTLEMAC && menu_vec.contains(&BuffOption::KO) {
return buff_mac(module_accessor);
} else if fighter_kind == *FIGHTER_KIND_EDGE && menu_vec.contains(&BuffOption::WING) {
return buff_sepiroth(module_accessor, percent);
}
return true;
}
unsafe fn buff_hero(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool {
let buff_vec = get_spell_vec();
if !is_buffing(module_accessor) {
// Initial set up for spells
start_buff(module_accessor);
set_buff_rem(module_accessor,buff_vec.len() as i32);
// Since it's the first step of buffing, we need to set up how many buffs there are
}
if get_buff_rem(module_accessor) <= 0 {
// If there are no buffs selected/left, we're done
return true;
}
buff_hero_single(module_accessor, status, buff_vec);
return false;
}
unsafe fn buff_hero_single(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32, buff_vec: Vec<BuffOption>) {
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
if prev_status_kind == FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START {
// If we just applied a buff successfully, subtract from buffs remaining
let new_rem_value = get_buff_rem(module_accessor) - 1;
set_buff_rem(module_accessor, new_rem_value);
}
let spell_index = get_buff_rem(module_accessor) - 1;
// Used to get spell from our vector
let spell_option = buff_vec.get(spell_index as usize);
if spell_option.is_none() {
// There are no spells selected, or something went wrong with making the vector
return;
}
let real_spell_value = spell_option.unwrap().into_int().unwrap();
if status != FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START {
WorkModule::set_int(module_accessor, real_spell_value, *FIGHTER_BRAVE_INSTANCE_WORK_ID_INT_SPECIAL_LW_DECIDE_COMMAND);
StatusModule::change_status_force(
module_accessor,
*FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START,
true,
// True to prevent Shielding over the spells
);
}
if status == FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START {
MotionModule::set_rate(module_accessor, 50.0);
}
}
unsafe fn buff_cloud(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
if !is_buffing(module_accessor) {
// Only need to add to the limit gauge once
start_buff(module_accessor);
handle_add_limit(100.0,module_accessor,0);
}
if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) {
// Need to wait 2 frames to make sure we stop the limit SFX, since it's a bit delayed
return false;
}
return true;
}
unsafe fn buff_joker(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
if !is_buffing(module_accessor) { // Only need to add to the rebel gauge once
start_buff(module_accessor);
let entry_id = app::FighterEntryID(FighterId::CPU as i32);
// Strangely, this doesn't actually matter and works for both fighters
app::FighterSpecializer_Jack::add_rebel_gauge(module_accessor, entry_id, 120.0);
}
if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) {
// Need to wait 2 frames to make sure we stop the voice call, since it's a bit delayed
return false;
}
return true;
}
unsafe fn buff_mac(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
WorkModule::set_float(module_accessor, 100.0, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLOAT_KO_GAGE);
// Trying to stop KO Punch from playing seems to make it play multiple times in rapid succession
// Look at 0x7100c44b60 for the func that handles this
// Need to figure out how to update the KO meter if this is fixed
return true;
}
unsafe fn buff_sepiroth(module_accessor: &mut app::BattleObjectModuleAccessor, percent: f32) -> bool {
start_buff(module_accessor);
if WorkModule::get_int(module_accessor, *FIGHTER_EDGE_INSTANCE_WORK_ID_INT_ONE_WINGED_WING_STATE) == 1 {
// Once we're in wing, heal to correct damage
DamageModule::heal(
module_accessor,
-1.0 * DamageModule::damage(module_accessor, 0),
0,
);
DamageModule::add_damage(module_accessor, percent, 0);
return true;
} else { // if we're not in wing, add damage
DamageModule::add_damage(module_accessor, 1000.0, 0);
}
return false;
}
unsafe fn buff_wiifit(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool {
if is_buffing(module_accessor) {
if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) {
// Need to wait 2 frames to make sure we stop breathing SFX
return false;
}
return true;
}
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
if prev_status_kind == FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS {
start_buff(module_accessor);
return false;
}
if status != FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS {
StatusModule::change_status_force(
module_accessor,
*FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS,
false,
);
} else {
MotionModule::set_rate(module_accessor, 40.0);
}
return false;
}

View file

View file

@ -5,6 +5,7 @@ use skyline::nn::ro::LookupSymbol;
use smash::app::{self, lua_bind::*};
use smash::lib::lua_const::*;
use smash::params::*;
use smash::phx::Hash40;
pub mod combo;
pub mod directional_influence;
@ -14,6 +15,7 @@ pub mod sdi;
pub mod shield;
pub mod tech;
pub mod throw;
pub mod buff;
mod air_dodge_direction;
mod attack_angle;
@ -305,6 +307,39 @@ fn params_main(params_info: &ParamsInfo<'_>) {
}
}
static CLOUD_ADD_LIMIT_OFFSET: usize = 0x008dc140; // this function is used to add limit to Cloud's limit gauge. Hooking it here so we can call it in buff.rs
#[skyline::hook(offset = CLOUD_ADD_LIMIT_OFFSET)]
pub unsafe fn handle_add_limit(add_limit: f32, module_accessor: &mut app::BattleObjectModuleAccessor, is_special_lw: u64) {
original!()(add_limit,module_accessor,is_special_lw)
}
#[skyline::hook(replace = EffectModule::req_screen)] // hooked to prevent the screen from darkening when loading a save state with One-Winged Angel
pub unsafe fn handle_req_screen(module_accessor: &mut app::BattleObjectModuleAccessor, my_hash: Hash40, bool_1:bool, bool_2:bool, bool_3:bool) -> u64 {
if !is_training_mode() {
return original!()(module_accessor,my_hash,bool_1,bool_2,bool_3);
}
let new_hash = my_hash.hash;
if new_hash == 72422354958 && buff::is_buffing(module_accessor) { // Wing bg hash
let replace_hash = Hash40::new("bg");
return original!()(module_accessor,replace_hash,bool_1,bool_2,bool_3);
}
original!()(module_accessor,my_hash,bool_1,bool_2,bool_3)
}
#[skyline::hook(replace = app::FighterSpecializer_Jack::check_doyle_summon_dispatch)] // returns status of summon dispatch if triggered, -1 as u64 otherwise
pub unsafe fn handle_check_doyle_summon_dispatch(module_accessor: &mut app::BattleObjectModuleAccessor, bool_1: bool, bool_2: bool) -> u64 {
let ori = original!()(module_accessor,bool_1,bool_2);
if !is_training_mode() {
return ori;
}
if ori == *FIGHTER_JACK_STATUS_KIND_SUMMON as u64 {
if buff::is_buffing(module_accessor) {
return 4294967295;
}
}
return ori;
}
#[allow(improper_ctypes)]
extern "C" {
fn add_nn_hid_hook(callback: fn(*mut NpadHandheldState, *const u32));
@ -366,6 +401,11 @@ pub fn training_mods() {
handle_is_enable_transition_term,
// SDI
crate::training::sdi::check_hit_stop_delay_command,
// Buffs
//get_param_float_hook,
handle_add_limit,
handle_check_doyle_summon_dispatch,
handle_req_screen,
);
combo::init();
@ -375,4 +415,5 @@ pub fn training_mods() {
ledge::init();
throw::init();
menu::init();
buff::init();
}

View file

@ -4,6 +4,7 @@ use crate::common::consts::SaveStateMirroring;
use crate::common::MENU;
use crate::common::{get_random_int, is_dead};
use crate::training::reset;
use crate::training::buff;
use smash::app::{self, lua_bind::*};
use smash::hash40;
use smash::lib::lua_const::*;
@ -16,6 +17,7 @@ enum SaveState {
KillPlayer,
PosMove,
NanaPosMove,
ApplyBuff,
}
struct SavedState {
@ -147,6 +149,15 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
.contains(&fighter_kind);
let fighter_is_popo = fighter_kind == *FIGHTER_KIND_POPO; // For making sure Popo doesn't steal Nana's PosMove
let fighter_is_nana = fighter_kind == *FIGHTER_KIND_NANA; // Don't want Nana to reopen savestates etc.
let fighter_is_buffable = [
*FIGHTER_KIND_BRAVE,
*FIGHTER_KIND_CLOUD,
*FIGHTER_KIND_JACK,
*FIGHTER_KIND_LITTLEMAC,
*FIGHTER_KIND_EDGE,
*FIGHTER_KIND_WIIFIT,
]
.contains(&fighter_kind);
// Grab + Dpad up: reset state
if ControlModule::check_button_on(module_accessor, *CONTROL_PAD_BUTTON_CATCH)
@ -253,9 +264,12 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
save_state.state = NoAction;
}
// if we're done moving, reset percent
// if we're done moving, reset percent and apply buffs
if save_state.state == NoAction {
set_damage(module_accessor, save_state.percent);
if fighter_is_buffable {
save_state.state = ApplyBuff;
}
}
// if the fighter is Popo, change the state to one where only Nana can move
@ -269,6 +283,16 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
return;
}
if save_state.state == ApplyBuff {
// needs its own save_state.state since this may take multiple frames, want it to loop
if buff::handle_buffs(module_accessor, fighter_kind, status, save_state.percent) {
// returns true when done buffing fighter
buff::restart_buff(module_accessor);
// set is_buffing back to false when done
save_state.state = NoAction;
}
}
// Grab + Dpad down: Save state
if ControlModule::check_button_on(module_accessor, *CONTROL_PAD_BUTTON_CATCH)