diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a40435c7fa..8e081bdb4d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { "recommendations": [ "ms-vscode.cpptools", - "nanaian.vscode-star-rod", + "nanaian.papermario", "notskm.clang-tidy", "EditorConfig.EditorConfig", ], diff --git a/include/common_structs.h b/include/common_structs.h index f53b71e623..c58871b240 100644 --- a/include/common_structs.h +++ b/include/common_structs.h @@ -21,6 +21,12 @@ typedef struct Vec2bu { /* 0x01 */ u8 y; } Vec2bu; // size = 0x02 +typedef struct Vec3b { + /* 0x00 */ s8 x; + /* 0x01 */ s8 y; + /* 0x02 */ s8 z; +} Vec3b; // size = 0x03 + typedef struct Vec3f { /* 0x00 */ f32 x; /* 0x04 */ f32 y; @@ -658,31 +664,6 @@ typedef struct TextureHeader { /* 0x2F */ u8 filtering; } TextureHeader; // size = 0x30 -typedef struct StaticActorData { - /* 0x00 */ s32 flags; - /* 0x04 */ char unk_04; - /* 0x05 */ u8 type; - /* 0x06 */ u8 level; - /* 0x07 */ u8 maxHP; - /* 0x08 */ s16 partCount; - /* 0x0A */ char unk_0A[2]; - /* 0x0C */ struct StaticActorPart** partsData; - /* 0x10 */ UNK_PTR script; - /* 0x14 */ UNK_PTR statusTable; - /* 0x18 */ u8 escapeChance; - /* 0x19 */ u8 airLiftChance; - /* 0x1A */ u8 spookChance; - /* 0x1B */ u8 baseStatusChance; - /* 0x1C */ u8 upAndAwayChance; - /* 0x1D */ u8 spinSmashReq; - /* 0x1E */ u8 powerBounceChance; - /* 0x1F */ u8 coinReward; - /* 0x20 */ u8 size[2]; - /* 0x22 */ Vec2b hpBarOffset; - /* 0x24 */ u8 statusIconOffset[2]; - /* 0x26 */ u8 statusMessageOffset[2]; -} StaticActorData; // size = 0x28 - typedef struct StaticMove { /* 0x00 */ s32 moveNameID; /* 0x04 */ s32 flags; @@ -1042,7 +1023,7 @@ typedef struct ActorPartMovement { typedef struct ActorPart { /* 0x00 */ s32 flags; /* 0x04 */ s32 targetFlags; /* initialized to 0 */ - /* 0x08 */ struct StaticActorPart* staticData; + /* 0x08 */ struct ActorPartDesc* staticData; /* 0x0C */ struct ActorPart* nextPart; /* 0x10 */ struct ActorPartMovement* movement; /* 0x14 */ Vec3s partOffset; @@ -1236,7 +1217,7 @@ typedef struct ActorFlyPos { typedef struct Actor { /* 0x000 */ s32 flags; /* 0x004 */ char unk_04[4]; - /* 0x008 */ struct StaticActorData* staticActorData; + /* 0x008 */ struct ActorDesc* staticActorData; /* 0x00C */ ActorMovePos movePos; /* 0x030 */ char unk_30[24]; /* 0x048 */ f32 jumpAccel; @@ -1358,19 +1339,6 @@ typedef struct Actor { /* 0x440 */ struct MenuIcon* ptrDefuffIcon; } Actor; // size = 0x444 -typedef struct StaticActorPart { - /* 0x00 */ s32 flags; - /* 0x04 */ s8 index; - /* 0x05 */ u8 posOffset[3]; - /* 0x08 */ u8 targetOffset[2]; - /* 0x0A */ s16 opacity; - /* 0x0C */ u32* idleAnimations; - /* 0x10 */ u32* defenseTable; - /* 0x14 */ s32 eventFlags; - /* 0x18 */ s32 flags3; - /* 0x1C */ char unk_1C[8]; -} StaticActorPart; // size = 0x24 - typedef struct TileDescriptor { /* 0x00 */ s8 name[32]; /* 0x20 */ s16 auxW; diff --git a/src/battle/actor/goomba.c b/src/battle/actor/goomba.c new file mode 100644 index 0000000000..1ed7f9d1e1 --- /dev/null +++ b/src/battle/actor/goomba.c @@ -0,0 +1,477 @@ +#include "common.h" +#include "battle/battle.h" +#include "script_api/battle.h" +#include "sprite/npc/goomba.h" +#include "goomba.h" + +ApiStatus func_8021818C_430B2C(ScriptInstance* script, s32 isInitialCall); +s32 goomba_anims_running[]; +s32 goomba_anims[]; +s32 goomba_defense_table[]; +s32 goomba_status_table[]; +s32 goomba_defense_table[]; +ActorPartDesc goomba_parts[]; +Script goomba_init; +Script goomba_turn; +Script goomba_idle; +Script goomba_dispatch; + +s32 goomba_defense_table[] = { + Element_NORMAL, 0, + + Element_END, +}; + +s32 goomba_status_table[] = { + Debuff_NORMAL, 0, + Debuff_DEFAULT, 0, + Debuff_SLEEP, 100, + Debuff_POISON, 100, + Debuff_FROZEN, 100, + Debuff_DIZZY, 100, + Debuff_FEAR, 100, + Debuff_STATIC, 100, + Debuff_PARALYZE, 100, + Debuff_SHRINK, 100, + Debuff_STOP, 100, + + Debuff_DEFAULT_TURN_MOD, 0, + Debuff_SLEEP_TURN_MOD, 0, + Debuff_POISON_TURN_MOD, 0, + Debuff_FROZEN_TURN_MOD, 0, + Debuff_DIZZY_TURN_MOD, 0, + Debuff_FEAR_TURN_MOD, 0, + Debuff_STATIC_TURN_MOD, 0, + Debuff_PARALYZE_TURN_MOD, 0, + Debuff_SHRINK_TURN_MOD, 0, + Debuff_STOP_TURN_MOD, 0, + + Debuff_END, +}; + +ActorPartDesc goomba_parts[] = { + { + .flags = 0x00800000, + .index = 1, + .posOffset = { 0, 0, 0 }, + .targetOffset = { 0, 20 }, + .opacity = 0xFF, + .idleAnimations = goomba_anims, + .defenseTable = goomba_defense_table, + .eventFlags = 0, + .elementImmunityFlags = 0, + 0x00, 0xF6, + }, +}; + +ActorDesc goomba = { + .flags = 0, + .type = 7, + .level = 5, + .maxHP = 2, + .partCount = 1, + .partsData = &goomba_parts, + .script = &goomba_init, + .statusTable = &goomba_status_table, + .escapeChance = 90, + .airLiftChance = 100, + .spookChance = 90, + .baseStatusChance = 100, + .upAndAwayChance = 95, + .spinSmashReq = 0, + .powerBounceChance = 100, + .coinReward = 1, + .size = { 24, 24 }, + .hpBarOffset = { 0, 0 }, + .statusIconOffset = { -10, 20 }, + .statusMessageOffset = { 10, 20 }, +}; + +s32 goomba_anims[] = { + Debuff_NORMAL, NPC_ANIM(goomba, normal, idle), + Debuff_STONE, NPC_ANIM(goomba, normal, still), + Debuff_SLEEP, NPC_ANIM(goomba, normal, asleep), + Debuff_POISON, NPC_ANIM(goomba, normal, idle), + Debuff_STOP, NPC_ANIM(goomba, normal, still), + Debuff_STATIC, NPC_ANIM(goomba, normal, idle), + Debuff_PARALYZE, NPC_ANIM(goomba, normal, still), + Debuff_DIZZY, NPC_ANIM(goomba, normal, dizzy), + Debuff_FEAR, NPC_ANIM(goomba, normal, dizzy), + + Debuff_END, +}; + +s32 goomba_anims_running[] = { + Debuff_NORMAL, NPC_ANIM(goomba, normal, run), + Debuff_STONE, NPC_ANIM(goomba, normal, still), + Debuff_SLEEP, NPC_ANIM(goomba, normal, asleep), + Debuff_POISON, NPC_ANIM(goomba, normal, idle), + Debuff_STOP, NPC_ANIM(goomba, normal, still), + Debuff_STATIC, NPC_ANIM(goomba, normal, run), + Debuff_PARALYZE, NPC_ANIM(goomba, normal, still), + Debuff_DIZZY, NPC_ANIM(goomba, normal, dizzy), + Debuff_FEAR, NPC_ANIM(goomba, normal, dizzy), + + Debuff_END, +}; + +Script goomba_init = SCRIPT({ + BindTakeTurn(ActorID_SELF, goomba_turn); + BindIdle(ActorID_SELF, goomba_idle); + BindHandleEvent(ActorID_SELF, goomba_dispatch); +}); + +Script goomba_idle = SCRIPT({ +10: + RandInt(80, SI_VAR(0)); + SI_VAR(0) += 80; + loop SI_VAR(0) { +0: + GetStatusFlags(ActorID_SELF, SI_VAR(1)); + if (SI_VAR(1) & 0x35D000) { + sleep 1; + goto 0; + } + sleep 1; + } + GetActorPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SI_VAR(0) += 5; + SetActorIdleSpeed(ActorID_SELF, 1.0); + SetIdleAnimations(ActorID_SELF, 1, goomba_anims_running); + SetIdleGoal(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + IdleRunToGoal(ActorID_SELF, 0); + SetIdleAnimations(ActorID_SELF, 1, goomba_anims); + loop 20 { +1: + GetStatusFlags(ActorID_SELF, SI_VAR(1)); + if (SI_VAR(1) & 0x35D000) { + sleep 1; + goto 1; + } + sleep 1; + } + GetActorPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SI_VAR(0) -= 5; + SetActorIdleSpeed(ActorID_SELF, 1.0); + SetIdleAnimations(ActorID_SELF, 1, goomba_anims_running); + SetIdleGoal(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + IdleRunToGoal(ActorID_SELF, 0); + SetIdleAnimations(ActorID_SELF, 1, goomba_anims); + loop 80 { +2: + GetStatusFlags(ActorID_SELF, SI_VAR(1)); + if (SI_VAR(1) & 0x35D000) { + sleep 1; + goto 2; + } + sleep 1; + } + goto 10; +}); + +Script goomba_dispatch = SCRIPT({ + UseIdleAnimation(ActorID_SELF, 0); + EnableIdleScript(ActorID_SELF, 0); + SetActorScale(ActorID_SELF, 1.0, 1.0, 1.0); + GetLastEvent(ActorID_SELF, SI_VAR(0)); + match SI_VAR(0) { + Event_HIT_COMBO, Event_HIT { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, pain); + await DoNormalHit; + } + == Event_BURN_HIT { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, burn_pain); + SI_VAR(2) = c NPC_ANIM(goomba, normal, burn_dead); + await DoBurnHit; + } + == Event_BURN_DEATH { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, burn_pain); + SI_VAR(2) = c NPC_ANIM(goomba, normal, burn_dead); + await DoBurnHit; + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, burn_dead); + await DoDeath; + return; + } + == Event_SPIN_SMASH_HIT { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, pain); + await DoSpinSmashHit; + } + == Event_SPIN_SMASH_DEATH { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, pain); + await DoSpinSmashHit; + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, dead); + await DoDeath; + return; + } + == Event_SHOCK_HIT { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, electrocute); + await DoShockHit; + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, pain); + await DoJumpBack; + JumpToGoal(ActorID_SELF, 5, 0, 1, 0); + SetAnimationRate(ActorID_SELF, 1, 2.0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, dizzy)); + SetGoalToHome(ActorID_SELF); + SetActorSpeed(ActorID_SELF, 8.0); + RunToGoal(ActorID_SELF, 0, 0); + SetAnimationRate(ActorID_SELF, 1, 1.0); + sleep 5; + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle)); + SetActorJumpGravity(ActorID_SELF, 1.6); + JumpToGoal(ActorID_SELF, 5, 0, 1, 0); + } + == Event_SHOCK_DEATH { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, electrocute); + await DoShockHit; + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, dead); + await DoDeath; + return; + } + == Event_STAR_BEAM, 23, Event_IMMUNE, Event_AIR_LIFT_FAILED { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, idle); + await DoImmune; + } + == Event_DEATH { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, pain); + await DoNormalHit; + sleep 10; + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, dead); + await DoDeath; + return; + } + == Event_END_FIRST_STRIKE { + SetAnimationRate(ActorID_SELF, 1, 2.0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, run)); + SetGoalToHome(ActorID_SELF); + SetActorSpeed(ActorID_SELF, 4.0); + RunToGoal(ActorID_SELF, 0, 0); + SetAnimationRate(ActorID_SELF, 1, 1.0); + HPBarToHome(ActorID_SELF); + } + == Event_RECOVER_STATUS { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, idle); + await DoRecover; + } + == Event_SCARE_AWAY { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, run); + SI_VAR(2) = c NPC_ANIM(goomba, normal, pain); + await DoScareAway; + return; + } + == Event_BEGIN_AIR_LIFT { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, run); + await DoAirLift; + } + == Event_BLOW_AWAY { + SI_VAR(0) = c 1; + SI_VAR(1) = c NPC_ANIM(goomba, normal, pain); + await DoBlowAway; + return; + } else { + } + } + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle)); + EnableIdleScript(ActorID_SELF, 1); + UseIdleAnimation(ActorID_SELF, 1); +}); + +f32 float_table_for_func_80218000[] = { + 0.000000f, 0.017452f, 0.034899f, 0.052336f, 0.069756f, 0.087156f, 0.104528f, 0.121869f, + 0.139173f, 0.156434f, 0.173648f, 0.190809f, 0.207912f, 0.224951f, 0.241922f, 0.258819f, + 0.275637f, 0.292372f, 0.309017f, 0.325568f, 0.342020f, 0.358368f, 0.374607f, 0.390731f, + 0.406737f, 0.422618f, 0.438371f, 0.453990f, 0.469472f, 0.484810f, 0.500000f, 0.515038f, + 0.529919f, 0.544639f, 0.559193f, 0.573576f, 0.587785f, 0.601815f, 0.615661f, 0.629320f, + 0.642788f, 0.656059f, 0.669131f, 0.681998f, 0.694658f, 0.707107f, 0.719340f, 0.731354f, + 0.743145f, 0.754710f, 0.766044f, 0.777146f, 0.788011f, 0.798636f, 0.809017f, 0.819152f, + 0.829038f, 0.838671f, 0.848048f, 0.857167f, 0.866025f, 0.874620f, 0.882948f, 0.891007f, + 0.898794f, 0.906308f, 0.913545f, 0.920505f, 0.927184f, 0.933580f, 0.939693f, 0.945519f, + 0.951057f, 0.956305f, 0.961262f, 0.965926f, 0.970296f, 0.974370f, 0.978148f, 0.981627f, + 0.984808f, 0.987688f, 0.990268f, 0.992546f, 0.994522f, 0.996195f, 0.997564f, 0.998630f, + 0.999391f, 0.999848f, 1.000000f, +}; + +Script goomba_turn = SCRIPT({ + UseIdleAnimation(ActorID_SELF, 0); + EnableIdleScript(ActorID_SELF, 0); + SetTargetActor(ActorID_SELF, 0); + UseCamPreset(63); + BattleCamTargetActor(ActorID_SELF); + 0x8024ECF8(-1, 1, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, run)); + SetGoalToTarget(ActorID_SELF); + AddGoalPos(ActorID_SELF, 50, 0, 0); + SetActorSpeed(ActorID_SELF, 6.0); + RunToGoal(ActorID_SELF, 0, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle)); + SetActorDispOffset(ActorID_SELF, 0, -1, 0); + sleep 1; + SetActorDispOffset(ActorID_SELF, 0, -2, 0); + sleep 5; + SetActorDispOffset(ActorID_SELF, 0, 0, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk)); + EnemyTestTarget(ActorID_SELF, SI_VAR(0), 0, 0, 1, 16); + match SI_VAR(0) { + 6, 5 { + SI_VAR(10) = SI_VAR(0); + SetGoalToTarget(ActorID_SELF); + GetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SI_VAR(0) -= 10; + SI_VAR(1) = 10; + SI_VAR(2) += 3; + SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SetActorJumpGravity(ActorID_SELF, 1.2); + spawn { + GetActorPos(ActorID_SELF, SI_VAR(1), SI_VAR(2), SI_VAR(0)); + SI_VAR(0) = 0; + loop 16 { + GetActorPos(ActorID_SELF, SI_VAR(4), SI_VAR(5), SI_VAR(6)); + func_8021818C_430B2C(SI_VAR(1), SI_VAR(2), SI_VAR(4), SI_VAR(5), SI_VAR(0)); + SetActorRotation(ActorID_SELF, 0, 0, SI_VAR(0)); + SI_VAR(1) = SI_VAR(4); + SI_VAR(2) = SI_VAR(5); + SI_VAR(3) = SI_VAR(6); + sleep 1; + } + } + spawn { + sleep 6; + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk)); + } + JumpToGoal(ActorID_SELF, 16, 0, 1, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, asleep)); + SetActorScale(ActorID_SELF, 1.1, 0.8, 1.0); + SetActorDispOffset(ActorID_SELF, 0, 5, 0); + sleep 1; + SetActorScale(ActorID_SELF, 1.3, 0.5, 1.0); + SetActorDispOffset(ActorID_SELF, 0, -2, 0); + sleep 1; + SetActorScale(ActorID_SELF, 1.0, 1.0, 1.0); + SetActorDispOffset(ActorID_SELF, 0, 7, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, pain)); + sleep 5; + if (SI_VAR(10) == 5) { + EnemyTestTarget(ActorID_SELF, SI_VAR(0), 0x80000000, 0, 0, 0); + } + sleep 5; + SetActorDispOffset(ActorID_SELF, 0, 0, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk)); + SetGoalToTarget(ActorID_SELF); + GetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SI_VAR(0) += 20; + SI_VAR(1) = 0; + SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SetActorJumpGravity(ActorID_SELF, 2.0); + spawn { + sleep 4; + SI_VAR(0) = 180; + loop 4 { + SI_VAR(0) -= 45; + SetActorRotation(ActorID_SELF, 0, 0, SI_VAR(0)); + sleep 1; + } + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk)); + } + JumpToGoal(ActorID_SELF, 15, 0, 1, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, dizzy)); + sleep 5; + UseCamPreset(2); + YieldTurn(); + SetActorYaw(ActorID_SELF, 180); + AddActorDecoration(ActorID_SELF, 1, 0, 2); + SetAnimationRate(ActorID_SELF, 1, 2.0); + SetGoalToHome(ActorID_SELF); + SetActorSpeed(ActorID_SELF, 8.0); + RunToGoal(ActorID_SELF, 0, 0); + SetAnimationRate(ActorID_SELF, 1, 1.0); + SetActorYaw(ActorID_SELF, 0); + sleep 5; + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle)); + SetActorJumpGravity(ActorID_SELF, 1.6); + JumpToGoal(ActorID_SELF, 5, 0, 1, 0); + RemoveActorDecoration(ActorID_SELF, 1, 0); + EnableIdleScript(ActorID_SELF, 1); + UseIdleAnimation(ActorID_SELF, 1); + return; + } else { + SetGoalToTarget(ActorID_SELF); + SetActorJumpGravity(ActorID_SELF, 1.2); + spawn { + GetActorPos(ActorID_SELF, SI_VAR(1), SI_VAR(2), SI_VAR(0)); + SI_VAR(0) = 0; + loop 16 { + GetActorPos(ActorID_SELF, SI_VAR(4), SI_VAR(5), SI_VAR(6)); + func_8021818C_430B2C(SI_VAR(1), SI_VAR(2), SI_VAR(4), SI_VAR(5), SI_VAR(0)); + SetActorRotation(ActorID_SELF, 0, 0, SI_VAR(0)); + SI_VAR(1) = SI_VAR(4); + SI_VAR(2) = SI_VAR(5); + SI_VAR(3) = SI_VAR(6); + sleep 1; + } + } + spawn { + sleep 6; + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk)); + } + JumpToGoal(ActorID_SELF, 16, 0, 1, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, pre_headbonk)); + SetActorScale(ActorID_SELF, 1.1, 0.8, 1.0); + sleep 1; + SetActorScale(ActorID_SELF, 1.3, 0.5, 1.0); + sleep 1; + } + } + EnemyDamageTarget(ActorID_SELF, SI_VAR(0), 0, 0, 0, 1, 32); + match SI_VAR(0) { + 0, 2 { + UseCamPreset(2); + SetActorScale(ActorID_SELF, 1.1, 0.8, 1.0); + sleep 1; + SetActorScale(ActorID_SELF, 1.0, 1.0, 1.0); + sleep 1; + SetActorRotation(ActorID_SELF, 0, 0, 0); + SetActorDispOffset(ActorID_SELF, 0, 0, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle)); + GetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + SI_VAR(0) += 40; + SI_VAR(1) = 0; + SetActorJumpGravity(ActorID_SELF, 1.8); + SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + JumpToGoal(ActorID_SELF, 10, 0, 1, 0); + SI_VAR(0) += 30; + SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + JumpToGoal(ActorID_SELF, 8, 0, 1, 0); + SI_VAR(0) += 20; + SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2)); + JumpToGoal(ActorID_SELF, 6, 0, 1, 0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle)); + sleep 3; + YieldTurn(); + SetAnimationRate(ActorID_SELF, 1, 2.0); + SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, run)); + SetGoalToHome(ActorID_SELF); + SetActorSpeed(ActorID_SELF, 8.0); + RunToGoal(ActorID_SELF, 0, 0); + SetAnimationRate(ActorID_SELF, 1, 1.0); + } + } + EnableIdleScript(ActorID_SELF, 1); + UseIdleAnimation(ActorID_SELF, 1); +}); + diff --git a/src/battle/actor/goomba.h b/src/battle/actor/goomba.h index 04e018590b..3845021230 100644 --- a/src/battle/actor/goomba.h +++ b/src/battle/actor/goomba.h @@ -1,7 +1,9 @@ #ifndef _BATTLE_ACTOR_GOOMBA_ #define _BATTLE_ACTOR_GOOMBA_ -// TODO: disasm -#define goomba 0x802196EC +#include "common.h" +#include "battle/battle.h" + +ActorDesc goomba; #endif diff --git a/src/battle/actor/paragoomba.h b/src/battle/actor/paragoomba.h index 0f90efadec..1bea71359c 100644 --- a/src/battle/actor/paragoomba.h +++ b/src/battle/actor/paragoomba.h @@ -1,7 +1,9 @@ #ifndef _BATTLE_ACTOR_PARAGOOMBA_ #define _BATTLE_ACTOR_PARAGOOMBA_ +#include "battle/battle.h" + // TODO: disasm -#define paragoomba 0x8021CD00 +extern ActorDesc paragoomba; #endif diff --git a/src/battle/actor/spiked_goomba.h b/src/battle/actor/spiked_goomba.h index 0c1b89f363..f4a8bb9eb1 100644 --- a/src/battle/actor/spiked_goomba.h +++ b/src/battle/actor/spiked_goomba.h @@ -1,7 +1,9 @@ #ifndef _BATTLE_ACTOR_SPIKED_GOOMBA_ #define _BATTLE_ACTOR_SPIKED_GOOMBA_ +#include "battle/battle.h" + // TODO: disasm -#define spikedGoomba 0x8021B0AC +extern ActorDesc spiked_goomba; #endif diff --git a/src/battle/area_kmr_part_1/4309A0.c b/src/battle/area_kmr_part_1/4309A0.c index e755dbb50f..6718a11dc6 100644 --- a/src/battle/area_kmr_part_1/4309A0.c +++ b/src/battle/area_kmr_part_1/4309A0.c @@ -1,6 +1,6 @@ #include "common.h" -INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_80218000_4309A0); +INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_80218000_4309A0); // goomba.c INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_8021818C_430B2C); diff --git a/src/battle/area_kmr_part_1/battles.c b/src/battle/area_kmr_part_1/battles.c index 46c4e894de..5b2401b56f 100644 --- a/src/battle/area_kmr_part_1/battles.c +++ b/src/battle/area_kmr_part_1/battles.c @@ -6,66 +6,66 @@ #include "battle/actor/spiked_goomba.h" Formation formation0 = { - { goomba, .position = 1, .priority = 10 }, + { &goomba, .position = 1, .priority = 10 }, }; Formation formation1 = { - { goomba, .position = 1, .priority = 10 }, - { goomba, .position = 2, .priority = 9 }, + { &goomba, .position = 1, .priority = 10 }, + { &goomba, .position = 2, .priority = 9 }, }; Formation formation2 = { - { goomba, .position = 0, .priority = 10 }, - { goomba, .position = 1, .priority = 9 }, - { goomba, .position = 2, .priority = 8 }, + { &goomba, .position = 0, .priority = 10 }, + { &goomba, .position = 1, .priority = 9 }, + { &goomba, .position = 2, .priority = 8 }, }; Formation formation3 = { - { goomba, .position = 1, .priority = 10 }, - { paragoomba, .position = 6, .priority = 9 }, + { &goomba, .position = 1, .priority = 10 }, + { ¶goomba, .position = 6, .priority = 9 }, }; Formation formation4 = { - { goomba, .position = 0, .priority = 10 }, - { goomba, .position = 1, .priority = 9 }, - { goomba, .position = 2, .priority = 8 }, - { goomba, .position = 3, .priority = 7 }, + { &goomba, .position = 0, .priority = 10 }, + { &goomba, .position = 1, .priority = 9 }, + { &goomba, .position = 2, .priority = 8 }, + { &goomba, .position = 3, .priority = 7 }, }; Formation formation5 = { - { goomba, .position = 1, .priority = 10 }, - { spikedGoomba, .position = 2, .priority = 9 }, + { &goomba, .position = 1, .priority = 10 }, + { &spiked_goomba, .position = 2, .priority = 9 }, }; Formation formation6 = { - { goomba, .position = 0, .priority = 10 }, - { paragoomba, .position = 5, .priority = 9 }, - { goomba, .position = 2, .priority = 8 }, - { paragoomba, .position = 7, .priority = 7 }, + { &goomba, .position = 0, .priority = 10 }, + { ¶goomba, .position = 5, .priority = 9 }, + { &goomba, .position = 2, .priority = 8 }, + { ¶goomba, .position = 7, .priority = 7 }, }; Formation formation7 = { - { paragoomba, .position = 5, .priority = 10 }, + { ¶goomba, .position = 5, .priority = 10 }, }; Formation formation8 = { - { paragoomba, .position = 5, .priority = 10 }, - { paragoomba, .position = 6, .priority = 9 }, + { ¶goomba, .position = 5, .priority = 10 }, + { ¶goomba, .position = 6, .priority = 9 }, }; Formation formation9 = { - { paragoomba, .position = 4, .priority = 10 }, - { paragoomba, .position = 5, .priority = 9 }, - { paragoomba, .position = 6, .priority = 8 }, + { ¶goomba, .position = 4, .priority = 10 }, + { ¶goomba, .position = 5, .priority = 9 }, + { ¶goomba, .position = 6, .priority = 8 }, }; Formation formation10 = { - { spikedGoomba, .position = 1, .priority = 10 }, + { &spiked_goomba, .position = 1, .priority = 10 }, }; Formation formation11 = { - { spikedGoomba, .position = 1, .priority = 10 }, - { goomba, .position = 2, .priority = 9 }, + { &spiked_goomba, .position = 1, .priority = 10 }, + { &goomba, .position = 2, .priority = 9 }, }; BattleList area_kmr_part_1_battles = { diff --git a/src/battle/battle.h b/src/battle/battle.h index 2d97adfa11..1496cb0600 100644 --- a/src/battle/battle.h +++ b/src/battle/battle.h @@ -3,6 +3,31 @@ #include "common.h" +typedef struct ActorDesc { + /* 0x00 */ s32 flags; + /* 0x04 */ char unk_04; + /* 0x05 */ u8 type; + /* 0x06 */ u8 level; + /* 0x07 */ u8 maxHP; + /* 0x08 */ s16 partCount; + /* 0x0A */ char unk_0A[2]; + /* 0x0C */ struct ActorPartDesc** partsData; + /* 0x10 */ Bytecode* script; + /* 0x14 */ s32* statusTable; + /* 0x18 */ u8 escapeChance; + /* 0x19 */ u8 airLiftChance; + /* 0x1A */ u8 spookChance; + /* 0x1B */ u8 baseStatusChance; + /* 0x1C */ u8 upAndAwayChance; + /* 0x1D */ u8 spinSmashReq; + /* 0x1E */ u8 powerBounceChance; + /* 0x1F */ u8 coinReward; + /* 0x20 */ Vec2b size; + /* 0x22 */ Vec2b hpBarOffset; + /* 0x24 */ Vec2b statusIconOffset; + /* 0x26 */ Vec2b statusMessageOffset; +} ActorDesc; // size = 0x28 + typedef struct Stage { /* 0x00 */ const char* texture; /* 0x04 */ const char* shape; @@ -23,7 +48,7 @@ typedef struct StageListRow { } StageList[]; // size = 0x08 * n typedef struct FormationRow { - /* 0x00 */ StaticActorData* actor; + /* 0x00 */ ActorDesc* actor; /* 0x04 */ s32 position; ///< Home position. May also be a `Vector3*`. /* 0x08 */ s32 priority; ///< Actors with higher priority values take their turn first. /* 0x0C */ s32 var0; @@ -45,4 +70,24 @@ typedef struct Battle { // TODO: enum for home position (0..3 are floor, 4..7 are air, etc.) +typedef struct { + Element element; + s32 defense; +} DefenseTableEntry; + +typedef DefenseTableEntry DefenseTable[]; + +typedef struct ActorPartDesc { + /* 0x00 */ s32 flags; + /* 0x04 */ s8 index; + /* 0x05 */ Vec3b posOffset; + /* 0x08 */ Vec2b targetOffset; + /* 0x0A */ s16 opacity; + /* 0x0C */ s32* idleAnimations; + /* 0x10 */ s32* defenseTable; + /* 0x14 */ s32 eventFlags; + /* 0x18 */ s32 elementImmunityFlags; + /* 0x1C */ char unk_1C[8]; +} ActorPartDesc; // size = 0x24 + #endif diff --git a/src/code_190B20.c b/src/code_190B20.c index 82b8cfae99..5d73b2d9b7 100644 --- a/src/code_190B20.c +++ b/src/code_190B20.c @@ -1,4 +1,5 @@ #include "code_190B20.h" +#include "battle/battle.h" INCLUDE_ASM(s32, "code_190B20", create_target_list); @@ -93,11 +94,6 @@ INCLUDE_ASM(s32, "code_190B20", func_80265CE8); INCLUDE_ASM(s32, "code_190B20", func_80265D44); -typedef struct { - Element element; - s32 defense; -} DefenseTableEntry; - s32 lookup_defense(DefenseTableEntry* defenseTable, Element elementKey) { DefenseTableEntry* row; s32 normalDefense = 0; diff --git a/src/code_197F40.c b/src/code_197F40.c index 0cc624d125..29db736be7 100644 --- a/src/code_197F40.c +++ b/src/code_197F40.c @@ -1,4 +1,5 @@ #include "common.h" +#include "battle/battle.h" s32 count_targets(Actor* actor, s32 targetHomeIndex, s32 targetSelectionFlags) { BattleStatus* battleStatus = BATTLE_STATUS; diff --git a/src/code_1A5830.c b/src/code_1A5830.c index 64de475aa0..812ef1c94f 100644 --- a/src/code_1A5830.c +++ b/src/code_1A5830.c @@ -1,4 +1,5 @@ #include "common.h" +#include "battle/battle.h" void dispatch_event_actor(Actor* actor, Event event); diff --git a/src/code_1AC760.c b/src/code_1AC760.c index 4a27ff9f56..2059bc8e35 100644 --- a/src/code_1AC760.c +++ b/src/code_1AC760.c @@ -1,4 +1,5 @@ #include "common.h" +#include "battle/battle.h" INCLUDE_ASM(s32, "code_1AC760", dispatch_event_partner); diff --git a/tools/compile_dsl_macros.py b/tools/compile_dsl_macros.py index 14b7d01aac..379c27eb4f 100755 --- a/tools/compile_dsl_macros.py +++ b/tools/compile_dsl_macros.py @@ -77,7 +77,8 @@ script_parser = Lark(r""" | "<" -> cond_op_lt | ">=" -> cond_op_ge | "<=" -> cond_op_le - | "?" -> cond_op_flag + | "&" -> cond_op_flag + | "!&" -> cond_op_not_flag match_stmt: "match" expr "{" (match_cases SEMICOLON*)? "}" match_const_stmt: "matchc" expr "{" (match_cases SEMICOLON*)? "}" @@ -259,12 +260,12 @@ class LabelAllocation(Visitor): raise CompileError(f"label `{name}' already declared", tree.meta) try: - label_idx = int(name, base=0) + label_idx = int(name) - while len(self.labels) < label_idx: + while len(self.labels) <= label_idx: self.labels.append(None) - self.labels.insert(label_idx, name) + self.labels[label_idx] = name except ValueError: self.labels.append(name) @@ -353,7 +354,8 @@ class Compile(Transformer): def cond_op_gt(self, tree): return { "if": "ScriptOpcode_IF_GT", "case": "ScriptOpcode_CASE_GT" } def cond_op_le(self, tree): return { "if": "ScriptOpcode_IF_LE", "case": "ScriptOpcode_CASE_LE" } def cond_op_ge(self, tree): return { "if": "ScriptOpcode_IF_GE", "case": "ScriptOpcode_CASE_GE" } - def cond_op_flag(self, tree): return { "if": "ScriptOpcode_IF_FLAG", "case": "ScriptOpcode_CASE_FLAG" } + def cond_op_flag(self, tree): return { "__op__": "&", "if": "ScriptOpcode_IF_FLAG", "case": "ScriptOpcode_CASE_FLAG" } + def cond_op_not_flag(self, tree): return { "__op__": "!&", "if": "ScriptOpcode_IF_NOT_FLAG" } def match_stmt(self, tree): expr = tree.children[0] @@ -389,13 +391,21 @@ class Compile(Transformer): return [tree.children[0], *tree.children[1]] def case_else(self, tree): - return [Cmd("ScriptOpcode_ELSE"), *tree.children[0]] + return [Cmd("ScriptOpcode_CASE_ELSE"), *tree.children[0]] def case_op(self, tree): if len(tree.children) == 4: op, expr, multi_case, block = tree.children + + if not "case" in op: + raise CompileError(f"operation `{opcodes['__op__']}' not supported in match cases", tree.meta) + return [Cmd(op["case"], expr), *multi_case, *block, Cmd("ScriptOpcode_END_CASE_MULTI")] else: op, expr, block = tree.children + + if not "case" in op: + raise CompileError(f"operation `{opcodes['__op__']}' not supported in match cases", tree.meta) + return [Cmd(op["case"], expr), *block] def case_range(self, tree): if len(tree.children) == 4: @@ -583,33 +593,6 @@ class Compile(Transformer): "const": "ScriptOpcode_OR_CONST", } - def label_decl(self, tree): - if len(tree.children) == 1: - label = tree.children[0] - return Cmd("ScriptOpcode_LABEL", label, meta=tree.meta) - else: - label, cmd_or_block = tree.children - - if type(cmd_or_block) is not list: - cmd_or_block = [cmd_or_block] - - for cmd in cmd_or_block: - if isinstance(cmd, BaseCmd): - cmd.add_context(LabelCtx(label)) - - return [ - Cmd("ScriptOpcode_LABEL", label, meta=tree.meta), - *cmd_or_block - ] - def label_goto(self, tree): - label = tree.children[0] - return Cmd("ScriptOpcode_GOTO", label, meta=tree.meta) - def label(self, tree): - name = tree.children[0] - if name in self.alloc.labels: - return self.alloc.labels.index(name) - raise CompileError(f"label `{name}' is undeclared", tree.meta) - def variable(self, tree): name = tree.children[0] return self.alloc.variables.index(name) - 30000000 diff --git a/tools/disasm_map.py b/tools/disasm_map.py deleted file mode 100755 index d0ffe9b910..0000000000 --- a/tools/disasm_map.py +++ /dev/null @@ -1,232 +0,0 @@ -#! /usr/bin/python3 - -import sys -import os -import yaml -import json -from struct import unpack - -import disasm_script - -def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"): - out = "" - found_data = False - - while len(midx) > 0: - struct = midx.pop(0) - name = struct["name"] - - if name == "Script_Main": name = f"M(Main)" - - #print(f"{offset:X} ({name}, start = {struct['start']:X}, len = {struct['length']:X})") - - if struct["start"] == offset: - found_data = True - - if struct["start"] != offset: - # end of data / padding - break - - # format struct - if struct["type"].startswith("Script"): - pos = bytes.tell() - try: - out += disasm_script.ScriptDSLDisassembler(bytes, f"M({name})", symbol_map).disassemble() - except disasm_script.UnsupportedScript as e: - print(f"Unable to use DSL for {struct['name']}: {e}") - - bytes.seek(pos) - out += disasm_script.ScriptDisassembler(bytes, f"M({name})", symbol_map).disassemble() - elif struct["type"] == "Padding": - # nops at end of file - bytes.seek(offset % 4, 1) - return out - elif struct["type"] == "EntryList": - out += f"EntryList M(entryList) = {{" - for i in range(0, struct["length"], 4 * 4): - x,y,z,yaw = unpack(">ffff", bytes.read(4 * 4)) - out += f"\n {{ {x}f, {y}f, {z}f, {yaw}f }}," - out += f"\n}};\n" - elif struct["type"] == "Header": - out += f"MapConfig M(config) = {{\n" - - bytes.read(0x10) - - main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3)) - out += f" .main = M(Main),\n" - out += f" .entryList = M(entryList),\n" - out += f" .entryCount = ENTRY_COUNT(M(entryList)),\n" - - bytes.read(0x1C) - - bg,tattle = unpack(">II", bytes.read(4 * 2)) - if bg == 0x80200000: - out += f" .background = &gBackgroundImage,\n" - elif bg != 0: - raise Exception(f"unknown MapConfig background {bg:X}") - out += f" .tattle = 0x{tattle:X},\n" - - out += f"}};\n" - elif struct["type"] == "ASCII": - string_data = bytes.read(struct["length"]).decode("ascii") - - # strip null terminator(s) - while string_data[-1] == "\0": - string_data = string_data[:-1] - - string_literal = json.dumps(string_data) - out += f"const char M({struct['name']})[] = {string_literal};" - else: # unknown type of struct - out += f"s32 M({name})[] = {{" - for i in range(0, struct["length"], 4): - if (i % 0x20) == 0: - out += f"\n " - - word = int.from_bytes(bytes.read(4), byteorder="big") - - if word in symbol_map: - out += f" {symbol_map[word]}," - else: - out += f" 0x{word:08X}," - - out += f"\n}};\n" - - out += "\n" - elif found_data: - if struct["type"] != "Padding": - # put struct back on list - midx.insert(0, struct) - - # nops at end of file - bytes.seek(offset % 4, 1) - - return out - - if struct["type"] != "Function" and not struct["type"] == "Padding" and not (struct["type"] == "Missing" and not found_data): - offset += struct["length"] - - # end of data - return out - -def parse_midx(file, prefix = ""): - structs = [] - - for line in file.readlines(): - s = line.split("#") - if len(s) == 5: - if s[0] == "$Start": continue - if s[0] == "$End": continue - - structs.append({ - "name": prefix + name_struct(s[0]), - "type": s[1], - "start": int(s[2], 16), - "vaddr": int(s[3], 16), - "length": int(s[4], 16), - "end": int(s[2], 16) + int(s[4], 16), - }) - elif "Missing" in s: - start = int(s[1], 16) - end = int(s[2], 16) - vaddr = start + 0x80240000 - structs.append({ - "name": f"{prefix}unk_missing_{vaddr:X}", - "type": "Missing", - "start": start, - "vaddr": vaddr, - "length": end - start, - "end": end, - }) - elif "Padding" in s: - start = int(s[1], 16) - end = int(s[2], 16) - vaddr = start + 0x80240000 - structs.append({ - "name": f"{prefix}__padding__", - "type": "Padding", - "start": start, - "vaddr": vaddr, - "length": end - start, - "end": end, - }) - - structs.sort(key=lambda s: s["start"]) - return structs - -def name_struct(s): - s = s[1:].replace("???", "unk") - - # use ThisCase for scripts - if s.startswith("Script_"): - s = s[7].upper() + s[8:] - - # if `s` is hex, prefix it with Script_ again - try: - int(s, 16) - return "Script_" + s - except Exception: - pass - - if s.startswith("Main"): - return "Main" - - return s - - if s.startswith("ASCII"): - return s - - return s[0].lower() + s[1:] - -if __name__ == "__main__": - if len(sys.argv) == 1: - print("usage: ./disasm_map.py ") - print("Converts split map data into C files using a .midx file from Star Rod.") - exit() - - map_name = os.path.splitext(os.path.basename(sys.argv[1]))[0] - area_name = "area_" + map_name.split("_")[0] - if len(area_name) > 8: - area_name = area_name[:8] - - with open(sys.argv[1], "r") as f: - midx = parse_midx(f) - - symbol_map = {} - for struct in midx: - symbol_map[struct["vaddr"]] = "M(" + struct["name"] + ")" - - bin_dir = f"bin/world/{area_name}/{map_name}" - src_dir = f"src/world/{area_name}/{map_name}" - - splits = [] - rom_start = 0 - with open(os.path.join(os.path.dirname(__file__), "splat.yaml")) as splat: - splat = yaml.safe_load(splat) - - for segment in splat["segments"]: - if type(segment) == dict and segment.get("name") == f"world/{area_name}/{map_name}/": - rom_start = segment.get("start", 0) - splits = segment.get("files", []) - continue - if len(splits) == 0: - print(f"unable to find {map_name} in splat.yaml") - exit(1) - - # advance to the EntryList (start of data) - while midx[0]["type"] != "EntryList": - midx.pop(0) - - for split in splits: - rom_addr = split[0] - filetype = split[1] - - if filetype == "bin": - with open(f"{bin_dir}/{rom_addr:X}.bin", "rb") as bytes: - print(f"Disassembling {rom_addr:X}") - - disasm = disassemble(bytes, rom_addr - rom_start, midx, symbol_map, map_name) - - if len(disasm.strip()) > 0: - with open(f"{src_dir}/{rom_addr:X}.bin.c", "w") as f: - f.write(f'#include "{map_name}.h"\n\n') - f.write(disasm.rstrip() + "\n") diff --git a/tools/disasm_script.py b/tools/disasm_script.py index 9c571b931c..75d58efd81 100755 --- a/tools/disasm_script.py +++ b/tools/disasm_script.py @@ -433,11 +433,11 @@ class ScriptDSLDisassembler(ScriptDisassembler): self.write_line("}") if opcode == 0x01: - if self.out.endswith("return\n"): + if self.out.endswith("return;\n"): # implicit return; break - self.out = self.out[:-7].rstrip() + "\n" + self.out = self.out[:-8].rstrip() + "\n" else: - self.write_line("break") + self.write_line("break;") self.indent -= 1 @@ -446,7 +446,10 @@ class ScriptDSLDisassembler(ScriptDisassembler): self.done = True elif opcode == 0x02: self.write_line(f"return;") - elif opcode == 0x03: self.write_line(f"{self.var(argv[0])}:") + elif opcode == 0x03: + self.indent -= 1 + self.write_line(f"{self.var(argv[0])}:") + self.indent += 1 elif opcode == 0x04: self.write_line(f"goto {self.var(argv[0])};") elif opcode == 0x05: if argv[0] == 0: @@ -479,7 +482,10 @@ class ScriptDSLDisassembler(ScriptDisassembler): self.write_line(f"if ({self.var(argv[0])} >= {self.var(argv[1])}) {{") self.indent += 1 elif opcode == 0x10: - self.write_line(f"if ({self.var(argv[0])} ? {self.var(argv[1])}) {{") + self.write_line(f"if ({self.var(argv[0])} & {self.var(argv[1])}) {{") + self.indent += 1 + elif opcode == 0x11: + self.write_line(f"if ({self.var(argv[0])} !& {self.var(argv[1])}) {{") self.indent += 1 elif opcode == 0x12: self.indent -= 1 @@ -595,7 +601,7 @@ class ScriptDSLDisassembler(ScriptDisassembler): elif opcode == 0x42: self.write_line(f"{self.var(argv[0])} |=c {argv[1]:X};") elif opcode == 0x43: argv_str = ", ".join(self.var(arg) for arg in argv[1:]) - self.write_line(f"{self.addr_ref(argv[0])}({argv_str})") + self.write_line(f"{self.addr_ref(argv[0])}({argv_str});") elif opcode == 0x44: self.write_line(f"spawn {self.addr_ref(argv[0])};") elif opcode == 0x45: self.write_line(f"{self.var(argv[1])} = spawn {self.addr_ref(argv[0])};") elif opcode == 0x46: self.write_line(f"await {self.addr_ref(argv[0])};") diff --git a/tools/splat.yaml b/tools/splat.yaml index d3c1234d93..657d17ea85 100644 --- a/tools/splat.yaml +++ b/tools/splat.yaml @@ -934,7 +934,8 @@ segments: files: - [0x4309A0, c] - [0x431B80, .data, battle/area_kmr_part_1/battles] - - [0x431FB0, bin, battle/area_kmr_part_1/goomba] + - [0x431FB0, .data, battle/actor/goomba] + - [0x433970, bin] - [0x4398A0, .rodata, battle/area_kmr_part_1/battles] - [0x439984, bin] - name: battle/area_kmr_part_2/ diff --git a/tools/star_rod_idx_to_c.py b/tools/star_rod_idx_to_c.py new file mode 100755 index 0000000000..b1d561a246 --- /dev/null +++ b/tools/star_rod_idx_to_c.py @@ -0,0 +1,198 @@ +#! /usr/bin/python3 + +import sys +import os +import yaml +import json +from struct import unpack +import argparse + +import disasm_script + +DIR = os.path.dirname(__file__) + +def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0): + out = "" + + entry_list_name = None + main_script_name = None + + while len(midx) > 0: + struct = midx.pop(0) + name = struct["name"] + + if comments: + out += f"// {romstart+struct['start']:X}-{romstart+struct['end']:X} (VRAM: {struct['vaddr']:X})\n" + + # format struct + if struct["type"].startswith("Script"): + if struct["type"] == "Script_Main": + main_script_name = name + + pos = bytes.tell() + try: + out += disasm_script.ScriptDSLDisassembler(bytes, name, symbol_map).disassemble() + except disasm_script.UnsupportedScript as e: + out += f"// Unable to use DSL: {e}\n" + + bytes.seek(pos) + out += disasm_script.ScriptDisassembler(bytes, name, symbol_map).disassemble() + elif struct["type"] == "EntryList": + entry_list_name = name + out += f"EntryList {name} = {{" + for i in range(0, struct["length"], 4 * 4): + x,y,z,yaw = unpack(">ffff", bytes.read(4 * 4)) + out += f"\n {{ {x}f, {y}f, {z}f, {yaw}f }}," + out += f"\n}};\n" + elif struct["type"] == "Header": + out += f"MapConfig {name} = {{\n" + + bytes.read(0x10) + + main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3)) + out += f" .main = {main_script_name},\n" + out += f" .entryList = {entry_list_name},\n" + out += f" .entryCount = ENTRY_COUNT({entry_list_name}),\n" + + bytes.read(0x1C) + + bg,tattle = unpack(">II", bytes.read(4 * 2)) + if bg == 0x80200000: + out += f" .background = &gBackgroundImage,\n" + elif bg != 0: + raise Exception(f"unknown MapConfig background {bg:X}") + out += f" .tattle = 0x{tattle:X},\n" + + out += f"}};\n" + elif struct["type"] == "ASCII": + string_data = bytes.read(struct["length"]).decode("ascii") + + # strip null terminator(s) + while string_data[-1] == "\0": + string_data = string_data[:-1] + + string_literal = json.dumps(string_data) + out += f"const char {struct['name']}[] = {string_literal};\n" + elif struct["type"].startswith("Function"): + bytes.read(struct["length"]) + out += f"// function: {name}\n" + elif struct["type"] == "FloatTable": + out += f"f32 {name}[] = {{" + for i in range(0, struct["length"], 4): + if (i % 0x20) == 0: + out += f"\n " + + word = unpack(">f", bytes.read(4))[0] + out += " %ff," % word + + out += f"\n}};\n" + else: # unknown type of struct + out += f"s32 {name}[] = {{" + for i in range(0, struct["length"], 4): + if (i % 0x20) == 0: + out += f"\n " + + word = int.from_bytes(bytes.read(4), byteorder="big") + + if word in symbol_map: + out += f" {symbol_map[word]}," + else: + out += f" 0x{word:08X}," + + out += f"\n}};\n" + + out += "\n" + + # end of data + return out + +def parse_midx(file, prefix = ""): + structs = [] + + for line in file.readlines(): + s = line.split("#") + if len(s) == 5: + if s[0] == "$Start": continue + if s[0] == "$End": continue + + structs.append({ + "name": prefix + name_struct(s[0]), + "type": s[1], + "start": int(s[2], 16), + "vaddr": int(s[3], 16), + "length": int(s[4], 16), + "end": int(s[2], 16) + int(s[4], 16), + }) + elif "Missing" in s: + start = int(s[1], 16) + end = int(s[2], 16) + vaddr = start + 0x80240000 + structs.append({ + "name": f"{prefix}unk_missing_{vaddr:X}", + "type": "Missing", + "start": start, + "vaddr": vaddr, + "length": end - start, + "end": end, + }) + elif "Padding" in s: + start = int(s[1], 16) + end = int(s[2], 16) + vaddr = start + 0x80240000 + structs.append({ + "name": f"{prefix}pad_{start:X}", + "type": "Padding", + "start": start, + "vaddr": vaddr, + "length": end - start, + "end": end, + }) + + structs.sort(key=lambda s: s["start"]) + return structs + +def name_struct(s): + s = s[1:].replace("???", "unk") + + """ + # use ThisCase for scripts + if s.startswith("Script_"): + s = s[7].upper() + s[8:] + + # if `s` is hex, prefix it with Script_ again + try: + int(s, 16) + return "Script_" + s + except Exception: + pass + + if s.startswith("Main"): + return "Main" + + return s + """ + + if s.startswith("ASCII"): + return s + + return s[0].lower() + s[1:] + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Converts split data to C using a Star Rod idx file") + parser.add_argument("idxfile", help="Input .*idx file from Star Rod dump") + parser.add_argument("offset", help="Starting ROM offset") + parser.add_argument("--comments", action="store_true", help="Write offset/vaddr comments") + + args = parser.parse_args() + + with open(args.idxfile, "r") as f: + midx = parse_midx(f) + + symbol_map = {} + for struct in midx: + symbol_map[struct["vaddr"]] = struct["name"] + + with open(os.path.join(DIR, "../baserom.z64"), "rb") as romfile: + romfile.seek(eval(args.offset)) + disasm = disassemble(romfile, midx, symbol_map, args.comments, eval(args.offset)) + print(disasm.rstrip()) diff --git a/undefined_syms.txt b/undefined_syms.txt index fdf1ca6df4..a7fc80590a 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -329,6 +329,10 @@ DoShockHit = 0x8029A6FC; DoSleepHit = 0x802945E0; DoSpinSmashHit = 0x8029B998; DoStopHit = 0x80294650; +DoAirLift = 0x8029C37C; +DoBlowAway = 0x8029C4A8; +DoBurnHit = 0x8029A0D0; +DoDeath = 0x8029AEC0; ShakeCam1 = 0x802D9CB0; ShakeCamX = 0x802D9CE8; @@ -358,3 +362,6 @@ D_DE0079B8 = 0xDE0079B8; D_DE003E00 = 0xDE003E00; D_C1F06370 = 0xC1F06370; D_DE001F00 = 0xDE001F00; + +paragoomba = 0x8021CD00; +spiked_goomba = 0x8021B0AC;