1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-24 02:44:17 +00:00

Pikmin Save States and Skyline Smash update (#613)

* Add reusable print_fighter_info()

* rustfmt

* Clippy

* starting charge

* Working Pikmin Save States, motion not working, order can be messed up by loading during an aerial, if you save too quickly you lose pikmin in the state

* often failing boid read

* notes

* Prevent Pikmin from being cleaned up by the regular cleanup process

* migration progress

* skyline-smash branch, more pikmin work

* Using buff to spawn pikmin, character specific file for cleanliness

* failed reordering attempt

* more failed ordering

* almost kind of works but out of order

* failed all, need to try individual hold and status change

* hard reorder crash

* battleobject not boma

* comment clarification

* messing around with printing

* printing, about to try to find where autonomy is being set

* solution found, going to hook backshield to find where autonomy is being set to true

* found where to hook, will start hooking

* switched to charge, issues with inline hook

* need to only have autonomy ignored on save state load

* distance and status controls (doesn't work perfectly)

* Christmas Miracle

* Working w/ Cleanup

* training mode check, improved owner check

* debug feature print methods

* Pikmin debug assertions

* clippy print fix

* appease clippy
This commit is contained in:
GradualSyrup 2023-08-30 21:00:29 -05:00 committed by GitHub
parent 68a16f66fe
commit 9e94ef72a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 405 additions and 18 deletions

View file

@ -9,7 +9,7 @@ crate-type = ["cdylib"]
[dependencies]
skyline = { git = "https://github.com/ultimate-research/skyline-rs.git" }
skyline_smash = { git = "https://github.com/ultimate-research/skyline-smash.git", branch = "no-cache" }
skyline_smash = { git = "https://github.com/GradualSyrup/skyline-smash.git", branch = "training-modpack-updates" }
skyline-web = { git = "https://github.com/skyline-rs/skyline-web.git" }
bitflags = "1.2.1"
parking_lot = { version = "0.12.0", features = ["nightly"] }

View file

@ -1,4 +1,4 @@
use smash::app::{self, lua_bind::*};
use smash::app::{self, lua_bind::*, utility};
use smash::hash40;
use smash::lib::lua_const::*;
use smash::lua2cpp::L2CFighterCommon;
@ -34,7 +34,7 @@ pub fn is_training_mode() -> bool {
}
pub fn get_category(module_accessor: &app::BattleObjectModuleAccessor) -> i32 {
(module_accessor.info >> 28) as u8 as i32
(module_accessor.battle_object_id >> 28) as u8 as i32
}
pub fn is_emulator() -> bool {
@ -266,6 +266,88 @@ pub unsafe fn get_fighter_distance() -> f32 {
)
}
// Example Call:
// print_fighter_info(
// module_accessor,
// "DebugTest",
// true,
// false,
// true,
// true,
// vec![
// ("FIGHTER_INSTANCE_WORK_ID_INT_CLIFF_COUNT", FIGHTER_INSTANCE_WORK_ID_INT_CLIFF_COUNT),
// ],
// Vec::new(),
// vec![
// ("FIGHTER_STATUS_CLIFF_FLAG_TO_FALL", FIGHTER_STATUS_CLIFF_FLAG_TO_FALL),
// ],
// );
#[allow(dead_code)] // We won't be using this function in builds, but we don't want to be warned about it
#[allow(clippy::too_many_arguments)] // This function has so many arguments so it's easy to quickly fill them in when debugging with the analyzer
pub fn print_fighter_info(
module_accessor: &mut app::BattleObjectModuleAccessor,
title: &str,
player_only: bool,
cpu_only: bool,
print_fighter_kind: bool,
print_status: bool,
work_int_pairs: Vec<(&str, i32)>,
work_float_pairs: Vec<(&str, i32)>,
work_flag_pairs: Vec<(&str, i32)>,
) {
unsafe {
// Don't print for fighters we don't want to
let is_cpu = is_operation_cpu(module_accessor);
if (player_only && is_cpu) || (cpu_only && !is_cpu) {
return;
}
// Print Title
print!("{}: ", title);
// Print Fighter Kind:
if print_fighter_kind {
print!("FIGHTER_KIND: {}, ", utility::get_kind(module_accessor));
}
// Print Status:
if print_status {
print!(
"FIGHTER_STATUS: {}, ",
StatusModule::status_kind(module_accessor)
);
}
// Print Work Ints:
for work_int_pair in work_int_pairs {
print!(
"{}: {}, ",
work_int_pair.0,
WorkModule::get_int(module_accessor, work_int_pair.1)
);
}
// Print Work Floats:
for work_float_pair in work_float_pairs {
print!(
"{}: {}, ",
work_float_pair.0,
WorkModule::get_float(module_accessor, work_float_pair.1)
);
}
// Print Work Flags:
for work_flag_pair in work_flag_pairs {
print!(
"{}: {}, ",
work_flag_pair.0,
WorkModule::is_flag(module_accessor, work_flag_pair.1)
);
}
// End Line
println!("|");
}
}
// From https://github.com/chrispo-git/ult-s/blob/cc1c3060ed83f6d33f39964e84f9c32c07a17bae/src/controls/util.rs#L106
pub unsafe fn get_fighter_common_from_accessor(
module_accessor: &mut app::BattleObjectModuleAccessor,

View file

@ -1,6 +1,6 @@
#![allow(clippy::unnecessary_unwrap)]
use crate::common::dialog;
use crate::consts::*;
use crate::dialog;
use crate::logging::*;
use crate::MENU;
use anyhow::{anyhow, Result};

View file

@ -2,6 +2,7 @@ use smash::app::{self};
mod bowser;
pub mod items;
pub mod pikmin;
pub mod steve;
/**

View file

@ -0,0 +1,214 @@
use smash::app::{self, lua_bind::*, smashball::is_training_mode};
use smash::lib::lua_const::*;
#[repr(C)]
struct TroopsManager {
_x0: u64,
max_pikmin_count: usize, // always 3
current_pikmin_count: usize,
pikmin_objects: *mut *mut app::BattleObject,
pikmin: [*mut app::BattleObject; 3],
padding_0: u64,
padding_1: u64,
padding_2: u64,
padding_3: u64,
padding_4: u64,
padding_5: u64,
padding_6: u64,
padding_7: u64,
padding_8: u64,
held_pikmin_count: usize,
maybe_more_pikmin_objects: *mut *mut app::BattleObject,
held_pikmin: [*mut app::BattleObject; 3], // @ 0x90
}
#[repr(C)]
pub struct WeaponWorkModule {
vtable: u64,
owner: *mut app::BattleObjectModuleAccessor,
}
// Prevent Order Loss
static ACTIVATE_AUTONOMY_OFFSET: usize = 0x034b5cf0;
#[skyline::hook(offset = ACTIVATE_AUTONOMY_OFFSET)]
pub unsafe fn autonomy_handle(weapon: *mut app::Weapon, work_module: *mut WeaponWorkModule) {
if !is_training_mode() {
return original!()(weapon, work_module);
}
let pikmin_boma = (*work_module).owner;
// If the Pikmin is in a status where we want this behavior, execute the original process
let pikmin_status = StatusModule::status_kind(pikmin_boma);
let is_pikmin_thrown = (*WEAPON_PIKMIN_PIKMIN_STATUS_KIND_ATTACK_AIR
..=*WEAPON_PIKMIN_PIKMIN_STATUS_KIND_ATTACK_HI4_LANDING)
.contains(&pikmin_status);
if is_pikmin_thrown {
original!()(weapon, work_module)
}
}
pub fn init() {
skyline::install_hooks!(autonomy_handle,);
}
fn get_pikmin_prev(variation: i32) -> i32 {
if variation > 0 {
return variation - 1;
}
4
}
pub unsafe fn follow(module_accessor: &mut app::BattleObjectModuleAccessor) {
let troops_manager = WorkModule::get_int64(module_accessor, 0x100000C0) as *mut TroopsManager;
let following_count = (*troops_manager).current_pikmin_count;
let held_count = (*troops_manager).held_pikmin_count;
let mut pikmin_boid_following_vec = Vec::new();
let mut pikmin_boid_held_vec = Vec::new();
// First, we get the order of held pikmin, since they'll be in front if we save state during a move or grab
for held_index in 0..held_count {
let held_boid = (*((*troops_manager).held_pikmin[held_index])).battle_object_id;
pikmin_boid_held_vec.push(held_boid);
}
// Next, we get the order of the following pikmin
for following_index in 0..following_count {
let following_boid = (*((*troops_manager).pikmin[following_index])).battle_object_id;
pikmin_boid_following_vec.push(following_boid);
}
for pikmin_boid in pikmin_boid_following_vec {
let pikmin_boma = app::sv_battle_object::module_accessor(pikmin_boid);
StatusModule::change_status_request(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_STATUS_KIND_AIR_FOLLOW,
false,
);
}
}
pub unsafe fn spawn_pikmin(module_accessor: &mut app::BattleObjectModuleAccessor, variation: i32) {
WorkModule::set_int(
module_accessor,
get_pikmin_prev(variation),
*FIGHTER_PIKMIN_INSTANCE_WORK_INT_PRE_PIKMIN_VARIATION,
);
WorkModule::set_int(
module_accessor,
get_pikmin_prev(get_pikmin_prev(variation)),
*FIGHTER_PIKMIN_INSTANCE_WORK_INT_BEFORE_PRE_PIKMIN_VARIATION,
);
ArticleModule::generate_article(
module_accessor as *mut app::BattleObjectModuleAccessor,
0,
false,
-1,
);
}
pub unsafe fn get_current_pikmin(
module_accessor: &mut app::BattleObjectModuleAccessor,
) -> [Option<i32>; 3] {
let troops_manager = WorkModule::get_int64(module_accessor, 0x100000C0) as *mut TroopsManager;
let following_count = (*troops_manager).current_pikmin_count;
let held_count = (*troops_manager).held_pikmin_count;
let mut pikmin_boid_vec = Vec::new();
//let mut pikmin_boma_vec: [*mut app::BattleObjectModuleAccessor; 3] = [0 as *mut app::BattleObjectModuleAccessor; 3];
let mut ordered_pikmin_variation: [Option<i32>; 3] = [None; 3];
// First, we get the order of held pikmin, since they'll be in front if we save state during a move or grab
for held_index in 0..held_count {
let held_work_var = match held_index {
0 => *FIGHTER_PIKMIN_INSTANCE_WORK_INT_PIKMIN_HOLD_PIKMIN_OBJECT_ID_0,
1 => *FIGHTER_PIKMIN_INSTANCE_WORK_INT_PIKMIN_HOLD_PIKMIN_OBJECT_ID_1,
2 => *FIGHTER_PIKMIN_INSTANCE_WORK_INT_PIKMIN_HOLD_PIKMIN_OBJECT_ID_2,
_ => {
panic!("Pikmin Held Out of Bounds!");
}
};
let held_boid = WorkModule::get_int(module_accessor, held_work_var) as u32;
println!(", boid: {}", held_boid);
pikmin_boid_vec.push(held_boid);
}
// Next, we get the order of the following pikmin
for following_index in 0..following_count {
let following_boid = (*((*troops_manager).pikmin[following_index])).battle_object_id;
pikmin_boid_vec.push(following_boid);
}
// Now, we have all pikmin boids, and want to get their bomas (if they exist) so we can check their color
for (idx, pikmin_boid) in pikmin_boid_vec.iter().enumerate() {
if *pikmin_boid != *BATTLE_OBJECT_ID_INVALID as u32
&& app::sv_battle_object::is_active(*pikmin_boid)
{
let pikmin_boma = app::sv_battle_object::module_accessor(*pikmin_boid);
let pikmin_variation = WorkModule::get_int(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_INT_VARIATION,
);
ordered_pikmin_variation[idx] = Some(pikmin_variation);
}
}
ordered_pikmin_variation
}
#[cfg(debug_assertions)]
#[allow(dead_code)]
pub unsafe fn pretty_print(module_accessor: &mut app::BattleObjectModuleAccessor) {
let troops_manager = WorkModule::get_int64(module_accessor, 0x100000C0) as *mut TroopsManager;
let following_count = (*troops_manager).current_pikmin_count;
let held_count = (*troops_manager).held_pikmin_count;
let mut pikmin_following_boid_vec = Vec::new();
let mut pikmin_held_boid_vec = Vec::new();
// First, we get the order of held pikmin, since they'll be in front if we save state during a move or grab
for held_index in 0..held_count {
let held_boid = (*((*troops_manager).held_pikmin[held_index])).battle_object_id;
pikmin_held_boid_vec.push(held_boid);
print(held_boid, true);
}
// Next, we get the order of the following pikmin
for following_index in 0..following_count {
let following_boid = (*((*troops_manager).pikmin[following_index])).battle_object_id;
pikmin_following_boid_vec.push(following_boid);
print(following_boid, false);
}
println!("----------------------------------------")
}
#[cfg(debug_assertions)]
#[allow(dead_code)]
unsafe fn print(boid: u32, held: bool) {
if boid != *BATTLE_OBJECT_ID_INVALID as u32 && app::sv_battle_object::is_active(boid) {
let pikmin_boma = app::sv_battle_object::module_accessor(boid);
let pikmin_variation = WorkModule::get_int(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_INT_VARIATION,
);
let pikmin_status = StatusModule::status_kind(pikmin_boma);
let pikmin_autonomy: bool = WorkModule::is_flag(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_FLAG_AUTONOMY,
);
// solution:
//WorkModule::off_flag(pikmin_boma, *WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_FLAG_AUTONOMY);
let owner_cond = WorkModule::get_int(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_INT_OWNER_CONDITION_CURRENT,
);
let owner_cond_follow = WorkModule::get_int(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_INT_OWNER_CONDITION_FOLLOW,
);
let owner_opt_flag_follow = WorkModule::get_int(
pikmin_boma,
*WEAPON_PIKMIN_PIKMIN_INSTANCE_WORK_ID_INT_OWNER_OPTION_FLAG_FOLLOW,
);
println!("Color: {}, Status: {}, Held {}, Autonomy: {}, owner_cond: {}, owner_cond_follow: {}, owner_opt_flag_follow: {}",
pikmin_variation, pikmin_status, held, pikmin_autonomy, owner_cond, owner_cond_follow, owner_opt_flag_follow
);
}
}

View file

@ -1,3 +1,4 @@
use crate::training::character_specific::pikmin;
use serde::{Deserialize, Serialize};
use smash::app::{self, lua_bind::*, ArticleOperationTarget, FighterFacial, FighterUtil};
use smash::lib::lua_const::*;
@ -7,6 +8,7 @@ use smash::phx::{Hash40, Vector3f};
pub struct ChargeState {
pub int_x: Option<i32>,
pub int_y: Option<i32>,
pub int_z: Option<i32>,
pub float_x: Option<f32>,
pub float_y: Option<f32>,
pub float_z: Option<f32>,
@ -39,6 +41,18 @@ impl ChargeState {
self
}
fn set_pikmin(
mut self,
pikmin_1: Option<i32>,
pikmin_2: Option<i32>,
pikmin_3: Option<i32>,
) -> Self {
self.int_x = pikmin_1;
self.int_y = pikmin_2;
self.int_z = pikmin_3;
self
}
fn has_charge(mut self, has_charge: bool) -> Self {
self.has_charge = Some(has_charge);
self
@ -115,7 +129,10 @@ pub unsafe fn get_charge(
}
// Wario Waft
else if fighter_kind == FIGHTER_KIND_WARIO {
let my_charge = WorkModule::get_int(module_accessor, 0x100000BF); // FIGHTER_WARIO_INSTANCE_WORK_ID_INT_GASS_COUNT
let my_charge = WorkModule::get_int(
module_accessor,
*FIGHTER_WARIO_INSTANCE_WORK_ID_INT_GASS_COUNT,
);
charge_state.int_x(my_charge)
}
// Squirtle Water Gun
@ -126,6 +143,11 @@ pub unsafe fn get_charge(
);
charge_state.int_x(my_charge)
}
// Olimar Pikmin
else if fighter_kind == FIGHTER_KIND_PIKMIN {
let pikmin_array = pikmin::get_current_pikmin(module_accessor);
return charge_state.set_pikmin(pikmin_array[0], pikmin_array[1], pikmin_array[2]);
}
// Lucario Aura Sphere
else if fighter_kind == FIGHTER_KIND_LUCARIO {
let my_charge = WorkModule::get_int(
@ -174,7 +196,10 @@ pub unsafe fn get_charge(
}
// Pac-Man Bonus Fruit
else if fighter_kind == FIGHTER_KIND_PACMAN {
let my_charge = WorkModule::get_int(module_accessor, 0x100000C1); // FIGHTER_PACMAN_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE_RANK
let my_charge = WorkModule::get_int(
module_accessor,
*FIGHTER_PACMAN_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE_RANK,
);
let fruit_have = WorkModule::is_flag(
module_accessor,
*FIGHTER_PACMAN_INSTANCE_WORK_ID_FLAG_SPECIAL_N_PULL_THROW,
@ -501,7 +526,11 @@ pub unsafe fn handle_charge(
// Wario Waft - 0 to 6000
else if fighter_kind == FIGHTER_KIND_WARIO {
charge.int_x.map(|waft_count| {
WorkModule::set_int(module_accessor, waft_count, 0x100000BF); // FIGHTER_WARIO_INSTANCE_WORK_ID_INT_GASS_COUNT
WorkModule::set_int(
module_accessor,
waft_count,
*FIGHTER_WARIO_INSTANCE_WORK_ID_INT_GASS_COUNT,
);
});
}
// Squirtle Water Gun - 0 to 45
@ -517,6 +546,36 @@ pub unsafe fn handle_charge(
}
});
}
// Olimar Pikmin - 0 to 4
else if fighter_kind == FIGHTER_KIND_PIKMIN {
ArticleModule::remove_exist(
module_accessor,
*FIGHTER_PIKMIN_GENERATE_ARTICLE_PIKMIN,
app::ArticleOperationTarget(*ARTICLE_OPE_TARGET_ALL),
);
if ArticleModule::get_active_num(module_accessor, *FIGHTER_PIKMIN_GENERATE_ARTICLE_PIKMIN)
== 0
{
charge.int_x.map(|pikmin_1| {
pikmin::spawn_pikmin(module_accessor, pikmin_1);
});
}
if ArticleModule::get_active_num(module_accessor, *FIGHTER_PIKMIN_GENERATE_ARTICLE_PIKMIN)
== 1
{
charge.int_y.map(|pikmin_2| {
pikmin::spawn_pikmin(module_accessor, pikmin_2);
});
}
if ArticleModule::get_active_num(module_accessor, *FIGHTER_PIKMIN_GENERATE_ARTICLE_PIKMIN)
== 2
{
charge.int_z.map(|pikmin_3| {
pikmin::spawn_pikmin(module_accessor, pikmin_3);
});
}
pikmin::follow(module_accessor);
}
// Lucario Aura Sphere - 0 to 90, Boolean
else if fighter_kind == FIGHTER_KIND_LUCARIO {
charge.int_x.map(|charge_frame| {
@ -641,7 +700,11 @@ pub unsafe fn handle_charge(
else if fighter_kind == FIGHTER_KIND_PACMAN {
let mut has_key = false;
charge.int_x.map(|charge_rank| {
WorkModule::set_int(module_accessor, charge_rank, 0x100000C1); // FIGHTER_PACMAN_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE_RANK
WorkModule::set_int(
module_accessor,
charge_rank,
*FIGHTER_PACMAN_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE_RANK,
);
if charge_rank == 12 {
EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0);
@ -799,7 +862,10 @@ pub unsafe fn handle_charge(
// Hero (Ka)frizz(le) - 0 to 81
else if fighter_kind == FIGHTER_KIND_BRAVE {
EffectModule::remove_common(module_accessor, Hash40::new("charge_max"));
WorkModule::off_flag(module_accessor, 0x200000E8); // FIGHTER_BRAVE_INSTANCE_WORK_ID_FLAG_SPECIAL_N_MAX_EFFECT
WorkModule::off_flag(
module_accessor,
*FIGHTER_BRAVE_INSTANCE_WORK_ID_FLAG_SPECIAL_N_MAX_EFFECT,
);
charge.int_x.map(|frizz_charge| {
WorkModule::set_int(
module_accessor,

View file

@ -7,7 +7,7 @@ use crate::common::{
use crate::hitbox_visualizer;
use crate::input::*;
use crate::logging::*;
use crate::training::character_specific::items;
use crate::training::character_specific::{items, pikmin};
use skyline::hooks::{getRegionAddress, InlineCtx, Region};
use skyline::nn::ro::LookupSymbol;
use smash::app::{self, enSEType, lua_bind::*, utility};
@ -680,6 +680,17 @@ pub unsafe fn handle_reused_ui(
original!()(fighter_data, param_2)
}
static ARTICLE_GET_INT_OFFSET: usize = 0x3d5920;
#[skyline::hook(offset = ARTICLE_GET_INT_OFFSET)]
pub unsafe fn handle_article_get_int(
article_module: *mut app::BattleObjectModuleAccessor, // *mut ArticleModule
generate_article: i32,
address: i32,
) -> i32 {
original!()(article_module, generate_article, address)
}
// Instruction run on the completion of the CPU Control function
static OPCF_OFFSET: usize = 0x06b7fdc;
@ -735,6 +746,15 @@ unsafe fn handle_final_input_mapping(
input_record::handle_final_input_mapping(player_idx, out);
}
static BOMA_OFFSET: usize = 0x15cf1b0;
#[skyline::hook(offset = BOMA_OFFSET)]
pub unsafe fn handle_get_module_accessor(
battle_object_id: u32,
) -> *mut app::BattleObjectModuleAccessor {
original!()(battle_object_id)
}
pub fn training_mods() {
info!("Applying training mods.");
@ -813,10 +833,14 @@ pub fn training_mods() {
// Notifications
handle_once_per_cpu_frame,
// Input
handle_final_input_mapping
handle_final_input_mapping,
// Charge
handle_article_get_int,
handle_get_module_accessor,
);
items::init();
input_record::init();
ui::init();
pikmin::init();
}

View file

@ -34,7 +34,7 @@ use crate::training::reset;
use crate::training::ui::notifications;
use crate::{is_ptrainer, ITEM_MANAGER_ADDR};
// Don't remove Mii hats, or Luma, or crafting table
// Don't remove Mii hats, Pikmin, Luma, or crafting table
const ARTICLE_ALLOWLIST: [(LuaConst, LuaConst); 8] = [
(
FIGHTER_KIND_MIIFIGHTER,
@ -98,6 +98,7 @@ macro_rules! default_save_state {
charge: ChargeState {
int_x: None,
int_y: None,
int_z: None,
float_x: None,
float_y: None,
float_z: None,
@ -308,12 +309,11 @@ unsafe fn on_ptrainer_death(module_accessor: &mut app::BattleObjectModuleAccesso
ptrainer_module_accessor,
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
) {
let ptrainer_masterball: u64 = ArticleModule::get_article(
let ptrainer_masterball: *mut app::Article = ArticleModule::get_article(
ptrainer_module_accessor,
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
);
let ptrainer_masterball_id =
Article::get_battle_object_id(ptrainer_masterball as *mut app::Article);
let ptrainer_masterball_id = Article::get_battle_object_id(ptrainer_masterball);
let ptrainer_masterball_module_accessor =
&mut *app::sv_battle_object::module_accessor(ptrainer_masterball_id as u32);
MotionModule::set_rate(ptrainer_masterball_module_accessor, 1000.0);
@ -339,8 +339,9 @@ unsafe fn on_death(fighter_kind: i32, module_accessor: &mut app::BattleObjectMod
})
.for_each(|article_idx| {
if ArticleModule::is_exist(module_accessor, article_idx) {
let article: u64 = ArticleModule::get_article(module_accessor, article_idx);
let article_object_id = Article::get_battle_object_id(article as *mut app::Article);
let article: *mut app::Article =
ArticleModule::get_article(module_accessor, article_idx);
let article_object_id = Article::get_battle_object_id(article);
ArticleModule::remove_exist_object_id(module_accessor, article_object_id as u32);
}
});

View file

@ -496,7 +496,6 @@ bitflags! {
const U_TILT = 0x0010_0000;
const D_TILT = 0x0020_0000;
const GRAB = 0x0040_0000;
// TODO: Make work
const DASH = 0x0080_0000;
const DASH_ATTACK = 0x0100_0000;
const PLAYBACK_1 = 0x0200_0000;