1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-20 00:46:34 +00:00

Feature: Clatter Strength (#337)

* Initial clatter work

* Add clatter to menus

* Change joint to hip

* Update SDI and Clatter to use the same enum

* Avoid early clatter/sdi input
This commit is contained in:
asimon-1 2022-05-04 02:23:01 -04:00 committed by GitHub
parent 92a3d5711d
commit 2cd382e0bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 250 additions and 157 deletions

View file

@ -1,138 +1,142 @@
pub mod consts;
pub mod events;
pub mod menu;
pub mod raygun_printer;
pub mod release;
use crate::common::consts::*;
use smash::app::{self, lua_bind::*};
use smash::lib::lua_const::*;
pub use crate::common::consts::MENU;
pub static mut DEFAULTS_MENU: TrainingModpackMenu = crate::common::consts::DEFAULTS_MENU;
pub static mut BASE_MENU: TrainingModpackMenu = unsafe { DEFAULTS_MENU };
pub static mut FIGHTER_MANAGER_ADDR: usize = 0;
pub static mut STAGE_MANAGER_ADDR: usize = 0;
#[cfg(not(feature = "outside_training_mode"))]
extern "C" {
#[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"]
pub fn is_training_mode() -> bool;
}
#[cfg(feature = "outside_training_mode")]
pub fn is_training_mode() -> bool {
return true;
}
pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 {
(module_accessor.info >> 28) as u8 as i32
}
pub fn is_emulator() -> bool {
unsafe { skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as u64 == 0x8004000 }
}
pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor {
let entry_id_int = fighter_id as i32;
let entry_id = app::FighterEntryID(entry_id_int);
unsafe {
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
let fighter_entry =
FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry;
let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry);
app::sv_battle_object::module_accessor(current_fighter_id as u32)
}
}
pub fn is_fighter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
get_category(module_accessor) == BATTLE_OBJECT_CATEGORY_FIGHTER
}
pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
unsafe {
if !is_fighter(module_accessor) {
return false;
}
let entry_id_int =
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
if entry_id_int == 0 {
return false;
}
let entry_id = app::FighterEntryID(entry_id_int);
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
let fighter_information =
FighterManager::get_fighter_information(mgr, entry_id) as *mut app::FighterInformation;
FighterInformation::is_operation_cpu(fighter_information)
}
}
pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
situation_kind == SITUATION_KIND_GROUND
}
pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
situation_kind == SITUATION_KIND_AIR
}
pub fn is_idle(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
status_kind == FIGHTER_STATUS_KIND_WAIT
}
pub fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
(*FIGHTER_STATUS_KIND_DAMAGE..*FIGHTER_STATUS_KIND_DAMAGE_FALL).contains(&status_kind)
}
pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
(*FIGHTER_STATUS_KIND_TREAD_DAMAGE..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind)
}
pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 };
(*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind)
}
pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
let prev_status = unsafe { StatusModule::prev_status_kind(module_accessor, 0) };
// If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun
status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE
|| (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE
&& status_kind == FIGHTER_STATUS_KIND_GUARD_OFF)
}
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let fighter_kind = app::utility::get_kind(module_accessor);
let fighter_is_ptrainer = [
*FIGHTER_KIND_PZENIGAME,
*FIGHTER_KIND_PFUSHIGISOU,
*FIGHTER_KIND_PLIZARDON,
]
.contains(&fighter_kind);
let status_kind = StatusModule::status_kind(module_accessor) as i32;
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
// Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation
// And the previous status is FIGHTER_STATUS_NONE
if fighter_is_ptrainer {
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|| (status_kind == FIGHTER_STATUS_KIND_WAIT
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
} else {
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
}
}
pub mod consts;
pub mod events;
pub mod menu;
pub mod raygun_printer;
pub mod release;
use crate::common::consts::*;
use smash::app::{self, lua_bind::*};
use smash::lib::lua_const::*;
pub use crate::common::consts::MENU;
pub static mut DEFAULTS_MENU: TrainingModpackMenu = crate::common::consts::DEFAULTS_MENU;
pub static mut BASE_MENU: TrainingModpackMenu = unsafe { DEFAULTS_MENU };
pub static mut FIGHTER_MANAGER_ADDR: usize = 0;
pub static mut STAGE_MANAGER_ADDR: usize = 0;
#[cfg(not(feature = "outside_training_mode"))]
extern "C" {
#[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"]
pub fn is_training_mode() -> bool;
}
#[cfg(feature = "outside_training_mode")]
pub fn is_training_mode() -> bool {
return true;
}
pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 {
(module_accessor.info >> 28) as u8 as i32
}
pub fn is_emulator() -> bool {
unsafe { skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as u64 == 0x8004000 }
}
pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor {
let entry_id_int = fighter_id as i32;
let entry_id = app::FighterEntryID(entry_id_int);
unsafe {
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
let fighter_entry =
FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry;
let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry);
app::sv_battle_object::module_accessor(current_fighter_id as u32)
}
}
pub fn is_fighter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
get_category(module_accessor) == BATTLE_OBJECT_CATEGORY_FIGHTER
}
pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
unsafe {
if !is_fighter(module_accessor) {
return false;
}
let entry_id_int =
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
if entry_id_int == 0 {
return false;
}
let entry_id = app::FighterEntryID(entry_id_int);
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
let fighter_information =
FighterManager::get_fighter_information(mgr, entry_id) as *mut app::FighterInformation;
FighterInformation::is_operation_cpu(fighter_information)
}
}
pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
situation_kind == SITUATION_KIND_GROUND
}
pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
situation_kind == SITUATION_KIND_AIR
}
pub fn is_idle(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
status_kind == FIGHTER_STATUS_KIND_WAIT
}
pub fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
(*FIGHTER_STATUS_KIND_DAMAGE..*FIGHTER_STATUS_KIND_DAMAGE_FALL).contains(&status_kind)
}
pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
(*FIGHTER_STATUS_KIND_TREAD_DAMAGE..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind)
}
pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 };
(*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind)
}
pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
let prev_status = unsafe { StatusModule::prev_status_kind(module_accessor, 0) };
// If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun
status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE
|| (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE
&& status_kind == FIGHTER_STATUS_KIND_GUARD_OFF)
}
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
let fighter_kind = app::utility::get_kind(module_accessor);
let fighter_is_ptrainer = [
*FIGHTER_KIND_PZENIGAME,
*FIGHTER_KIND_PFUSHIGISOU,
*FIGHTER_KIND_PLIZARDON,
]
.contains(&fighter_kind);
let status_kind = StatusModule::status_kind(module_accessor) as i32;
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
// Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation
// And the previous status is FIGHTER_STATUS_NONE
if fighter_is_ptrainer {
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|| (status_kind == FIGHTER_STATUS_KIND_WAIT
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
} else {
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
}
}
pub unsafe fn is_in_clatter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
ControlModule::get_clatter_time(module_accessor, 0) > 0.0
}

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="80"
height="80"
viewBox="0 0 21.166666 21.166667"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<g
id="layer2"
style="display:inline">
<path
style="fill:#ffffff;stroke:none;stroke-width:0.0497127;fill-opacity:1"
d="M 13.064037,0.15777454 C 8.1424266,0.80397653 3.2845429,3.5550995 1.5652428,8.4884178 -0.15917764,13.436426 2.3169541,19.190626 7.5459235,20.622254 8.7329642,20.947276 10.006356,21.008224 11.224666,20.852722 17.566967,20.0432 20.634193,13.067458 19.846047,7.3450248 19.413696,4.206053 17.925744,0.44725481 14.306856,0.18197818 13.892599,0.15161215 13.479984,0.10316141 13.064037,0.15777454 m 5.915816,6.88897386 c -0.824287,0.084412 -1.367697,1.0032528 -1.66145,1.6902331 -0.575375,1.3453265 -0.676441,2.8591785 -0.320746,4.2752955 0.152618,0.607689 0.386119,1.283832 0.888516,1.690233 -0.170267,0.95528 -1.129524,1.975436 -1.789659,2.634776 -2.336747,2.333913 -5.743263,3.326677 -8.8985796,2.202672 C 2.6731458,17.928122 1.0093348,12.893563 2.45869,8.5878433 2.9049613,7.262104 3.7953064,5.583255 4.9608613,4.7599623 c 0,1.6125322 1.2650897,2.6094715 2.7839132,2.8540084 C 9.8316157,7.9499792 12.349118,7.2792051 13.68475,5.5553662 14.496013,4.5083512 14.838633,2.7528502 13.80774,1.7296327 13.428282,1.353019 12.920069,1.1740531 12.417772,1.0315068 12.922058,0.81791598 13.723775,0.89852022 14.257143,0.96779492 17.150126,1.3435586 18.762956,4.3213617 18.979853,7.0467484 M 8.6893164,10.448492 c -0.6508391,0.09321 -1.2731928,0.277596 -1.8393712,0.621359 -2.0621489,1.251966 -2.6350683,4.142314 -1.0290437,6.018871 1.0799496,1.261858 2.7350854,1.804871 4.3597975,1.569531 0.528645,-0.07661 1.030843,-0.232059 1.491382,-0.505877 3.699025,-2.199391 1.102429,-8.288854 -2.9827646,-7.703884 z"
id="path5397" />
</g>
<g
id="layer3"
style="display:inline" />
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

