diff --git a/dynamic/src/consts.rs b/dynamic/src/consts.rs
index d33b18baa9..7b245506b5 100644
--- a/dynamic/src/consts.rs
+++ b/dynamic/src/consts.rs
@@ -252,6 +252,11 @@ pub mod vars {
pub const DISABLE_ESCAPE_AIR_EFFECT_HANDLE: i32 = 0x0017;
+ // link arrow fuse
+ pub const FUSE_TEAM_NO: i32 = 0x0018;
+ pub const FUSE_STORED_ITEM_KIND: i32 = 0x0019;
+ pub const FUSE_STORED_TARGET_ID: i32 = 0x001A;
+
// floats
@@ -1014,6 +1019,21 @@ pub mod vars {
}
}
+ pub mod link_bowarrow {
+ pub mod instance {
+ // flags
+ pub const IS_ARROW_FUSE: i32 = 0x0100;
+ pub const IS_REFLECT_FIX: i32 = 0x0101;
+
+ // ints
+ pub const FUSED_ITEM_KIND: i32 = 0x0100;
+ pub const FUSED_ITEM_TYPE: i32 = 0x0101;
+ pub const FUSED_ITEM_POST_STATUS: i32 = 0x0102;
+ pub const FUSED_ITEM_ID: i32 = 0x0103;
+ pub const FUSED_ITEM_OWNER_ID: i32 = 0x0104;
+ }
+ }
+
pub mod littlemac {
pub mod instance {
// flags
@@ -2211,6 +2231,10 @@ pub mod statuses {
pub const SPECIAL_N_FIRE_HI: i32 = 0x1FA;
}
+ pub mod link {
+ pub const SPECIAL_LW_THROW: i32 = 0x1E8;
+ }
+
pub mod littlemac {
pub const SPECIAL_LW_CANCEL: i32 = 0x1F4;
pub const SPECIAL_LW_CANCEL_JUMP: i32 = 0x1F5;
diff --git a/fighters/common/src/function_hooks/controls.rs b/fighters/common/src/function_hooks/controls.rs
index d8bec849fa..99e808e527 100644
--- a/fighters/common/src/function_hooks/controls.rs
+++ b/fighters/common/src/function_hooks/controls.rs
@@ -1093,7 +1093,7 @@ static mut GC_TRIGGERS: [f32; 2] = [0.0, 0.0];
#[skyline::hook(offset = 0x366745c, inline)]
unsafe fn post_gamecube_process(ctx: &skyline::hooks::InlineCtx) {
let state: *mut skyline::nn::hid::NpadGcState =
- (ctx as *const _ as *mut u8).add(0x300) as *mut _;
+ (ctx.sp.x() as *const u8) as *mut _;
let controller: *mut Controller = ctx.registers[19].x() as _;
GC_TRIGGERS[0] = (*state).LTrigger as f32 / i16::MAX as f32;
diff --git a/fighters/common/src/function_hooks/vtables/krool.rs b/fighters/common/src/function_hooks/vtables/krool.rs
index 5b0c5a13a2..7ff0abd51c 100644
--- a/fighters/common/src/function_hooks/vtables/krool.rs
+++ b/fighters/common/src/function_hooks/vtables/krool.rs
@@ -24,7 +24,31 @@ pub unsafe extern "C" fn hook_on_damage(vtable: u64, battle_object: *mut Fighter
let ret = call_original!(vtable, battle_object, log);
- if opponent_boma.is_item() && opponent_boma.kind() == *ITEM_KIND_KROOLCROWN
+ // a crown fused to one of link's arrows, whether the crown or the arrow made contact
+ let fuse_arrow_id = if opponent_boma.is_item() && LinkModule::is_link(opponent_boma, *ITEM_LINK_NO_HAVE) {
+ LinkModule::get_parent_id(opponent_boma, *ITEM_LINK_NO_HAVE, true) as u32
+ }
+ else {
+ opponent_id
+ };
+ let mut fuse_arrow = std::ptr::null_mut();
+ if sv_battle_object::is_active(fuse_arrow_id) {
+ let arrow_boma = &mut *sv_battle_object::module_accessor(fuse_arrow_id);
+ if arrow_boma.is_weapon() && arrow_boma.kind() == *WEAPON_KIND_LINK_BOWARROW {
+ fuse_arrow = get_battle_object_from_id(fuse_arrow_id);
+ }
+ }
+ let is_crown_fuse_arrow = !fuse_arrow.is_null()
+ && VarModule::is_flag(fuse_arrow, vars::link_bowarrow::instance::IS_ARROW_FUSE)
+ && VarModule::get_int(fuse_arrow, vars::link_bowarrow::instance::FUSED_ITEM_KIND) == *ITEM_KIND_KROOLCROWN;
+
+ if is_crown_fuse_arrow
+ && boma.is_flag(*FIGHTER_KROOL_INSTANCE_WORK_ID_FLAG_DROP_CROWN) {
+ crate::opff::fuse::fuse_remove_item(fuse_arrow);
+ EffectModule::req_follow(boma, Hash40::new("sys_item_get"), Hash40::new("crown1"), &Vector3f::zero(), &Vector3f::zero(), 1.0, false, 0, 0, 0, 0, 0, false, false);
+ boma.off_flag(*FIGHTER_KROOL_INSTANCE_WORK_ID_FLAG_DROP_CROWN);
+ }
+ else if opponent_boma.is_item() && opponent_boma.kind() == *ITEM_KIND_KROOLCROWN
&& boma.is_flag(*FIGHTER_KROOL_INSTANCE_WORK_ID_FLAG_DROP_CROWN) {
StatusModule::change_status_request(opponent_boma, *ITEM_STATUS_KIND_DEAD, false);
EffectModule::req_follow(boma, Hash40::new("sys_item_get"), Hash40::new("crown1"), &Vector3f::zero(), &Vector3f::zero(), 1.0, false, 0, 0, 0, 0, 0, false, false);
diff --git a/fighters/common/src/misc.rs b/fighters/common/src/misc.rs
index ae8c659165..dc6ddc6b45 100644
--- a/fighters/common/src/misc.rs
+++ b/fighters/common/src/misc.rs
@@ -16,7 +16,7 @@ use globals::*;
#[skyline::hook(offset = 0xf13ddc, inline)]
unsafe fn steve_parry_stuff_fix(ctx: &mut skyline::hooks::InlineCtx) {
if ctx.registers[0].x() == 0x1D {
- *((ctx as *mut _ as *mut u8).add(0x300).add(0x98) as *mut u32) = 0x1;
+ *((ctx.sp.x() as *const u8).add(0x98) as *mut u32) = 0x1;
}
}
diff --git a/fighters/common/src/opff/fuse.rs b/fighters/common/src/opff/fuse.rs
new file mode 100644
index 0000000000..ba8983f347
--- /dev/null
+++ b/fighters/common/src/opff/fuse.rs
@@ -0,0 +1,362 @@
+use super::*;
+use globals::*;
+use smash::app::utility;
+
+#[repr(C)]
+pub struct CreateItemParam {
+ founder_pos: Vector4f,
+ item_pos: Vector4f,
+ item_kind: ItemKind,
+ another_battle_object_id: u32,
+ variation_kind: i32,
+ lr_dir: f32,
+ owner_id: u32,
+ unk_20: u32,
+ pokeball_or_assist_kind: i32,
+ unk_3c: f32,
+ unk_40: u32,
+ unk_44: u32,
+ unk_48: f32,
+ unk_4c: u32
+}
+
+#[skyline::from_offset(0x15db0b0)]
+unsafe fn create_item(item_manager: *const smash_rs::app::ItemManager, create_item_param: *mut CreateItemParam, unk: bool, unk2: bool, unk3: bool) -> *mut BattleObject;
+
+#[no_mangle]
+pub unsafe extern "Rust" fn item_owner_id_ptr(item_id: u32) -> *mut u32 {
+ let item = smash_rs::app::ItemManager::instance().unwrap().find_active_item_from_id(item_id);
+ if item.is_null() {
+ return std::ptr::null_mut();
+ }
+ (item as *mut u8).add(0x250) as *mut u32
+}
+
+// gives a released fused bomb back to whoever owned it before it was fused
+#[no_mangle]
+pub unsafe extern "Rust" fn restore_fused_item_owner(arrow_object: *mut BattleObject, item_boma: *mut BattleObjectModuleAccessor) {
+ if utility::get_kind(&mut *item_boma) != *ITEM_KIND_LINKBOMB {
+ return;
+ }
+ let owner_ptr = item_owner_id_ptr((*item_boma).battle_object_id);
+ if !owner_ptr.is_null() {
+ *owner_ptr = VarModule::get_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_OWNER_ID) as u32;
+ }
+}
+
+// returns the boma, object and fighter kind of the arrow's owner
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_get_owner(weapon_boma: *mut BattleObjectModuleAccessor) -> (*mut BattleObjectModuleAccessor, *mut BattleObject, i32) {
+ let owner_id = WorkModule::get_int(weapon_boma, *WEAPON_INSTANCE_WORK_ID_INT_LINK_OWNER) as u32;
+ let owner_boma = sv_battle_object::module_accessor(owner_id);
+ let owner_object = get_battle_object_from_id(owner_id);
+ let owner_kind = utility::get_kind(&mut *owner_boma);
+ (owner_boma, owner_object, owner_kind)
+}
+
+// returns the boma and object of the given id if it is an active Link arrow
+#[no_mangle]
+pub unsafe extern "Rust" fn get_arrow(object_id: u32) -> Option<(*mut BattleObjectModuleAccessor, *mut BattleObject)> {
+ if object_id == *BATTLE_OBJECT_ID_INVALID as u32 || !sv_battle_object::is_active(object_id) {
+ return None;
+ }
+ let boma = sv_battle_object::module_accessor(object_id);
+ if utility::get_category(&mut *boma) != *BATTLE_OBJECT_CATEGORY_WEAPON
+ || utility::get_kind(&mut *boma) != *WEAPON_KIND_LINK_BOWARROW {
+ return None;
+ }
+ Some((boma, get_battle_object_from_id(object_id)))
+}
+
+// attaches an item to the arrow's "top" bone, returning whether it was newly attached
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_attach_item(item_boma: *mut BattleObjectModuleAccessor, arrow_id: u32) -> bool {
+ if !LinkModule::is_link(item_boma, *ITEM_LINK_NO_HAVE) {
+ VisibilityModule::set_whole(item_boma, true);
+ LinkModule::link(item_boma, *ITEM_LINK_NO_HAVE, arrow_id);
+ LinkModule::set_model_constraint_pos_ort(item_boma, *ITEM_LINK_NO_HAVE, Hash40::new("top"), Hash40::new("top"), (*CONSTRAINT_FLAG_ORIENTATION | *CONSTRAINT_FLAG_POSITION) as u32, true);
+ return true;
+ }
+ false
+}
+
+// unlinks an item from whatever it is attached to, returning whether it was attached
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_detach_item(item_boma: *mut BattleObjectModuleAccessor) -> bool {
+ LinkModule::remove_model_constraint(item_boma, true);
+ if LinkModule::is_link(item_boma, *ITEM_LINK_NO_HAVE) {
+ LinkModule::unlink_all(item_boma);
+ return true;
+ }
+ false
+}
+
+// deletes the item fused to an arrow, if any
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_remove_item(arrow_object: *mut BattleObject) {
+ if !VarModule::is_flag(arrow_object, vars::link_bowarrow::instance::IS_ARROW_FUSE) {
+ return;
+ }
+ let item_id = VarModule::get_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_ID) as u32;
+ if sv_battle_object::is_active(item_id) {
+ smash_rs::app::ItemManager::instance().unwrap().remove_item_from_id(item_id);
+ }
+ VarModule::off_flag(arrow_object, vars::link_bowarrow::instance::IS_ARROW_FUSE);
+}
+
+// stores the fused item's info on the arrow/owner
+// pass ITEM_KIND_NONE / BATTLE_OBJECT_ID_INVALID to use the item the owner is currently holding.
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_set_arrow_params(arrow_boma: *mut BattleObjectModuleAccessor, item: i32, id: u32) {
+ let (owner_boma, owner_object, owner_kind) = fuse_get_owner(arrow_boma);
+ let arrow_object = get_battle_object_from_id((*arrow_boma).battle_object_id);
+
+ let kind = if item == *ITEM_KIND_NONE {
+ ItemModule::get_have_item_kind(owner_boma, 0)
+ }
+ else {
+ item
+ };
+ let item_id = if id == *BATTLE_OBJECT_ID_INVALID as u32 {
+ ItemModule::get_have_item_id(owner_boma, 0) as u32
+ }
+ else {
+ id
+ };
+ VarModule::set_int(owner_object, vars::common::instance::FUSE_TEAM_NO, TeamModule::team_no(owner_boma) as i32);
+
+ // 0: item travels with the arrow normally
+ // 1: arrow is fired straight and fast
+ let (item_type, post_status) = if kind == *ITEM_KIND_BOMBER {
+ (0, *ITEM_BOMBER_STATUS_KIND_BORN2)
+ }
+ else if [*ITEM_KIND_KILLER, *ITEM_KIND_BANANAGUN, *ITEM_KIND_DOLPHINBOMB].contains(&kind) {
+ (1, *ITEM_STATUS_KIND_THROW)
+ }
+ else if kind == *ITEM_KIND_LINKBOMB {
+ (0, *ITEM_STATUS_KIND_BORN)
+ }
+ else {
+ (0, *ITEM_STATUS_KIND_THROW)
+ };
+ VarModule::set_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_KIND, kind);
+ VarModule::set_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_TYPE, item_type);
+ VarModule::set_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_POST_STATUS, post_status);
+ VarModule::set_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_ID, item_id as i32);
+ let owner_ptr = item_owner_id_ptr(item_id);
+ let item_owner_id = if owner_ptr.is_null() { *BATTLE_OBJECT_ID_INVALID as u32 } else { *owner_ptr };
+ VarModule::set_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_OWNER_ID, item_owner_id as i32);
+}
+
+// spawns a new item of the given kind and fuses it to the given arrow
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_create_item(arrow_boma: *mut BattleObjectModuleAccessor, item: i32, founder_boma: *mut BattleObjectModuleAccessor, owner_id: u32) {
+ let pos = Vector4f{x: PostureModule::pos_x(founder_boma), y: PostureModule::pos_y(founder_boma), z: PostureModule::pos_z(founder_boma), w: 0.0};
+ let mut params = CreateItemParam {
+ founder_pos: pos,
+ item_pos: pos,
+ item_kind: ItemKind(item),
+ another_battle_object_id: *BATTLE_OBJECT_ID_INVALID as u32,
+ variation_kind: *ITEM_VARIATION_NONE,
+ lr_dir: PostureModule::lr(founder_boma),
+ owner_id: owner_id,
+ unk_20: 20,
+ pokeball_or_assist_kind: *ITEM_KIND_NONE,
+ unk_3c: 0.0,
+ unk_40: 0,
+ unk_44: 0,
+ unk_48: 1.0,
+ unk_4c: 0x63
+ };
+ let item_manager = smash_rs::app::ItemManager::instance().unwrap();
+ let battle_object = create_item(item_manager, &mut params, false, false, false);
+ if battle_object.is_null() {
+ return;
+ }
+ let item_boma = (*battle_object).module_accessor;
+ if ![*ITEM_KIND_HEALBALL, *ITEM_KIND_CHEWING, *ITEM_KIND_BOOMERANG].contains(&item) {
+ StatusModule::change_status_request(item_boma, *ITEM_STATUS_KIND_HAVE, false);
+ }
+ if item == *ITEM_KIND_LINKBOMB {
+ PostureModule::set_scale(item_boma, 1.3, false);
+ }
+ LinkModule::remove_model_constraint(item_boma, true);
+ fuse_attach_item(item_boma, (*arrow_boma).battle_object_id);
+ let arrow_object = get_battle_object_from_id((*arrow_boma).battle_object_id);
+ VarModule::on_flag(arrow_object, vars::link_bowarrow::instance::IS_ARROW_FUSE);
+ fuse_set_arrow_params(arrow_boma, item, (*item_boma).battle_object_id);
+}
+
+// ddd (and kirby with ddd's hat) store and re-create the fused item
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_dedede_common(fighter: &mut L2CFighterCommon) {
+ // TODO: the code below doesn't work. DDD strips the arrow of its fused item during NSpecial
+ // the old solution was to store the item on Link and copy it to DDD when he spits the arrow
+ // but that is unstable for obvious reasons. for now DDD simply spits back a normal arrow
+
+ // let nspecial_status = if fighter.kind() == *FIGHTER_KIND_KIRBY {
+ // if WorkModule::get_int(fighter.module_accessor, *FIGHTER_KIRBY_INSTANCE_WORK_ID_INT_COPY_CHARA) != *FIGHTER_KIND_DEDEDE {
+ // return;
+ // }
+ // *FIGHTER_KIRBY_STATUS_KIND_DEDEDE_SPECIAL_N_SHOT_OBJECT_HIT
+ // }
+ // else {
+ // *FIGHTER_DEDEDE_STATUS_KIND_SPECIAL_N_SHOT_OBJECT_HIT
+ // };
+
+ // if !fighter.is_status(nspecial_status) {
+ // VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, *ITEM_KIND_NONE);
+ // VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_TARGET_ID, *BATTLE_OBJECT_ID_INVALID);
+ // return;
+ // }
+ // let obj_id = WorkModule::get_int(fighter.module_accessor, *FIGHTER_DEDEDE_STATUS_SPECIAL_N_WORK_INT_SHOT_OBJECT_ID) as u32;
+ // let (obj_boma, obj_object) = match get_arrow(obj_id) {
+ // Some(arrow) => arrow,
+ // None => return
+ // };
+
+ // // store the arrow's item once, as it comes in
+ // if obj_id != VarModule::get_int(fighter.battle_object, vars::common::instance::FUSE_STORED_TARGET_ID) as u32 {
+ // VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_TARGET_ID, obj_id as i32);
+ // let fused_item = VarModule::get_int(obj_object, vars::link_bowarrow::instance::FUSED_ITEM_KIND);
+ // let stored_item = if fused_item > *ITEM_KIND_ASSIST { fused_item } else { *ITEM_KIND_NONE };
+ // VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, stored_item);
+ // fuse_remove_item(obj_object);
+ // }
+
+ // // re-create it once the arrow is spit
+ // let stored_item = VarModule::get_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND);
+ // if MotionModule::frame(fighter.module_accessor) >= 7.0
+ // && stored_item > *ITEM_KIND_ASSIST
+ // && WorkModule::is_flag(fighter.module_accessor, *FIGHTER_DEDEDE_STATUS_SPECIAL_N_FLAG_SHOT_OBJECT_SHOOT) {
+ // fuse_create_item(obj_boma, stored_item, fighter.module_accessor, (*fighter.module_accessor).battle_object_id);
+ // VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, *ITEM_KIND_NONE);
+ // }
+}
+
+// villager/isabelle (and kirby with their hat) store the fused item when pocketing the arrow, and delete the original
+// the item is re-created when the arrow is pulled back out
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_pocket_common(fighter: &mut L2CFighterCommon) {
+ if ![*FIGHTER_KIND_MURABITO, *FIGHTER_KIND_SHIZUE, *FIGHTER_KIND_KIRBY].contains(&fighter.kind()) {
+ return;
+ }
+
+ if fighter.kind() == *FIGHTER_KIND_KIRBY {
+ let copy_chara = WorkModule::get_int(fighter.module_accessor, *FIGHTER_KIRBY_INSTANCE_WORK_ID_INT_COPY_CHARA);
+ if ![*FIGHTER_KIND_MURABITO, *FIGHTER_KIND_SHIZUE].contains(©_chara) {
+ // if kirby is not storing a fused arrow, clear the stored item
+ if copy_chara != *FIGHTER_KIND_DEDEDE {
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, *ITEM_KIND_NONE);
+ }
+ return;
+ }
+ }
+
+ let search_statuses = if fighter.kind() == *FIGHTER_KIND_KIRBY {
+ [*FIGHTER_KIRBY_STATUS_KIND_MURABITO_SPECIAL_N_SEARCH, *FIGHTER_KIRBY_STATUS_KIND_SHIZUE_SPECIAL_N_SEARCH]
+ }
+ else {
+ [*FIGHTER_MURABITO_STATUS_KIND_SPECIAL_N_SEARCH, *FIGHTER_MURABITO_STATUS_KIND_SPECIAL_N_SEARCH]
+ };
+
+ // the pocket is lost on death
+ if fighter.is_status(*FIGHTER_STATUS_KIND_DEAD) {
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, *ITEM_KIND_NONE);
+ }
+ if !fighter.is_status_one_of(&search_statuses) {
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_TARGET_ID, *BATTLE_OBJECT_ID_INVALID);
+ return;
+ }
+
+ // kirby's copy abilities use the copied fighter's instance work ids
+ let obj_id = WorkModule::get_int(fighter.module_accessor, *FIGHTER_MURABITO_INSTANCE_WORK_ID_INT_TARGET_OBJECT_ID) as u32;
+ if obj_id == *BATTLE_OBJECT_ID_INVALID as u32
+ || obj_id == VarModule::get_int(fighter.battle_object, vars::common::instance::FUSE_STORED_TARGET_ID) as u32 {
+ return;
+ }
+
+ // handle each pocketed object once, replacing whatever fused item was stored before
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_TARGET_ID, obj_id as i32);
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, *ITEM_KIND_NONE);
+ let obj_object = match get_arrow(obj_id) {
+ Some((_, obj_object)) => obj_object,
+ None => return
+ };
+
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_TEAM_NO, TeamModule::team_no(fighter.module_accessor) as i32);
+ if VarModule::is_flag(obj_object, vars::link_bowarrow::instance::IS_ARROW_FUSE) {
+ let fused_item = VarModule::get_int(obj_object, vars::link_bowarrow::instance::FUSED_ITEM_KIND);
+ VarModule::set_int(fighter.battle_object, vars::common::instance::FUSE_STORED_ITEM_KIND, fused_item);
+ fuse_remove_item(obj_object);
+ }
+}
+
+// kirby's inhale deletes the fused item
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_kirby_common(fighter: &mut L2CFighterCommon) {
+ if !fighter.is_status(*FIGHTER_KIRBY_STATUS_KIND_SPECIAL_N_LOOP) {
+ return;
+ }
+ let obj_id = WorkModule::get_int(fighter.module_accessor, *FIGHTER_KIRBY_STATUS_SPECIAL_N_WORK_INT_INHALE_OBJECT_ID) as u32;
+ if let Some((_, obj_object)) = get_arrow(obj_id) {
+ fuse_remove_item(obj_object);
+ }
+}
+
+// rosalina's gravitational pull deletes the fused item
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_rosetta_common(fighter: &mut L2CFighterCommon) {
+ if !fighter.is_status(*FIGHTER_STATUS_KIND_SPECIAL_LW) {
+ return;
+ }
+ let obj_id = WorkModule::get_int(fighter.module_accessor, *FIGHTER_ROSETTA_STATUS_SPECIAL_LW_INT_CAPTURE_OBJECT_ID) as u32;
+ if let Some((_, obj_object)) = get_arrow(obj_id) {
+ fuse_remove_item(obj_object);
+ }
+}
+
+// drops the fused item if Link's (or Kirby's) NSpecial ends before the arrow is fired
+#[no_mangle]
+pub unsafe extern "Rust" fn fuse_special_n_exit(fighter: &mut L2CFighterCommon) {
+ if !ArticleModule::is_exist(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_BOWARROW) {
+ return;
+ }
+
+ let arrow_article = ArticleModule::get_article(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_BOWARROW);
+ if arrow_article.is_null() {
+ return;
+ }
+
+ // exit if the arrow is not active
+ let arrow_id = smash::app::lua_bind::Article::get_battle_object_id(arrow_article) as u32;
+ if !sv_battle_object::is_active(arrow_id) {
+ return;
+ }
+
+ // exit if the arrow is not fused
+ let arrow_object = get_battle_object_from_id(arrow_id);
+ if !VarModule::is_flag(arrow_object, vars::link_bowarrow::instance::IS_ARROW_FUSE) {
+ return;
+ }
+ VarModule::off_flag(arrow_object, vars::link_bowarrow::instance::IS_ARROW_FUSE);
+
+ // exit if fused item is not active
+ let fused_item_id = VarModule::get_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_ID) as u32;
+ if !sv_battle_object::is_active(fused_item_id) {
+ return;
+ }
+
+ // detach the item from the arrow and make it fall
+ let fused_item_boma = &mut (*sv_battle_object::module_accessor(fused_item_id));
+ let fused_item_kind = VarModule::get_int(arrow_object, vars::link_bowarrow::instance::FUSED_ITEM_KIND);
+ let item_next_status = if fused_item_kind == *ITEM_KIND_LINKBOMB {
+ (*ITEM_STATUS_KIND_BORN) // links bomb blows up in his face when dropped
+ }
+ else {
+ (*ITEM_STATUS_KIND_FALL)
+ };
+ fuse_detach_item(fused_item_boma);
+ restore_fused_item_owner(arrow_object, fused_item_boma);
+ StatusModule::change_status_request(fused_item_boma, item_next_status, false);
+}
diff --git a/fighters/common/src/opff/mod.rs b/fighters/common/src/opff/mod.rs
index 53ebdd5f34..068df49c55 100644
--- a/fighters/common/src/opff/mod.rs
+++ b/fighters/common/src/opff/mod.rs
@@ -18,6 +18,7 @@ pub mod floats;
pub mod other;
pub mod fe;
pub mod pocket;
+pub mod fuse;
use other::*;
diff --git a/fighters/dedede/src/opff.rs b/fighters/dedede/src/opff.rs
index 67d3dd7e6b..73009ff432 100644
--- a/fighters/dedede/src/opff.rs
+++ b/fighters/dedede/src/opff.rs
@@ -313,10 +313,16 @@ pub unsafe fn moveset(fighter: &mut L2CFighterCommon, boma: &mut BattleObjectMod
jet_hammer_charge(fighter, motion_kind);
}
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_dedede_common(fighter: &mut smash::lua2cpp::L2CFighterCommon);
+}
+
pub extern "C" fn dedede_frame_wrapper(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
unsafe {
common::opff::fighter_common_opff(fighter);
- dedede_frame(fighter)
+ dedede_frame(fighter);
+ fuse_dedede_common(fighter);
}
}
diff --git a/fighters/kirby/src/opff.rs b/fighters/kirby/src/opff.rs
index c99a671aee..c52b7bccee 100644
--- a/fighters/kirby/src/opff.rs
+++ b/fighters/kirby/src/opff.rs
@@ -10,6 +10,13 @@ extern "Rust" {
fn gimmick_flash(boma: &mut BattleObjectModuleAccessor);
}
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_kirby_common(fighter: &mut L2CFighterCommon);
+ fn fuse_pocket_common(fighter: &mut L2CFighterCommon);
+ fn fuse_dedede_common(fighter: &mut L2CFighterCommon);
+}
+
unsafe fn final_cutter_landing_bugfix(fighter: &mut L2CFighterCommon) {
if fighter.is_status(*FIGHTER_KIRBY_STATUS_KIND_SPECIAL_HI2)
&& MotionModule::frame(fighter.module_accessor) <= 2.0 {
@@ -108,7 +115,10 @@ pub unsafe fn moveset(fighter: &mut L2CFighterCommon) {
pub extern "C" fn kirby_frame_wrapper(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
unsafe {
common::opff::fighter_common_opff(fighter);
- kirby_frame(fighter)
+ kirby_frame(fighter);
+ fuse_kirby_common(fighter);
+ fuse_pocket_common(fighter);
+ fuse_dedede_common(fighter);
}
}
diff --git a/fighters/kirby/src/status/copy/link_special_n.rs b/fighters/kirby/src/status/copy/link_special_n.rs
new file mode 100644
index 0000000000..a6935f4f36
--- /dev/null
+++ b/fighters/kirby/src/status/copy/link_special_n.rs
@@ -0,0 +1,18 @@
+use super::*;
+
+// FIGHTER_KIRBY_STATUS_KIND_LINK_SPECIAL_N
+
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_special_n_exit(fighter: &mut L2CFighterCommon);
+}
+
+// drops the fused item if the arrow was never fired
+unsafe extern "C" fn special_n_exit(fighter: &mut L2CFighterCommon) -> L2CValue {
+ fuse_special_n_exit(fighter);
+ smashline::original_status(Exit, fighter, *FIGHTER_KIRBY_STATUS_KIND_LINK_SPECIAL_N)(fighter)
+}
+
+pub fn install(agent: &mut Agent) {
+ agent.status(Exit, *FIGHTER_KIRBY_STATUS_KIND_LINK_SPECIAL_N, special_n_exit);
+}
diff --git a/fighters/kirby/src/status/copy/mod.rs b/fighters/kirby/src/status/copy/mod.rs
index a3a0705b18..ed282fac86 100644
--- a/fighters/kirby/src/status/copy/mod.rs
+++ b/fighters/kirby/src/status/copy/mod.rs
@@ -14,6 +14,7 @@ mod ganon_special_n_float;
mod gaogaen_special_n;
mod koopa_special_n;
mod krool_special_n;
+mod link_special_n;
mod littlemac_special_n;
mod lucario_special_n;
mod lucas_special_n;
@@ -52,6 +53,7 @@ pub fn install(agent: &mut Agent) {
gaogaen_special_n::install(agent);
koopa_special_n::install(agent);
krool_special_n::install(agent);
+ link_special_n::install(agent);
littlemac_special_n::install(agent);
lucario_special_n::install(agent);
lucas_special_n::install(agent);
diff --git a/fighters/link/src/acmd/aerials.rs b/fighters/link/src/acmd/aerials.rs
index 9b554a67cc..887c7baa87 100644
--- a/fighters/link/src/acmd/aerials.rs
+++ b/fighters/link/src/acmd/aerials.rs
@@ -13,25 +13,19 @@ unsafe extern "C" fn game_attackairn(agent: &mut L2CAgentBase) {
FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
ATTACK(agent, 0, 0, Hash40::new("kneer"), 10.0, 361, 100, 0, 15, 4.5, -2.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 1, 0, Hash40::new("kneer"), 12.0, 361, 100, 0, 15, 4.5, 3.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 2, 0, Hash40::new("kneel"), 9.0, 361, 100, 0, 15, 4.0, 1.75, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 1, 0, Hash40::new("kneer"), 12.0, 361, 100, 0, 15, 4.5, 3.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 2, 0, Hash40::new("kneel"), 9.0, 361, 100, 0, 15, 4.0, 1.75, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
}
wait(lua_state, 3.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("kneer"), 8.0, 361, 100, 0, 10, 4.0, -2.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 1, 0, Hash40::new("kneer"), 8.0, 361, 100, 0, 10, 4.0, 3.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 2, 0, Hash40::new("kneel"), 8.0, 361, 100, 0, 10, 3.5, 1.75, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 0, 0, Hash40::new("kneer"), 7.0, 361, 100, 0, 10, 4.0, -2.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 1, 0, Hash40::new("kneer"), 7.0, 361, 100, 0, 10, 3.5, 3.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 2, 0, Hash40::new("kneel"), 7.0, 361, 100, 0, 10, 3.5, 1.75, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
}
- frame(lua_state, 28.0);
+ frame(lua_state, 36.0);
if is_excute(agent) {
AttackModule::clear_all(boma);
- }
- frame(lua_state, 34.0);
- if is_excute(agent) {
VarModule::on_flag(agent.battle_object, vars::link::status::ENABLE_SPECIAL_LW_CANCEL);
- }
- frame(lua_state, 36.0);
- if is_excute(agent) {
WorkModule::off_flag(boma, *FIGHTER_STATUS_ATTACK_AIR_FLAG_ENABLE_LANDING);
}
}
@@ -47,10 +41,10 @@ unsafe extern "C" fn game_attackairf(agent: &mut L2CAgentBase) {
frame(lua_state, 13.0);
FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 12.0, 361, 103, 0, 30, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 30, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 30, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 30, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 11.0, 44, 100, 0, 30, 3.0, -1.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 11.0, 44, 100, 0, 30, 3.0, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 30, 3.5, 4.2, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 30, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
}
frame(lua_state, 16.0);
FT_MOTION_RATE_RANGE(agent, 16.0, 20.0, 7.0);
@@ -61,10 +55,10 @@ unsafe extern "C" fn game_attackairf(agent: &mut L2CAgentBase) {
FT_MOTION_RATE(agent, 1.0);
frame(lua_state, 21.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 9.0, 68, 65, 0, 43, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 2, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 10.0, 68, 65, 0, 43, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 2, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 10.0, 68, 65, 0, 43, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 2, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 10.0, 68, 65, 0, 43, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 2, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 9.0, 68, 65, 0, 43, 3.0, -1.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 68, 65, 0, 43, 3.0, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 10.0, 68, 65, 0, 43, 3.5, 4.2, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 10.0, 68, 65, 0, 43, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
}
frame(lua_state, 24.0);
if is_excute(agent) {
@@ -131,12 +125,15 @@ unsafe extern "C" fn game_attackairb(agent: &mut L2CAgentBase) {
frame(lua_state, 6.0);
FT_MOTION_RATE_RANGE(agent, 6.0, 14.0, 10.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("hip"), 5.0, 70, 100, 40, 0, 4.8, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 1, 0, Hash40::new("hip"), 5.0, 365, 100, 40, 0, 4.8, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_A, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 2, 0, Hash40::new("kneer"), 5.0, 70, 100, 40, 0, 4.3, -0.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 3, 0, Hash40::new("kneer"), 5.0, 365, 100, 40, 0, 4.3, -0.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_A, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 4, 0, Hash40::new("kneer"), 5.0, 70, 100, 40, 0, 4.3, 5.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 5, 0, Hash40::new("kneer"), 5.0, 365, 100, 40, 0, 4.3, 5.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_A, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ // ground
+ ATTACK(agent, 0, 0, Hash40::new("hip"), 3.0, 70, 30, 0, 45, 4.8, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 1, 0, Hash40::new("kneer"), 3.0, 70, 30, 0, 45, 4.3, -0.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 2, 0, Hash40::new("kneer"), 3.0, 70, 30, 0, 45, 4.3, 5.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+
+ // air
+ ATTACK(agent, 3, 0, Hash40::new("hip"), 3.0, 365, 30, 0, 45, 4.8, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_A, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 4, 0, Hash40::new("kneer"), 3.0, 365, 30, 0, 45, 4.3, -0.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_A, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 5, 0, Hash40::new("kneer"), 3.0, 365, 30, 0, 45, 4.3, 5.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, true, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_A, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
}
wait(lua_state, 3.0);
if is_excute(agent) {
@@ -145,9 +142,9 @@ unsafe extern "C" fn game_attackairb(agent: &mut L2CAgentBase) {
frame(lua_state, 14.0);
FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("hip"), 7.0, 48, 90, 0, 25, 4.8, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 0, 0, Hash40::new("hip"), 7.0, 48, 90, 0, 25, 4.8, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
ATTACK(agent, 1, 0, Hash40::new("kneel"), 7.0, 48, 90, 0, 25, 4.3, -0.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
- ATTACK(agent, 2, 0, Hash40::new("kneel"), 7.0, 48, 90, 0, 25, 4.3, 5.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
+ ATTACK(agent, 2, 0, Hash40::new("kneel"), 7.0, 48, 90, 0, 25, 4.3, 5.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_B, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_KICK);
}
frame(lua_state, 19.0);
if is_excute(agent) {
diff --git a/fighters/link/src/acmd/ground.rs b/fighters/link/src/acmd/ground.rs
index 16997bc547..a4cd8b9bc6 100644
--- a/fighters/link/src/acmd/ground.rs
+++ b/fighters/link/src/acmd/ground.rs
@@ -4,8 +4,8 @@ unsafe extern "C" fn game_attack11(agent: &mut L2CAgentBase) {
let lua_state = agent.lua_state_agent;
let boma = agent.boma();
frame(lua_state, 1.0);
- FT_MOTION_RATE_RANGE(agent, 1.0, 6.0, 4.0);
- frame(lua_state, 6.0);
+ FT_MOTION_RATE_RANGE(agent, 1.0, 7.0, 4.0);
+ frame(lua_state, 7.0);
FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
ATTACK(agent, 0, 0, Hash40::new("armr"), 4.0, 70, 30, 0, 25, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA_d, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
@@ -134,17 +134,24 @@ unsafe extern "C" fn expression_attack13(agent: &mut L2CAgentBase) {
unsafe extern "C" fn game_attackdash(agent: &mut L2CAgentBase) {
let lua_state = agent.lua_state_agent;
let boma = agent.boma();
+ frame(lua_state, 3.0);
+ if is_excute(agent) {
+ WorkModule::on_flag(boma, *FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_WAIT_SHIELD);
+ HIT_NODE(agent, Hash40::new("head"), *HIT_STATUS_XLU);
+ HIT_NODE(agent, Hash40::new("shoulderl"), *HIT_STATUS_XLU);
+ HIT_NODE(agent, Hash40::new("arml"), *HIT_STATUS_XLU);
+ }
frame(lua_state, 7.0);
sv_kinetic_energy!(set_speed_mul, agent, FIGHTER_KINETIC_ENERGY_ID_MOTION, 0.93);
if is_excute(agent) {
- WorkModule::on_flag(boma, *FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_WAIT_SHIELD);
- ATTACK(agent, 0, 0, Hash40::new("top"), 12.0, 70, 93, 0, 40, 3.5, 0.0, 4.0, 6.0, Some(0.0), Some(10.0), Some(6.0), 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 3, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
+ HitModule::set_status_all(boma, HitStatus(*HIT_STATUS_NORMAL), 0);
+ ATTACK(agent, 0, 0, Hash40::new("top"), 12.0, 60, 93, 0, 40, 3.5, 0.0, 4.0, 6.0, Some(0.0), Some(10.0), Some(6.0), 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 3, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
}
frame(lua_state, 9.0);
sv_kinetic_energy!(set_speed_mul, agent, FIGHTER_KINETIC_ENERGY_ID_MOTION, 1.0);
frame(lua_state, 10.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("top"), 9.0, 62, 55, 0, 45, 5.5, 0.0, 8.5, 4.0, Some(0.0), Some(8.5), Some(4.75), 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 3, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_PUNCH, *ATTACK_REGION_OBJECT);
+ ATTACK(agent, 0, 0, Hash40::new("top"), 9.0, 60, 55, 0, 45, 5.5, 0.0, 8.5, 4.0, Some(0.0), Some(8.5), Some(4.75), 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_PUNCH, *ATTACK_REGION_OBJECT);
}
frame(lua_state, 15.0);
if is_excute(agent) {
diff --git a/fighters/link/src/acmd/other.rs b/fighters/link/src/acmd/other.rs
index a8001c2067..aab551ec70 100644
--- a/fighters/link/src/acmd/other.rs
+++ b/fighters/link/src/acmd/other.rs
@@ -226,6 +226,26 @@ unsafe extern "C" fn effect_win3(agent: &mut L2CAgentBase) {
}
}
+unsafe extern "C" fn game_squat(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ if is_excute(agent) {
+ WorkModule::on_flag(boma, *FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_WAIT_SHIELD);
+ }
+ frame(lua_state, 7.0);
+ if is_excute(agent) {
+ WorkModule::on_flag(boma, *FIGHTER_STATUS_WORK_ID_FLAG_RESERVE_CHANGE_STATUS_DLAY_MOTION);
+ }
+}
+
+unsafe extern "C" fn game_squatrv(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ if is_excute(agent) {
+ WorkModule::on_flag(boma, *FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_WAIT_SHIELD);
+ }
+}
+
pub fn install(agent: &mut Agent) {
agent.acmd("game_cliffescape", acmd_stub, Priority::Low);
@@ -252,4 +272,7 @@ pub fn install(agent: &mut Agent) {
agent.acmd("effect_win1", effect_win1, Priority::Low);
agent.acmd("effect_win3", effect_win3, Priority::Low);
+
+ agent.acmd("game_squat", game_squat, Priority::Low);
+ agent.acmd("game_squatrv", game_squatrv, Priority::Low);
}
diff --git a/fighters/link/src/acmd/smashes.rs b/fighters/link/src/acmd/smashes.rs
index 9362e91257..ca18e1372b 100644
--- a/fighters/link/src/acmd/smashes.rs
+++ b/fighters/link/src/acmd/smashes.rs
@@ -11,17 +11,17 @@ unsafe extern "C" fn game_attacks4(agent: &mut L2CAgentBase) {
frame(lua_state, 14.75);
FT_MOTION_RATE_RANGE(agent, 14.75, 15.0, 1.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 15.0, 45, 93, 0, 25, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 15.0, 45, 93, 0, 25, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 15.0, 45, 93, 0, 25, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 15.0, 45, 93, 0, 25, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
ATTACK(agent, 2, 0, Hash40::new("sword2"), 14.0, 45, 93, 0, 25, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 45, 93, 0, 25, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 45, 93, 0, 25, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
if DamageModule::damage(boma, 0) == 0.0 {
ArticleModule::generate_article(boma,*FIGHTER_LINK_GENERATE_ARTICLE_SWORD_BEAM, false, 0);
}
}
frame(lua_state, 15.0);
FT_MOTION_RATE(agent, 1.0);
- frame(lua_state, 18.0);
+ frame(lua_state, 17.0);
if is_excute(agent) {
AttackModule::clear_all(boma);
}
@@ -128,11 +128,11 @@ unsafe extern "C" fn game_attacks4s2(agent: &mut L2CAgentBase) {
frame(lua_state, 10.0);
FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("shoulderr"), 19.0, 65, 100, 0, 30, 3.0, 0.0, 0.0, 0.0, None, None, None, 1.25, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("armr"), 19.0, 65, 100, 0, 30, 3.0, 1.0, 0.0, 0.0, None, None, None, 1.25, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 19.0, 65, 100, 0, 30, 3.0, 0.0, 0.0, -2.0, None, None, None, 1.25, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 17.0, 65, 100, 0, 30, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 4, 0, Hash40::new("sword2"), 17.0, 65, 100, 0, 30, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("shoulderr"), 19.0, 65, 100, 0, 30, 3.0, 0.0, 0.0, 0.0, None, None, None, 1.2, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("armr"), 19.0, 65, 100, 0, 30, 3.0, 1.0, 0.0, 0.0, None, None, None, 1.2, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 19.0, 65, 100, 0, 30, 3.0, 0.0, 0.0, -2.0, None, None, None, 1.2, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 17.0, 65, 100, 0, 30, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 4, 0, Hash40::new("sword2"), 17.0, 65, 100, 0, 30, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
}
frame(lua_state, 11.0);
FT_MOTION_RATE_RANGE(agent, 11.0, 11.75, 1.0);
@@ -214,11 +214,11 @@ unsafe extern "C" fn game_attackhi4(agent: &mut L2CAgentBase) {
frame(lua_state, 41.0);
FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("sword2"), 9.0, 90, 93, 0, 70, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 90, 93, 0, 70, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 9.0, 90, 93, 0, 70, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("armr"), 9.0, 90, 93, 0, 70, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 4, 0, Hash40::new("top"), 9.0, 90, 93, 0, 70, 3.0, 0.0, 12.5, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("sword2"), 9.0, 90, 93, 0, 70, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 90, 93, 0, 70, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 9.0, 90, 93, 0, 70, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("armr"), 9.0, 90, 93, 0, 70, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 4, 0, Hash40::new("top"), 9.0, 90, 93, 0, 70, 3.0, 0.0, 12.5, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 10, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
}
frame(lua_state, 48.0);
if is_excute(agent) {
@@ -351,11 +351,11 @@ unsafe extern "C" fn game_attacklw4(agent: &mut L2CAgentBase) {
}
frame(lua_state, 9.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 13.0, 75, 95, 0, 30, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 16.0, 75, 95, 0, 30, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 16.0, 75, 95, 0, 30, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 16.0, 75, 95, 0, 30, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 4, 0, Hash40::new("kneel"), 13.0, 75, 95, 0, 30, 2.7, 0.5, 0.0, -0.9, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 13.0, 75, 95, 0, 30, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 16.0, 75, 95, 0, 30, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 16.0, 75, 95, 0, 30, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 16.0, 75, 95, 0, 30, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 4, 0, Hash40::new("kneel"), 13.0, 75, 95, 0, 30, 2.7, 0.5, 0.0, -0.9, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
AttackModule::set_attack_height_all(boma, app::AttackHeight(*ATTACK_HEIGHT_LOW), false);
}
wait(lua_state, 2.0);
@@ -364,11 +364,11 @@ unsafe extern "C" fn game_attacklw4(agent: &mut L2CAgentBase) {
}
frame(lua_state, 21.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 10.0, 75, 98, 0, 20, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 14.0, 86, 72, 0, 40, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 14.0, 86, 72, 0, 40, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 86, 72, 0, 40, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 4, 0, Hash40::new("kneer"), 10.0, 75, 98, 0, 20, 2.7, 1.5, 0.0, -0.7, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 10.0, 75, 98, 0, 20, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 14.0, 86, 72, 0, 40, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 14.0, 86, 72, 0, 40, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 86, 72, 0, 40, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 4, 0, Hash40::new("kneer"), 10.0, 75, 98, 0, 20, 2.7, 1.5, 0.0, -0.7, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 5, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
AttackModule::set_attack_height_all(boma, app::AttackHeight(*ATTACK_HEIGHT_LOW), false);
}
frame(lua_state, 23.0);
diff --git a/fighters/link/src/acmd/specials.rs b/fighters/link/src/acmd/specials.rs
index ad3bdd1af5..abfab42088 100644
--- a/fighters/link/src/acmd/specials.rs
+++ b/fighters/link/src/acmd/specials.rs
@@ -1,5 +1,52 @@
use super::*;
+unsafe extern "C" fn game_specials1(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ frame(lua_state, 5.0);
+ if is_excute(agent) {
+ ArticleModule::generate_article(boma, *FIGHTER_LINK_GENERATE_ARTICLE_BOOMERANG, false, -1);
+ }
+
+ frame(lua_state, 10.0);
+ let startup = if agent.is_flag(*FIGHTER_LINK_STATUS_BOOMERANG_FLAG_FLICK) {
+ 28.0
+ } else {
+ 23.0
+ };
+ FT_MOTION_RATE_RANGE(agent, 10.0, 25.0, startup - 12.0);
+
+ frame(lua_state, 25.0);
+ FT_MOTION_RATE(agent, 1.0);
+
+ frame(lua_state, 27.0);
+ let endlag = if agent.is_flag(*FIGHTER_LINK_STATUS_BOOMERANG_FLAG_FLICK) {
+ 15.0
+ } else {
+ 20.0
+ };
+ FT_MOTION_RATE_RANGE(agent, 27.0, 46.0, endlag);
+ if is_excute(agent) {
+ ArticleModule::shoot(boma, *FIGHTER_LINK_GENERATE_ARTICLE_BOOMERANG, app::ArticleOperationTarget(*ARTICLE_OPE_TARGET_ALL), false);
+ }
+
+ frame(lua_state, 46.0);
+ FT_MOTION_RATE(agent, 1.0);
+}
+
+unsafe extern "C" fn sound_specials1(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ frame(lua_state, 3.0);
+ if is_excute(agent) {
+ PLAY_SE(agent, Hash40::new("vc_link_attack03"));
+ }
+ frame(lua_state, 25.0);
+ if is_excute(agent) {
+ PLAY_SE(agent, Hash40::new("se_link_special_s01"));
+ }
+}
+
unsafe extern "C" fn game_specialhi(agent: &mut L2CAgentBase) {
let lua_state = agent.lua_state_agent;
let boma = agent.boma();
@@ -325,9 +372,70 @@ unsafe extern "C" fn effect_specialairhi(agent: &mut L2CAgentBase) {
}
}
+unsafe extern "C" fn game_speciallwthrow(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ frame(lua_state, 7.0);
+ if is_excute(agent) {
+ let have_item = ItemModule::get_have_item_id(boma, 0) as u32;
+ let have_item_boma = sv_battle_object::module_accessor(have_item);
+ ItemModule::throw_item(boma, 48.0, 2.5, 1.0, 0, true, agent.get_float(*ITEM_FIGHTER_VAR_FLOAT_ITEM_THROW_POWER));
+ PostureModule::add_pos_2d(have_item_boma, &Vector2f {x: (8.0 * agent.lr()), y: 0.0});
+ }
+}
+
+unsafe extern "C" fn effect_speciallwthrow(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ frame(lua_state, 5.0);
+ if is_excute(agent) {
+ LANDING_EFFECT(agent, Hash40::new("sys_atk_smoke"), Hash40::new("top"), 0, 0, 0, 0, 0, 0, 0.9, 0, 0, 0, 0, 0, 0, false);
+ }
+}
+
+unsafe extern "C" fn sound_speciallwthrow(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ frame(lua_state, 7.0);
+ if is_excute(agent) {
+ PLAY_SE(agent, Hash40::new("se_item_item_throw"));
+ }
+}
+
+unsafe extern "C" fn expression_speciallwthrow(agent: &mut L2CAgentBase) {
+ let lua_state = agent.lua_state_agent;
+ let boma = agent.boma();
+ if is_excute(agent) {
+ slope!(agent, *MA_MSC_CMD_SLOPE_SLOPE, *SLOPE_STATUS_LR);
+ VisibilityModule::set_int64(boma, hash40("shield"), hash40("shield_back"));
+ }
+ frame(lua_state, 5.0);
+ if is_excute(agent) {
+ ControlModule::set_rumble(boma, Hash40::new("rbkind_nohits"), 5, false, *BATTLE_OBJECT_ID_INVALID as u32);
+ }
+ frame(lua_state, 26.0);
+ if is_excute(agent) {
+ VisibilityModule::set_int64(boma, hash40("shield"), hash40("shield_normal"));
+ }
+}
+
pub fn install(agent: &mut Agent) {
+ agent.acmd("game_specials1", game_specials1, Priority::Low);
+ agent.acmd("game_specialairs1", game_specials1, Priority::Low);
+ agent.acmd("sound_specials1", sound_specials1, Priority::Low);
+ agent.acmd("sound_specialsair1", sound_specials1, Priority::Low);
+
agent.acmd("game_specialhi", game_specialhi, Priority::Low);
agent.acmd("effect_specialhi", effect_specialhi, Priority::Low);
agent.acmd("game_specialairhi", game_specialairhi, Priority::Low);
agent.acmd("effect_specialairhi", effect_specialairhi, Priority::Low);
+
+ agent.acmd("game_speciallwthrow", game_speciallwthrow, Priority::Low);
+ agent.acmd("game_specialairlwthrow", game_speciallwthrow, Priority::Low);
+ agent.acmd("effect_speciallwthrow", effect_speciallwthrow, Priority::Low);
+ agent.acmd("effect_specialairlwthrow", effect_speciallwthrow, Priority::Low);
+ agent.acmd("sound_speciallwthrow", sound_speciallwthrow, Priority::Low);
+ agent.acmd("sound_specialairlwthrow", sound_speciallwthrow, Priority::Low);
+ agent.acmd("expression_speciallwthrow", expression_speciallwthrow, Priority::Low);
+ agent.acmd("expression_specialairlwthrow", expression_speciallwthrow, Priority::Low);
}
diff --git a/fighters/link/src/acmd/tilts.rs b/fighters/link/src/acmd/tilts.rs
index bc51146d07..3a2be0f466 100644
--- a/fighters/link/src/acmd/tilts.rs
+++ b/fighters/link/src/acmd/tilts.rs
@@ -14,10 +14,10 @@ unsafe extern "C" fn game_attacks3(agent: &mut L2CAgentBase) {
frame(lua_state, 15.5);
FT_MOTION_RATE(agent, 2.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 13.0, 41, 90, 0, 35, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 13.0, 41, 90, 0, 35, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 13.0, 41, 90, 0, 35, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 35, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 13.0, 41, 90, 0, 35, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 13.0, 41, 90, 0, 35, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 35, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 14.0, 361, 100, 0, 35, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
AttackModule::set_attack_height_all(boma, app::AttackHeight(*ATTACK_HEIGHT_HIGH), false);
}
frame(lua_state, 16.0);
@@ -25,6 +25,14 @@ unsafe extern "C" fn game_attacks3(agent: &mut L2CAgentBase) {
frame(lua_state, 18.0);
FT_MOTION_RATE(agent, 1.0);
frame(lua_state, 19.0);
+ if is_excute(agent) {
+ ATTACK(agent, 4, 0, Hash40::new("top"), 13.0, 41, 90, 0, 40, 2.5, 0.0, 1.5, 8.0, Some(0.0), Some(1.5), Some(21.0), 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ AttackModule::clear(boma, 0, false);
+ AttackModule::clear(boma, 1, false);
+ AttackModule::clear(boma, 2, false);
+ AttackModule::clear(boma, 3, false);
+ }
+ frame(lua_state, 21.0);
if is_excute(agent) {
AttackModule::clear_all(boma);
}
@@ -100,10 +108,18 @@ unsafe extern "C" fn game_attackhi3(agent: &mut L2CAgentBase) {
let boma = agent.boma();
frame(lua_state, 8.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 9.0, 70, 122, 0, 30, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 70, 122, 0, 30, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 2, 0, Hash40::new("sword2"), 9.0, 70, 122, 0, 30, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 10.0, 85, 115, 0, 30, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("head"), 9.0, 99, 122, 0, 30, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 99, 122, 0, 30, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 9.0, 99, 122, 0, 30, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 10.0, 105, 112, 0, 30, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ }
+ frame(lua_state, 11.0);
+ if is_excute(agent) {
+ AttackModule::clear(boma, 0, false);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 9.0, 81, 122, 0, 30, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 81, 122, 0, 30, 3.5, 0.0, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 2, 0, Hash40::new("sword2"), 9.0, 81, 122, 0, 30, 3.5, 4.25, 0.0, -2.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 10.0, 87, 112, 0, 30, 3.5, 8.5, 0.0, -2.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_POS, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
}
frame(lua_state, 14.0);
if is_excute(agent) {
@@ -137,21 +153,22 @@ unsafe extern "C" fn effect_attackhi3(agent: &mut L2CAgentBase) {
unsafe extern "C" fn game_attacklw3(agent: &mut L2CAgentBase) {
let lua_state = agent.lua_state_agent;
let boma = agent.boma();
- frame(lua_state, 1.0);
- FT_MOTION_RATE_RANGE(agent, 1.0, 13.0, 9.0);
frame(lua_state, 13.0);
- FT_MOTION_RATE(agent, 1.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("armr"), 10.0, 64, 101, 0, 25, 2.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 1, 0, Hash40::new("sword2"), 11.0, 82, 91, 0, 45, 3.5, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_G, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 3, 0, Hash40::new("sword2"), 11.0, 82, 91, 0, 45, 3.5, 4.25, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
- ATTACK(agent, 4, 0, Hash40::new("sword2"), 12.0, 82, 91, 0, 50, 3.5, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_OFF, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 0, 0, Hash40::new("armr"), 9.0, 78, 80, 0, 55, 2.0, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 1, 0, Hash40::new("sword2"), 9.0, 78, 80, 0, 55, 3.0, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 3, 0, Hash40::new("sword2"), 11.0, 96, 80, 0, 55, 3.0, 4.25, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
+ ATTACK(agent, 4, 0, Hash40::new("sword2"), 11.0, 96, 80, 0, 55, 3.0, 8.5, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_F, false, 0, 0.0, 0, false, false, false, false, true, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_cutup"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_CUTUP, *ATTACK_REGION_SWORD);
AttackModule::set_attack_height_all(boma, app::AttackHeight(*ATTACK_HEIGHT_LOW), false);
}
frame(lua_state, 16.0);
if is_excute(agent) {
AttackModule::clear_all(boma);
}
+ frame(lua_state, 21.0);
+ if is_excute(agent) {
+ VarModule::on_flag(agent.battle_object, vars::link::status::ENABLE_SPECIAL_LW_CANCEL);
+ }
}
unsafe extern "C" fn effect_attacklw3(agent: &mut L2CAgentBase) {
@@ -168,10 +185,6 @@ unsafe extern "C" fn effect_attacklw3(agent: &mut L2CAgentBase) {
AFTER_IMAGE_OFF(agent, 4);
EFFECT_OFF_KIND(agent, Hash40::new("link_sword"), false, false);
}
- frame(lua_state, 21.0);
- if is_excute(agent) {
- VarModule::on_flag(agent.battle_object, vars::link::status::ENABLE_SPECIAL_LW_CANCEL);
- }
}
unsafe extern "C" fn expression_attacklw3(agent: &mut L2CAgentBase) {
diff --git a/fighters/link/src/boomerang/acmd.rs b/fighters/link/src/boomerang/acmd.rs
index e5177c8701..6b974a8424 100644
--- a/fighters/link/src/boomerang/acmd.rs
+++ b/fighters/link/src/boomerang/acmd.rs
@@ -4,16 +4,16 @@ unsafe extern "C" fn game_fly(agent: &mut L2CAgentBase) {
let lua_state = agent.lua_state_agent;
let boma = agent.boma();
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("top"), 10.0, 80, 29, 0, 91, 4.0, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
+ ATTACK(agent, 0, 0, Hash40::new("top"), 11.0, 78, 41, 0, 60, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_L, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
AttackModule::enable_safe_pos(boma);
}
- frame(lua_state, 10.0);
+ frame(lua_state, 8.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("top"), 8.0, 80, 37, 0, 74, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
+ ATTACK(agent, 0, 0, Hash40::new("top"), 8.0, 80, 46, 0, 57, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
}
frame(lua_state, 23.0);
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("top"), 6.0, 80, 44, 0, 55, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_M, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
+ ATTACK(agent, 0, 0, Hash40::new("top"), 5.0, 84, 51, 0, 54, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
}
}
@@ -39,7 +39,7 @@ unsafe extern "C" fn game_turn(agent: &mut L2CAgentBase) {
let lua_state = agent.lua_state_agent;
let boma = agent.boma();
if is_excute(agent) {
- ATTACK(agent, 0, 0, Hash40::new("top"), 4.0, 80, 40, 0, 50, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.1, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
+ ATTACK(agent, 0, 0, Hash40::new("top"), 5.0, 84, 51, 0, 54, 3.6, 0.0, 0.0, 0.0, None, None, None, 1.0, 1.0, *ATTACK_SETOFF_KIND_ON, *ATTACK_LR_CHECK_SPEED, false, 0, 0.0, 0, true, false, false, false, false, *COLLISION_SITUATION_MASK_GA, *COLLISION_CATEGORY_MASK_ALL, *COLLISION_PART_MASK_ALL, false, Hash40::new("collision_attr_normal"), *ATTACK_SOUND_LEVEL_S, *COLLISION_SOUND_ATTR_KICK, *ATTACK_REGION_OBJECT);
}
}
diff --git a/fighters/link/src/bowarrow/mod.rs b/fighters/link/src/bowarrow/mod.rs
new file mode 100644
index 0000000000..e7204573ca
--- /dev/null
+++ b/fighters/link/src/bowarrow/mod.rs
@@ -0,0 +1,11 @@
+use super::*;
+
+mod opff;
+mod status;
+
+pub fn install() {
+ let agent = &mut Agent::new("link_bowarrow");
+ opff::install(agent);
+ status::install(agent);
+ agent.install();
+}
diff --git a/fighters/link/src/bowarrow/opff.rs b/fighters/link/src/bowarrow/opff.rs
new file mode 100644
index 0000000000..05810b7a03
--- /dev/null
+++ b/fighters/link/src/bowarrow/opff.rs
@@ -0,0 +1,27 @@
+use super::*;
+
+// when a fused arrow is reflected, the fused item joins the reflector's team
+// the arrow's team is restored to its owner once it sticks (see stick end)
+unsafe fn fuse_reflect_fix(weapon: &mut L2CFighterBase) {
+ if !weapon.is_status(*WN_LINK_BOWARROW_STATUS_KIND_FLY)
+ || !AttackModule::is_infliction(weapon.module_accessor, *COLLISION_KIND_MASK_REFLECTOR)
+ || !VarModule::is_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_ARROW_FUSE) {
+ return;
+ }
+ VarModule::on_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_REFLECT_FIX);
+ let item_id = VarModule::get_int(weapon.battle_object, vars::link_bowarrow::instance::FUSED_ITEM_ID) as u32;
+ if !sv_battle_object::is_active(item_id) {
+ return;
+ }
+ let item_boma = sv_battle_object::module_accessor(item_id);
+ TeamModule::set_team(item_boma, TeamModule::team_no(weapon.module_accessor) as i32, true);
+ TeamModule::set_team_owner_id(item_boma, TeamModule::team_owner_id(weapon.module_accessor) as u32);
+}
+
+pub unsafe extern "C" fn link_bowarrow_frame(weapon: &mut L2CFighterBase) {
+ fuse_reflect_fix(weapon);
+}
+
+pub fn install(agent: &mut Agent) {
+ agent.on_line(Main, link_bowarrow_frame);
+}
diff --git a/fighters/link/src/bowarrow/status.rs b/fighters/link/src/bowarrow/status.rs
new file mode 100644
index 0000000000..074394ecb4
--- /dev/null
+++ b/fighters/link/src/bowarrow/status.rs
@@ -0,0 +1,166 @@
+use super::*;
+
+// symbol-based calls for link's arrow fuse
+extern "Rust" {
+ fn restore_fused_item_owner(arrow_object: *mut BattleObject, item_boma: *mut BattleObjectModuleAccessor);
+ fn fuse_get_owner(weapon_boma: *mut BattleObjectModuleAccessor) -> (*mut BattleObjectModuleAccessor, *mut BattleObject, i32);
+ fn fuse_set_arrow_params(arrow_boma: *mut BattleObjectModuleAccessor, item: i32, id: u32);
+ fn fuse_detach_item(item_boma: *mut BattleObjectModuleAccessor) -> bool;
+ fn fuse_attach_item(item_boma: *mut BattleObjectModuleAccessor, arrow_id: u32) -> bool;
+ fn fuse_remove_item(arrow_object: *mut BattleObject);
+ fn fuse_create_item(arrow_boma: *mut BattleObjectModuleAccessor, item: i32, founder_boma: *mut BattleObjectModuleAccessor, owner_id: u32);
+}
+
+extern "C" {
+ #[link_name = "_ZN3app10pacmanboss11PACMAN_LIFEEv"]
+ fn pacmanboss_life() -> i32;
+}
+
+// returns the fused item the arrow's owner has pocketed, if any
+unsafe extern "C" fn fuse_get_pocket_item(weapon_boma: *mut BattleObjectModuleAccessor) -> i32 {
+ let (_, owner_object, _) = fuse_get_owner(weapon_boma);
+ let item = VarModule::get_int(owner_object, vars::common::instance::FUSE_STORED_ITEM_KIND);
+ // the var starts at 0 (ITEM_KIND_ASSIST), which can't be fused
+ if item > *ITEM_KIND_ASSIST { item } else { *ITEM_KIND_NONE }
+}
+
+// re-creates the owner's pocketed fused item on the arrow being pulled back out
+unsafe extern "C" fn fuse_take_out_pocket_item(weapon_boma: *mut BattleObjectModuleAccessor) {
+ let item = fuse_get_pocket_item(weapon_boma);
+ if item == *ITEM_KIND_NONE {
+ return;
+ }
+ let (owner_boma, owner_object, _) = fuse_get_owner(weapon_boma);
+ fuse_create_item(weapon_boma, item, weapon_boma, (*owner_boma).battle_object_id);
+ VarModule::set_int(owner_object, vars::common::instance::FUSE_STORED_ITEM_KIND, *ITEM_KIND_NONE);
+}
+
+// restores item state that is normally set up when a fighter throws the item
+unsafe extern "C" fn fuse_on_item_released(arrow_object: *mut BattleObject, item_boma: *mut BattleObjectModuleAccessor) {
+ restore_fused_item_owner(arrow_object, item_boma);
+ if utility::get_kind(&mut *item_boma) == *ITEM_KIND_PACMANBOSS {
+ WorkModule::on_flag(item_boma, *ITEM_PACMAN_INSTANCE_WORK_FLAG_THROW);
+ WorkModule::off_flag(item_boma, *ITEM_PACMAN_INSTANCE_WORK_FLAG_DROP);
+ WorkModule::off_flag(item_boma, *ITEM_PACMAN_INSTANCE_WORK_FLAG_HIT);
+ WorkModule::off_flag(item_boma, *ITEM_PACMAN_INSTANCE_WORK_FLAG_HOP);
+ WorkModule::set_int(item_boma, 0, *ITEM_PACMAN_INSTANCE_WORK_INT_BOUND_COUNT);
+ WorkModule::set_int(item_boma, pacmanboss_life(), *ITEM_PACMAN_INSTANCE_WORK_INT_LIFE);
+ WorkModule::set_int(item_boma, *PACMANBOSS_STATE_FIRST, *ITEM_PACMANBOSS_INSTANCE_WORK_INT_STATE);
+ WorkModule::set_int(item_boma, 0, *ITEM_PACMANBOSS_INSTANCE_WORK_INT_THROW_FRAME);
+ WorkModule::set_int(item_boma, 0, *ITEM_PACMANBOSS_INSTANCE_WORK_INT_ROT_FRAME);
+ WorkModule::set_float(item_boma, 0.0, *ITEM_PACMANBOSS_INSTANCE_WORK_FLOAT_ROT_ANGLE);
+ }
+}
+
+// WN_LINK_BOWARROW_STATUS_KIND_HAVED //
+
+unsafe extern "C" fn haved_main(weapon: &mut L2CWeaponCommon) -> L2CValue {
+ let (owner_boma, owner_object, owner_kind) = fuse_get_owner(weapon.module_accessor);
+ let mut is_fused = false;
+ if ItemModule::is_have_item(owner_boma, 0)
+ && fuse_get_pocket_item(weapon.module_accessor) == *ITEM_KIND_NONE {
+ let kind = ItemModule::get_have_item_kind(owner_boma, 0);
+ let trait_flag = ItemModule::get_have_item_trait(owner_boma, 0);
+ // trait flags are bitmasks, so swing/shoot items can carry other bits as well
+ let is_swing_or_shoot = trait_flag & (*ITEM_TRAIT_FLAG_SWING | *ITEM_TRAIT_FLAG_SHOOT) as u64 != 0;
+ // projectile items that fire the arrow straight (see fuse_set_arrow_params)
+ let is_projectile = [*ITEM_KIND_KILLER, *ITEM_KIND_BANANAGUN, *ITEM_KIND_DOLPHINBOMB].contains(&kind);
+ is_fused = ![*ITEM_KIND_NONE, *ITEM_KIND_ASSIST, *ITEM_KIND_LINKARROW].contains(&kind)
+ && trait_flag != *ITEM_TRAIT_FLAG_NONE as u64
+ && (is_projectile || !is_swing_or_shoot);
+ if is_fused {
+ fuse_set_arrow_params(weapon.module_accessor, *ITEM_KIND_NONE, *BATTLE_OBJECT_ID_INVALID as u32);
+ }
+ }
+ VarModule::set_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_ARROW_FUSE, is_fused);
+
+ let ret = smashline::original_status(Main, weapon, *WN_LINK_BOWARROW_STATUS_KIND_HAVED)(weapon);
+ if is_fused {
+ // move the held item from the owner's hand onto the arrow
+ let item_id = VarModule::get_int(weapon.battle_object, vars::link_bowarrow::instance::FUSED_ITEM_ID) as u32;
+ let item_boma = sv_battle_object::module_accessor(item_id);
+ ItemModule::drop_item(owner_boma, 90.0, 0.0, 0);
+ StatusModule::change_status_request(item_boma, *ITEM_STATUS_KIND_HAVE, false);
+ fuse_detach_item(item_boma);
+ if fuse_attach_item(item_boma, (*weapon.module_accessor).battle_object_id) {
+ // TODO: PLAY_SE(weapon, Hash40::new("se_link_fuse"));
+ }
+ MotionModule::change_motion(weapon.module_accessor, Hash40::new("haved"), 0.0, 1.0, false, 0.0, false, false);
+ }
+ ret
+}
+
+// re-creates the item stored by Villager/Isabelle/Kirby when a pocketed fused arrow is pulled back out
+unsafe extern "C" fn haved_end(weapon: &mut L2CWeaponCommon) -> L2CValue {
+ fuse_take_out_pocket_item(weapon.module_accessor);
+ 0.into()
+}
+
+// WN_LINK_BOWARROW_STATUS_KIND_FLY //
+
+unsafe extern "C" fn fly_init(weapon: &mut L2CWeaponCommon) -> L2CValue {
+ smashline::original_status(Init, weapon, *WN_LINK_BOWARROW_STATUS_KIND_FLY)(weapon);
+ if VarModule::is_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_ARROW_FUSE)
+ && VarModule::get_int(weapon.battle_object, vars::link_bowarrow::instance::FUSED_ITEM_TYPE) == 1 {
+ // projectile items fly straight and fast, and hit harder
+ let lr = PostureModule::lr(weapon.module_accessor);
+ weapon.clear_lua_stack();
+ lua_args!(weapon, *WEAPON_KINETIC_ENERGY_RESERVE_ID_NORMAL, 12.0 * lr, 0.0);
+ sv_kinetic_energy::set_speed(weapon.lua_state_agent);
+ weapon.clear_lua_stack();
+ lua_args!(weapon, *WEAPON_KINETIC_ENERGY_RESERVE_ID_NORMAL, 0.0, 0.0);
+ sv_kinetic_energy::set_accel(weapon.lua_state_agent);
+ KineticModule::enable_energy(weapon.module_accessor, *WEAPON_KINETIC_ENERGY_RESERVE_ID_NORMAL);
+ AttackModule::set_power_mul(weapon.module_accessor, 2.5);
+ }
+ 0.into()
+}
+
+unsafe extern "C" fn fly_end(weapon: &mut L2CWeaponCommon) -> L2CValue {
+ if VarModule::is_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_ARROW_FUSE) {
+ let status_next = StatusModule::status_kind_next(weapon.module_accessor);
+ if status_next == *WN_LINK_BOWARROW_STATUS_KIND_STICK
+ || status_next == *WN_LINK_BOWARROW_STATUS_KIND_HIT_STICK
+ || AttackModule::is_infliction_status(weapon.module_accessor, *COLLISION_KIND_MASK_HIT) {
+ // the arrow landed or hit something, so release the item
+ let item_id = VarModule::get_int(weapon.battle_object, vars::link_bowarrow::instance::FUSED_ITEM_ID) as u32;
+ if sv_battle_object::is_active(item_id) {
+ let item_boma = sv_battle_object::module_accessor(item_id);
+ if fuse_detach_item(item_boma) {
+ let status = VarModule::get_int(weapon.battle_object, vars::link_bowarrow::instance::FUSED_ITEM_POST_STATUS);
+ StatusModule::change_status_request(item_boma, status, false);
+ fuse_on_item_released(weapon.battle_object, item_boma);
+ }
+ }
+ }
+ else {
+ // the arrow vanished without hitting anything
+ fuse_remove_item(weapon.battle_object);
+ }
+ }
+ EffectModule::detach_all(weapon.module_accessor, 5);
+ 0.into()
+}
+
+// WN_LINK_BOWARROW_STATUS_KIND_STICK / HIT_STICK //
+
+// restores the arrow's team after a reflected fused arrow sticks
+unsafe extern "C" fn stick_end(weapon: &mut L2CWeaponCommon) -> L2CValue {
+ if VarModule::is_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_REFLECT_FIX) {
+ let (owner_boma, owner_object, owner_kind) = fuse_get_owner(weapon.module_accessor);
+ let owner_team_no = VarModule::get_int(owner_object, vars::common::instance::FUSE_TEAM_NO);
+ TeamModule::set_team(weapon.module_accessor, owner_team_no, true);
+ TeamModule::set_team_owner_id(weapon.module_accessor, (*owner_boma).battle_object_id);
+ VarModule::off_flag(weapon.battle_object, vars::link_bowarrow::instance::IS_REFLECT_FIX);
+ }
+ 0.into()
+}
+
+pub fn install(agent: &mut Agent) {
+ agent.status(Main, *WN_LINK_BOWARROW_STATUS_KIND_HAVED, haved_main);
+ agent.status(End, *WN_LINK_BOWARROW_STATUS_KIND_HAVED, haved_end);
+ agent.status(Init, *WN_LINK_BOWARROW_STATUS_KIND_FLY, fly_init);
+ agent.status(End, *WN_LINK_BOWARROW_STATUS_KIND_FLY, fly_end);
+ agent.status(End, *WN_LINK_BOWARROW_STATUS_KIND_STICK, stick_end);
+ agent.status(End, *WN_LINK_BOWARROW_STATUS_KIND_HIT_STICK, stick_end);
+}
diff --git a/fighters/link/src/lib.rs b/fighters/link/src/lib.rs
index 28bb571dad..a18386decd 100644
--- a/fighters/link/src/lib.rs
+++ b/fighters/link/src/lib.rs
@@ -10,6 +10,7 @@ pub mod status;
// articles
mod boomerang;
+mod bowarrow;
use smash::{
lib::{
@@ -52,4 +53,5 @@ pub fn install() {
agent.install();
boomerang::install();
+ bowarrow::install();
}
\ No newline at end of file
diff --git a/fighters/link/src/opff.rs b/fighters/link/src/opff.rs
index 5882f28d75..729ca378c0 100644
--- a/fighters/link/src/opff.rs
+++ b/fighters/link/src/opff.rs
@@ -18,21 +18,36 @@ unsafe fn fastfall_specials(fighter: &mut L2CFighterCommon) {
}
}
-unsafe fn bomb_cancel(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
+unsafe fn bomb_cancel(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
if VarModule::is_flag(fighter.battle_object, vars::link::status::ENABLE_SPECIAL_LW_CANCEL)
&& AttackModule::is_infliction_status(fighter.module_accessor, *COLLISION_KIND_MASK_HIT)
&& ArticleModule::is_exist(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_LINKBOMB)
&& fighter.is_cat_flag(Cat1::SpecialLw) {
// let bomb_exists = ArticleModule::is_exist(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_LINKBOMB);
// println!("Bomb Exists: {}", bomb_exists);
- fighter.change_status_req(*FIGHTER_STATUS_KIND_SPECIAL_LW, true); {
+ fighter.change_status_req(*FIGHTER_STATUS_KIND_SPECIAL_LW, true);
}
}
+
+unsafe fn shield_scale_fix(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
+ if !fighter.is_status_one_of(&[
+ *FIGHTER_STATUS_KIND_ATTACK_DASH,
+ *FIGHTER_STATUS_KIND_SQUAT,
+ *FIGHTER_STATUS_KIND_SQUAT_RV,
+ ]) {
+ return;
+ }
+
+ if let Some(target) = smashline::api::get_target_function("lua2cpp_link.nrs", 0x6e40) {
+ let shield_set_scale: extern "C" fn(&mut L2CFighterCommon) = std::mem::transmute(target);
+ shield_set_scale(fighter);
+ }
}
pub unsafe fn moveset(fighter: &mut L2CFighterCommon, boma: &mut BattleObjectModuleAccessor, id: usize, cat: [i32 ; 4], status_kind: i32, situation_kind: i32, motion_kind: u64, stick_x: f32, stick_y: f32, facing: f32, frame: f32) {
fastfall_specials(fighter);
bomb_cancel(fighter);
+ shield_scale_fix(fighter);
}
pub extern "C" fn link_frame_wrapper(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
diff --git a/fighters/link/src/status/mod.rs b/fighters/link/src/status/mod.rs
index 66b8ed961f..7fbab027a4 100644
--- a/fighters/link/src/status/mod.rs
+++ b/fighters/link/src/status/mod.rs
@@ -3,6 +3,7 @@ use globals::*;
// status script import
mod special_n;
+mod special_s;
mod special_hi;
mod special_lw;
@@ -21,8 +22,32 @@ pub unsafe extern "C" fn link_situation_helper(fighter: &mut L2CFighterCommon) -
return 0.into()
}
+// attempting to boomerang when one was already out would use a fail motion anyways
+// so lets just prevent using boomerang in that instance, removing the annoying fail motion
+unsafe extern "C" fn should_use_special_s_callback(fighter: &mut L2CFighterCommon) -> L2CValue {
+ if fighter.is_flag(*FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_BOOMERANG) {
+ return false.into()
+ }
+ return true.into()
+}
+
+// fixes some issues where DSpecial wouldn't work unexpectedly
+unsafe extern "C" fn should_use_special_lw_callback(fighter: &mut L2CFighterCommon) -> L2CValue {
+ return true.into()
+}
+
+unsafe extern "C" fn on_start(fighter: &mut L2CFighterCommon) {
+ fighter.global_table[globals::USE_SPECIAL_S_CALLBACK].assign(&L2CValue::Ptr(should_use_special_s_callback as *const () as _));
+ fighter.global_table[globals::USE_SPECIAL_LW_CALLBACK].assign(&L2CValue::Ptr(should_use_special_lw_callback as *const () as _));
+ fighter.global_table[0x27].assign(&false.into()); // ground special transition handler
+ fighter.global_table[0x26].assign(&false.into()); // air special transition handler
+}
+
pub fn install(agent: &mut Agent) {
+ agent.on_start(on_start);
+
special_n::install(agent);
+ special_s::install(agent);
special_hi::install(agent);
special_lw::install(agent);
}
\ No newline at end of file
diff --git a/fighters/link/src/status/special_lw.rs b/fighters/link/src/status/special_lw.rs
index ad03eac111..d773358ff2 100644
--- a/fighters/link/src/status/special_lw.rs
+++ b/fighters/link/src/status/special_lw.rs
@@ -1,6 +1,11 @@
use super::*;
unsafe extern "C" fn special_lw_pre(fighter: &mut L2CFighterCommon) -> L2CValue {
+ if ItemModule::is_have_item(fighter.module_accessor, 0)
+ && ItemModule::get_have_item_kind(fighter.module_accessor, 0) == *ITEM_KIND_LINKBOMB {
+ fighter.set_status_kind_interrupt(statuses::link::SPECIAL_LW_THROW);
+ return 1.into()
+ }
let ret = smashline::original_status(Pre, fighter, *FIGHTER_STATUS_KIND_SPECIAL_LW)(fighter);
FighterStatusModuleImpl::set_fighter_status_data(
fighter.module_accessor,
@@ -47,7 +52,71 @@ unsafe extern "C" fn special_lw_blast_pre(fighter: &mut L2CFighterCommon) -> L2C
return 0.into();
}
+// statuses::link::SPECIAL_LW_THROW
+
+unsafe extern "C" fn special_lw_throw_pre(fighter: &mut L2CFighterCommon) -> L2CValue {
+ StatusModule::init_settings(
+ fighter.module_accessor,
+ app::SituationKind(*SITUATION_KIND_NONE),
+ *FIGHTER_KINETIC_TYPE_UNIQ,
+ *GROUND_CORRECT_KIND_KEEP as u32,
+ app::GroundCliffCheckKind(*GROUND_CLIFF_CHECK_KIND_ON_DROP),
+ true,
+ *FIGHTER_STATUS_WORK_KEEP_FLAG_NONE_FLAG,
+ *FIGHTER_STATUS_WORK_KEEP_FLAG_NONE_INT,
+ *FIGHTER_STATUS_WORK_KEEP_FLAG_NONE_FLOAT,
+ 0
+ );
+ FighterStatusModuleImpl::set_fighter_status_data(
+ fighter.module_accessor,
+ false,
+ *FIGHTER_TREADED_KIND_NO_REAC,
+ false,
+ false,
+ false,
+ *FIGHTER_LOG_MASK_FLAG_ATTACK_KIND_THROW_ITEM as u64,
+ *FIGHTER_STATUS_ATTR_START_TURN as u32,
+ *FIGHTER_POWER_UP_ATTACK_BIT_ITEM_SHOOT as u32,
+ 0
+ );
+ 0.into()
+}
+
+unsafe extern "C" fn special_lw_throw_main(fighter: &mut L2CFighterCommon) -> L2CValue {
+ fighter.change_motion_by_situation("special_lw_throw", "special_air_lw_throw", 0.0, 1.0, false, 0.0, false, false);
+ fighter.change_kinetic_by_situation(*FIGHTER_KINETIC_TYPE_GROUND_STOP, *FIGHTER_KINETIC_TYPE_MOTION_FALL);
+ fighter.ground_correct_by_situation(*GROUND_CORRECT_KIND_GROUND, *GROUND_CORRECT_KIND_AIR);
+ fighter.main_shift(special_lw_throw_main_loop)
+}
+
+unsafe extern "C" fn special_lw_throw_main_loop(fighter: &mut L2CFighterCommon) -> L2CValue {
+ fighter.sub_air_check_dive();
+ if CancelModule::is_enable_cancel(fighter.module_accessor) {
+ if fighter.sub_wait_ground_check_common(false.into()).get_bool()
+ || fighter.sub_air_check_fall_common().get_bool() {
+ return 1.into();
+ }
+ }
+ if MotionModule::is_end(fighter.module_accessor) {
+ fighter.change_status_by_situation(*FIGHTER_STATUS_KIND_WAIT, *FIGHTER_STATUS_KIND_FALL, false);
+ }
+ if StatusModule::is_situation_changed(fighter.module_accessor) {
+ fighter.change_motion_inherit_frame_by_situation("special_lw_throw", "special_air_lw_throw", -1.0, 1.0, 0.0, false, false);
+ fighter.change_kinetic_by_situation(*FIGHTER_KINETIC_TYPE_GROUND_STOP, *FIGHTER_KINETIC_TYPE_MOTION_FALL);
+ fighter.ground_correct_by_situation(*GROUND_CORRECT_KIND_GROUND, *GROUND_CORRECT_KIND_AIR);
+ }
+ 0.into()
+}
+
+unsafe extern "C" fn special_lw_throw_end(fighter: &mut L2CFighterCommon) -> L2CValue {
+ 0.into()
+}
+
pub fn install(agent: &mut Agent) {
agent.status(Pre, *FIGHTER_STATUS_KIND_SPECIAL_LW, special_lw_pre);
agent.status(Pre, *FIGHTER_LINK_STATUS_KIND_SPECIAL_LW_BLAST, special_lw_blast_pre);
+
+ agent.status(Pre, statuses::link::SPECIAL_LW_THROW, special_lw_throw_pre);
+ agent.status(Main, statuses::link::SPECIAL_LW_THROW, special_lw_throw_main);
+ agent.status(End, statuses::link::SPECIAL_LW_THROW, special_lw_throw_end);
}
\ No newline at end of file
diff --git a/fighters/link/src/status/special_n.rs b/fighters/link/src/status/special_n.rs
index d8142f1dba..f0fd9d4e3b 100644
--- a/fighters/link/src/status/special_n.rs
+++ b/fighters/link/src/status/special_n.rs
@@ -619,7 +619,19 @@ unsafe extern "C" fn special_n_helper(fighter: &mut L2CFighterCommon) {
ArticleModule::shoot(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_BOWARROW, app::ArticleOperationTarget(*ARTICLE_OPE_TARGET_FIRST), true);
}
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_special_n_exit(fighter: &mut L2CFighterCommon);
+}
+
+// drops the fused item if the arrow was never fired
+pub unsafe extern "C" fn special_n_exit(fighter: &mut L2CFighterCommon) -> L2CValue {
+ fuse_special_n_exit(fighter);
+ smashline::original_status(Exit, fighter, *FIGHTER_STATUS_KIND_SPECIAL_N)(fighter)
+}
+
pub fn install(agent: &mut Agent) {
agent.status(Pre, *FIGHTER_STATUS_KIND_SPECIAL_N, special_n_pre);
agent.status(Main, *FIGHTER_STATUS_KIND_SPECIAL_N, special_n_main);
+ agent.status(Exit, *FIGHTER_STATUS_KIND_SPECIAL_N, special_n_exit);
}
\ No newline at end of file
diff --git a/fighters/link/src/status/special_s.rs b/fighters/link/src/status/special_s.rs
new file mode 100644
index 0000000000..0bdf982b1e
--- /dev/null
+++ b/fighters/link/src/status/special_s.rs
@@ -0,0 +1,223 @@
+use super::*;
+
+// FIGHTER_STATUS_KIND_SPECIAL_S
+
+unsafe extern "C" fn special_s_pre(fighter: &mut L2CFighterCommon) -> L2CValue {
+ let log_mask = if !fighter.is_flag(*FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_BOOMERANG) {
+ (*FIGHTER_LOG_MASK_FLAG_ATTACK_KIND_SPECIAL_S | *FIGHTER_LOG_MASK_FLAG_ACTION_CATEGORY_ATTACK | *FIGHTER_LOG_MASK_FLAG_ACTION_TRIGGER_ON | *FIGHTER_LOG_MASK_FLAG_SHOOT) as u64
+ }
+ else {
+ 0
+ };
+ StatusModule::init_settings(
+ fighter.module_accessor,
+ app::SituationKind(*SITUATION_KIND_NONE),
+ *FIGHTER_KINETIC_TYPE_UNIQ,
+ *GROUND_CORRECT_KIND_KEEP as u32,
+ app::GroundCliffCheckKind(*GROUND_CLIFF_CHECK_KIND_NONE),
+ true,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ FighterStatusModuleImpl::set_fighter_status_data(
+ fighter.module_accessor,
+ false,
+ *FIGHTER_TREADED_KIND_NO_REAC,
+ false,
+ false,
+ false,
+ log_mask,
+ *FIGHTER_STATUS_ATTR_START_TURN as u32,
+ *FIGHTER_POWER_UP_ATTACK_BIT_SPECIAL_S as u32,
+ 0
+ );
+ return false.into();
+}
+
+unsafe extern "C" fn special_s_init(fighter: &mut L2CFighterCommon) -> L2CValue {
+ return false.into();
+}
+
+unsafe extern "C" fn special_s_main(fighter: &mut L2CFighterCommon) -> L2CValue {
+ PostureModule::set_stick_lr(fighter.module_accessor, 0.0);
+ PostureModule::update_rot_y_lr(fighter.module_accessor);
+ let (motion, motion_air) = if fighter.is_flag(*FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_BOOMERANG) {
+ (hash40("special_s1_blank"), hash40("special_air_s1_blank"))
+ }
+ else {
+ (hash40("special_s1"), hash40("special_air_s1"))
+ };
+ fighter.set_int64(motion, *FIGHTER_LINK_STATUS_BOOMERANG_WORK_INT_MOTION);
+ fighter.set_int64(motion_air, *FIGHTER_LINK_STATUS_BOOMERANG_WORK_INT_MOTION_AIR);
+ fighter.sub_shift_status_main(L2CValue::Ptr(special_s_main_loop as *const () as _))
+}
+
+unsafe extern "C" fn special_s_main_loop(fighter: &mut L2CFighterCommon) -> L2CValue {
+ if fighter.sub_transition_group_check_air_cliff().get_bool() {
+ return true.into();
+ }
+ if CancelModule::is_enable_cancel(fighter.module_accessor)
+ && (fighter.sub_wait_ground_check_common(false.into()).get_bool()
+ || fighter.sub_air_check_fall_common().get_bool()) {
+ return false.into();
+ }
+ if !StatusModule::is_changing(fighter.module_accessor)
+ && MotionModule::is_end(fighter.module_accessor) {
+ if fighter.global_table[SITUATION_KIND] == SITUATION_KIND_GROUND {
+ fighter.change_status(FIGHTER_STATUS_KIND_WAIT.into(), false.into());
+ return true.into();
+ }
+ if fighter.global_table[SITUATION_KIND] == SITUATION_KIND_AIR {
+ fighter.change_status(FIGHTER_STATUS_KIND_FALL.into(), false.into());
+ return true.into();
+ }
+ }
+ if link_situation_helper(fighter).get_bool() {
+ special_s_set_kinetics(fighter);
+ }
+
+ // tap/hold input for boomerang distance
+ if fighter.check_hold_input(0, 8, Buttons::SpecialAll) {
+ fighter.on_flag(*FIGHTER_LINK_STATUS_BOOMERANG_FLAG_FLICK);
+ notify_event_msc_cmd!(fighter, Hash40::new_raw(0x20cbc92683), 1, FIGHTER_LOG_DATA_INT_HAJIKI_NUM);
+ }
+
+ return false.into();
+}
+
+unsafe extern "C" fn special_s_set_kinetics(fighter: &mut L2CFighterCommon) {
+ let motion_id = if fighter.global_table[SITUATION_KIND] == SITUATION_KIND_GROUND {
+ KineticModule::change_kinetic(fighter.module_accessor, *FIGHTER_KINETIC_TYPE_GROUND_STOP);
+ GroundModule::correct(fighter.module_accessor, app::GroundCorrectKind(*GROUND_CORRECT_KIND_GROUND_CLIFF_STOP_ATTACK));
+ *FIGHTER_LINK_STATUS_BOOMERANG_WORK_INT_MOTION
+ }
+ else {
+ KineticModule::change_kinetic(fighter.module_accessor, *FIGHTER_KINETIC_TYPE_AIR_STOP);
+ GroundModule::correct(fighter.module_accessor, app::GroundCorrectKind(*GROUND_CORRECT_KIND_AIR));
+ *FIGHTER_LINK_STATUS_BOOMERANG_WORK_INT_MOTION_AIR
+ };
+ let motion = Hash40::new_raw(fighter.get_int64(motion_id));
+ if fighter.is_flag(*FIGHTER_LINK_STATUS_BOOMERANG_FLAG_FIRST) {
+ MotionModule::change_motion_inherit_frame(fighter.module_accessor, motion, -1.0, 1.0, 0.0, false, false);
+ }
+ else {
+ MotionModule::change_motion(fighter.module_accessor, motion, 0.0, 1.0, false, 0.0, false, false);
+ fighter.on_flag(*FIGHTER_LINK_STATUS_BOOMERANG_FLAG_FIRST);
+ }
+}
+
+unsafe extern "C" fn special_s_end(fighter: &mut L2CFighterCommon) -> L2CValue {
+ return false.into();
+}
+
+unsafe extern "C" fn special_s_exit(fighter: &mut L2CFighterCommon) -> L2CValue {
+ if !fighter.is_flag(*FIGHTER_LINK_INSTANCE_WORK_ID_FLAG_BOOMERANG) {
+ ArticleModule::remove_exist(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_BOOMERANG, app::ArticleOperationTarget(*ARTICLE_OPE_TARGET_ALL));
+ }
+ return false.into();
+}
+
+// FIGHTER_LINK_STATUS_KIND_SPECIAL_S2
+
+unsafe extern "C" fn special_s2_pre(fighter: &mut L2CFighterCommon) -> L2CValue {
+ StatusModule::init_settings(
+ fighter.module_accessor,
+ app::SituationKind(*SITUATION_KIND_NONE),
+ *FIGHTER_KINETIC_TYPE_UNIQ,
+ *GROUND_CORRECT_KIND_KEEP as u32,
+ app::GroundCliffCheckKind(*GROUND_CLIFF_CHECK_KIND_NONE),
+ true,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ FighterStatusModuleImpl::set_fighter_status_data(
+ fighter.module_accessor,
+ false,
+ *FIGHTER_TREADED_KIND_NO_REAC,
+ false,
+ false,
+ false,
+ 0,
+ 0,
+ *FIGHTER_POWER_UP_ATTACK_BIT_SPECIAL_S as u32,
+ 0
+ );
+ false.into()
+}
+
+unsafe extern "C" fn special_s2_main(fighter: &mut L2CFighterCommon) -> L2CValue {
+ special_s2_set_kinetics(fighter);
+ fighter.sub_shift_status_main(L2CValue::Ptr(special_s2_main_loop as *const () as _))
+}
+
+unsafe extern "C" fn special_s2_main_loop(fighter: &mut L2CFighterCommon) -> L2CValue {
+ if fighter.sub_transition_group_check_air_cliff().get_bool() {
+ return true.into();
+ }
+ if CancelModule::is_enable_cancel(fighter.module_accessor) {
+ if fighter.sub_wait_ground_check_common(false.into()).get_bool()
+ || fighter.sub_air_check_fall_common().get_bool() {
+ return true.into();
+ }
+ }
+ if MotionModule::is_end(fighter.module_accessor) {
+ if fighter.global_table[SITUATION_KIND] == SITUATION_KIND_GROUND {
+ fighter.change_status(FIGHTER_STATUS_KIND_WAIT.into(), false.into());
+ return true.into();
+ }
+ if fighter.global_table[SITUATION_KIND] == SITUATION_KIND_AIR {
+ fighter.change_status(FIGHTER_STATUS_KIND_FALL.into(), false.into());
+ return true.into();
+ }
+ }
+ if !StatusModule::is_changing(fighter.module_accessor) {
+ let mtrans = fighter.get_int(*FIGHTER_LINK_STATUS_BOOMERANG_CATCH_INT_MTRANS);
+ if fighter.global_table[PREV_SITUATION_KIND] != mtrans
+ && fighter.global_table[SITUATION_KIND] == mtrans {
+ special_s2_set_kinetics(fighter);
+ }
+ }
+ return false.into();
+}
+
+unsafe extern "C" fn special_s2_set_kinetics(fighter: &mut L2CFighterCommon) {
+ let (motion, other_situation) = if fighter.global_table[SITUATION_KIND] == SITUATION_KIND_GROUND {
+ KineticModule::change_kinetic(fighter.module_accessor, *FIGHTER_KINETIC_TYPE_GROUND_STOP);
+ GroundModule::correct(fighter.module_accessor, app::GroundCorrectKind(*GROUND_CORRECT_KIND_GROUND_CLIFF_STOP_ATTACK));
+ (Hash40::new("special_s2"), *SITUATION_KIND_AIR)
+ }
+ else {
+ KineticModule::change_kinetic(fighter.module_accessor, *FIGHTER_KINETIC_TYPE_AIR_STOP);
+ GroundModule::correct(fighter.module_accessor, app::GroundCorrectKind(*GROUND_CORRECT_KIND_AIR));
+ (Hash40::new("special_air_s2"), *SITUATION_KIND_GROUND)
+ };
+ if fighter.is_flag(*FIGHTER_LINK_STATUS_BOOMERANG_CATCH_FLAG_FIRST) {
+ MotionModule::change_motion_inherit_frame(fighter.module_accessor, motion, -1.0, 1.0, 0.0, false, false);
+ }
+ else {
+ MotionModule::change_motion(fighter.module_accessor, motion, 0.0, 1.0, false, 0.0, false, false);
+ fighter.on_flag(*FIGHTER_LINK_STATUS_BOOMERANG_CATCH_FLAG_FIRST);
+ }
+ fighter.set_int(other_situation, *FIGHTER_LINK_STATUS_BOOMERANG_CATCH_INT_MTRANS);
+}
+
+unsafe extern "C" fn special_s2_end(fighter: &mut L2CFighterCommon) -> L2CValue {
+ ArticleModule::remove_exist(fighter.module_accessor, *FIGHTER_LINK_GENERATE_ARTICLE_BOOMERANG, app::ArticleOperationTarget(*ARTICLE_OPE_TARGET_ALL));
+ return false.into();
+}
+
+pub fn install(agent: &mut Agent) {
+ agent.status(Pre, *FIGHTER_STATUS_KIND_SPECIAL_S, special_s_pre);
+ agent.status(Init, *FIGHTER_STATUS_KIND_SPECIAL_S, special_s_init);
+ agent.status(Main, *FIGHTER_STATUS_KIND_SPECIAL_S, special_s_main);
+ agent.status(End, *FIGHTER_STATUS_KIND_SPECIAL_S, special_s_end);
+ agent.status(Exit, *FIGHTER_STATUS_KIND_SPECIAL_S, special_s_exit);
+
+ agent.status(Pre, *FIGHTER_LINK_STATUS_KIND_SPECIAL_S2, special_s2_pre);
+ agent.status(Main, *FIGHTER_LINK_STATUS_KIND_SPECIAL_S2, special_s2_main);
+ agent.status(End, *FIGHTER_LINK_STATUS_KIND_SPECIAL_S2, special_s2_end);
+}
diff --git a/fighters/murabito/src/opff.rs b/fighters/murabito/src/opff.rs
index 4d9a22effb..1311fa14e6 100644
--- a/fighters/murabito/src/opff.rs
+++ b/fighters/murabito/src/opff.rs
@@ -77,11 +77,17 @@ extern "Rust" {
fn ac_common(fighter: &mut smash::lua2cpp::L2CFighterCommon);
}
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_pocket_common(fighter: &mut smash::lua2cpp::L2CFighterCommon);
+}
+
pub extern "C" fn murabito_frame_wrapper(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
unsafe {
common::opff::fighter_common_opff(fighter);
murabito_frame(fighter);
ac_common(fighter);
+ fuse_pocket_common(fighter);
}
}
diff --git a/fighters/rosetta/src/opff.rs b/fighters/rosetta/src/opff.rs
index 547c3894dc..af3e7e8cee 100644
--- a/fighters/rosetta/src/opff.rs
+++ b/fighters/rosetta/src/opff.rs
@@ -17,6 +17,11 @@ extern "Rust" {
fn gimmick_flash(boma: &mut BattleObjectModuleAccessor);
}
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_rosetta_common(fighter: &mut L2CFighterCommon);
+}
+
use vars::common::instance::GIMMICK_TIMER;
use vars::rosetta::instance::*;
use vars::rosetta::status::*;
@@ -97,7 +102,8 @@ pub unsafe fn moveset(fighter: &mut L2CFighterCommon, boma: &mut BattleObjectMod
pub extern "C" fn rosetta_frame_wrapper(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
unsafe {
common::opff::fighter_common_opff(fighter);
- rosetta_frame(fighter)
+ rosetta_frame(fighter);
+ fuse_rosetta_common(fighter);
}
}
diff --git a/fighters/shizue/src/opff.rs b/fighters/shizue/src/opff.rs
index 4a3455bd38..c2d186b39c 100644
--- a/fighters/shizue/src/opff.rs
+++ b/fighters/shizue/src/opff.rs
@@ -135,11 +135,17 @@ extern "Rust" {
fn ac_common(fighter: &mut smash::lua2cpp::L2CFighterCommon);
}
+// symbol-based call for link's arrow fuse
+extern "Rust" {
+ fn fuse_pocket_common(fighter: &mut smash::lua2cpp::L2CFighterCommon);
+}
+
pub extern "C" fn shizue_frame_wrapper(fighter: &mut smash::lua2cpp::L2CFighterCommon) {
unsafe {
common::opff::fighter_common_opff(fighter);
shizue_frame(fighter);
ac_common(fighter);
+ fuse_pocket_common(fighter);
}
}
diff --git a/romfs/source/fighter/common/param/fighter_param.prcxml b/romfs/source/fighter/common/param/fighter_param.prcxml
index 1269fca8ba..8bc33ddab4 100644
--- a/romfs/source/fighter/common/param/fighter_param.prcxml
+++ b/romfs/source/fighter/common/param/fighter_param.prcxml
@@ -97,7 +97,7 @@
2.13
104
8
- 10
+ 8
7
14
18
diff --git a/romfs/source/fighter/link/motion/body/motion_patch.yaml b/romfs/source/fighter/link/motion/body/motion_patch.yaml
index f4b2613cc4..26b1def458 100644
--- a/romfs/source/fighter/link/motion/body/motion_patch.yaml
+++ b/romfs/source/fighter/link/motion/body/motion_patch.yaml
@@ -100,7 +100,7 @@ attack_13:
cancel_frame: 31
attack_lw3:
extra:
- cancel_frame: 33
+ cancel_frame: 30
special_hi:
extra:
cancel_frame: 80
@@ -143,6 +143,12 @@ attack_air_f:
special_n_start:
flags:
move: false
+special_n_end:
+ extra:
+ cancel_frame: 25
+special_air_n_end:
+ extra:
+ cancel_frame: 25
special_lw_blast:
extra:
cancel_frame: 40
@@ -162,4 +168,70 @@ step_pose_back:
extra:
cancel_frame: 6
cliff_wait:
- blend_frames: 2
\ No newline at end of file
+ blend_frames: 2
+special_lw_throw:
+ game_script: game_speciallwthrow
+ flags:
+ turn: false
+ loop: false
+ move: false
+ fix_trans: false
+ fix_rot: false
+ fix_scale: false
+ unk_40: false
+ unk_80: true
+ unk_100: false
+ unk_200: false
+ unk_400: false
+ unk_800: false
+ unk_1000: false
+ unk_2000: false
+ blend_frames: 0
+ animations:
+ - name: h01lightthrowf.nuanmb
+ unk: 0
+ scripts:
+ - effect_speciallwthrow
+ - sound_speciallwthrow
+ - expression_speciallwthrow
+ extra:
+ xlu_start: 0
+ xlu_end: 0
+ cancel_frame: 25
+ no_stop_intp: false
+special_air_lw_throw:
+ game_script: game_specialairlwthrow
+ flags:
+ turn: false
+ loop: false
+ move: false
+ fix_trans: false
+ fix_rot: false
+ fix_scale: false
+ unk_40: false
+ unk_80: true
+ unk_100: false
+ unk_200: false
+ unk_400: false
+ unk_800: false
+ unk_1000: false
+ unk_2000: false
+ blend_frames: 0
+ animations:
+ - name: h02lightthrowairf.nuanmb
+ unk: 0
+ scripts:
+ - expression_specialairlwthrow
+ - sound_specialairlwthrow
+ - effect_specialairlwthrow
+ extra:
+ xlu_start: 0
+ xlu_end: 0
+ cancel_frame: 25
+ no_stop_intp: false
+special_s2:
+ extra:
+ cancel_frame: 1
+special_airs2:
+ extra:
+ cancel_frame: 1
\ No newline at end of file
diff --git a/romfs/source/fighter/link/param/vl.prcxml b/romfs/source/fighter/link/param/vl.prcxml
index 7561132122..407d337946 100644
--- a/romfs/source/fighter/link/param/vl.prcxml
+++ b/romfs/source/fighter/link/param/vl.prcxml
@@ -8,6 +8,7 @@
+ 80
21
dummy
@@ -16,6 +17,7 @@
+ 60
2.3
11
17
diff --git a/romfs/source/item/common/param/param.prcxml b/romfs/source/item/common/param/param.prcxml
index 0d194da44e..45bb564f47 100644
--- a/romfs/source/item/common/param/param.prcxml
+++ b/romfs/source/item/common/param/param.prcxml
@@ -148,6 +148,9 @@
4.2
2.5
+ 0.1
+ 0.0
+ 0.25
item_shield_kind_hop
diff --git a/src/lua.rs b/src/lua.rs
index 1fb3a28cc8..bcb4b0609b 100644
--- a/src/lua.rs
+++ b/src/lua.rs
@@ -416,7 +416,7 @@ unsafe fn set_pane_text_values(ctx: &skyline::hooks::InlineCtx) {
#[skyline::hook(offset = 0x1d33b60, inline)]
unsafe fn set_parry_button_shield_text(ctx: &skyline::hooks::InlineCtx) {
- let sp = (ctx as *const _ as *const u8).add(0x300);
+ let sp = ctx.sp.x() as *const u8;
let ptr = *(sp.add(0xa8) as *const u64);
if ctx.registers[22].x() == 4 {
@@ -429,7 +429,7 @@ unsafe fn set_parry_button_shield_text(ctx: &skyline::hooks::InlineCtx) {
#[skyline::hook(offset = 0x1d33ca0, inline)]
unsafe fn set_parry_button_taunt_text(ctx: &skyline::hooks::InlineCtx) {
- let sp = (ctx as *const _ as *const u8).add(0x300);
+ let sp = ctx.sp.x() as *const u8;
let ptr = *(sp.add(0xa8) as *const u64);
if ctx.registers[22].x() == 4 {