49
src/training/clatter.rs Normal file
View file

@ -0,0 +1,49 @@
use crate::common::{is_in_clatter, is_operation_cpu, is_training_mode, MENU};
use smash::app::lua_bind::{ControlModule, EffectModule};
use smash::app::BattleObjectModuleAccessor;
use smash::lib::lua_const::*;
use smash::phx::{Hash40, Vector3f};
static mut COUNTER: u32 = 0;
unsafe fn do_clatter_input(module_accessor: &mut BattleObjectModuleAccessor) {
ControlModule::add_clatter_time(module_accessor, -8.0, 0);
let zeros = Vector3f {
x: 0.0,
y: 0.0,
z: 0.0,
};
EffectModule::req_on_joint(
module_accessor,
Hash40::new("sys_clatter"),
Hash40::new("hip"),
&zeros,
&zeros,
1.0,
&zeros,
&zeros,
true,
*EFFECT_SUB_ATTRIBUTE_NO_JOINT_SCALE as u32
| *EFFECT_SUB_ATTRIBUTE_FOLLOW as u32
| *EFFECT_SUB_ATTRIBUTE_CONCLUDE_STATUS as u32,
0,
0,
);
}
pub unsafe fn handle_clatter(module_accessor: &mut BattleObjectModuleAccessor) {
if !is_training_mode() || !is_operation_cpu(module_accessor) {
return;
}
if !is_in_clatter(module_accessor) {
return;
}
let repeat = MENU.clatter_strength.into_u32();
COUNTER = (COUNTER + 1) % repeat;
if COUNTER == repeat - 1 {
do_clatter_input(module_accessor);
}
}

View file

@ -10,6 +10,7 @@ use smash::phx::{Hash40, Vector3f};
pub mod buff;
pub mod charge;
pub mod clatter;
pub mod combo;
pub mod directional_influence;
pub mod frame_counter;
@ -114,6 +115,7 @@ fn once_per_frame_per_fighter(
hitbox_visualizer::get_command_flag_cat(module_accessor);
save_states::save_states(module_accessor);
tech::get_command_flag_cat(module_accessor);
clatter::handle_clatter(module_accessor);
}
fast_fall::get_command_flag_cat(module_accessor);

View file

@ -36,9 +36,10 @@ pub unsafe fn check_hit_stop_delay_command(
if !is_training_mode() || !is_operation_cpu(module_accessor) {
return original!()(module_accessor, sdi_direction);
}
let repeat = MENU.sdi_strength.into_u32();
COUNTER = (COUNTER + 1) % MENU.sdi_strength.into_u32();
if COUNTER == 1 {
COUNTER = (COUNTER + 1) % repeat;
if COUNTER == repeat - 1 {
if let Some(angle) = get_sdi_direction() {
// If there is a non-neutral direction picked,
// modify the SDI angle Vector2f as a side-effect

View file

@ -866,26 +866,29 @@ impl BoolFlag {
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter, Serialize, Deserialize)]
pub enum SdiStrength {
Normal = 0,
Medium = 1,
High = 2,
pub enum InputFrequency {
None = 0,
Normal = 1,
Medium = 2,
High = 4,
}
impl SdiStrength {
impl InputFrequency {
pub fn into_u32(self) -> u32 {
match self {
SdiStrength::Normal => 8,
SdiStrength::Medium => 6,
SdiStrength::High => 4,
InputFrequency::None => u32::MAX,
InputFrequency::Normal => 8,
InputFrequency::Medium => 6,
InputFrequency::High => 4,
}
}
pub fn as_str(self) -> Option<&'static str> {
Some(match self {
SdiStrength::Normal => "Normal",
SdiStrength::Medium => "Medium",
SdiStrength::High => "High",
InputFrequency::None => "None",
InputFrequency::Normal => "Normal",
InputFrequency::Medium => "Medium",
InputFrequency::High => "High",
})
}
@ -894,13 +897,13 @@ impl SdiStrength {
}
}
impl ToggleTrait for SdiStrength {
impl ToggleTrait for InputFrequency {
fn to_toggle_strs() -> Vec<&'static str> {
SdiStrength::iter().map(|i| i.as_str().unwrap_or("")).collect()
InputFrequency::iter().map(|i| i.as_str().unwrap_or("")).collect()
}
fn to_toggle_vals() -> Vec<usize> {
SdiStrength::iter().map(|i| i as usize).collect()
InputFrequency::iter().map(|i| i as usize).collect()
}
}
@ -958,7 +961,8 @@ url_params! {
pub stage_hazards: OnOff,
pub di_state: Direction,
pub sdi_state: Direction,
pub sdi_strength: SdiStrength,
pub sdi_strength: InputFrequency,
pub clatter_strength: InputFrequency,
pub air_dodge_dir: Direction,
pub mash_state: Action,
pub follow_up: Action,
@ -1018,6 +1022,7 @@ impl TrainingModpackMenu {
aerial_delay = Delay::from_bits(val),
air_dodge_dir = Direction::from_bits(val),
attack_angle = AttackAngle::from_bits(val),
clatter_strength = num::FromPrimitive::from_u32(val),
defensive_state = Defensive::from_bits(val),
di_state = Direction::from_bits(val),
falling_aerials = BoolFlag::from_bits(val),
@ -1082,7 +1087,8 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu {
stage_hazards: OnOff::Off,
di_state: Direction::empty(),
sdi_state: Direction::empty(),
sdi_strength: SdiStrength::Normal,
sdi_strength: InputFrequency::None,
clatter_strength: InputFrequency::None,
air_dodge_dir: Direction::empty(),
mash_state: Action::empty(),
follow_up: Action::empty(),
@ -1362,12 +1368,18 @@ pub unsafe fn get_menu() -> UiMenu<'static> {
"SDI Direction: Direction to angle the smash directional influence during hitlag",
false,
);
defensive_tab.add_submenu_with_toggles::<SdiStrength>(
defensive_tab.add_submenu_with_toggles::<InputFrequency>(
"SDI Strength",
"sdi_strength",
"SDI Strength: Relative strength of the smash directional influence inputs",
true,
);
defensive_tab.add_submenu_with_toggles::<InputFrequency>(
"Clatter Strength",
"clatter_strength",
"Clatter Strength: Relative strength of the mashing out of grabs, buries, etc.",
true,
);
defensive_tab.add_submenu_with_toggles::<LedgeOption>(
"Ledge Options",
"ledge_state",