mirror of
https://github.com/pmret/papermario.git
synced 2024-11-18 00:42:34 +01:00
commit
8a5f2dcf5c
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-vscode.cpptools",
|
||||
"nanaian.vscode-star-rod",
|
||||
"nanaian.papermario",
|
||||
"notskm.clang-tidy",
|
||||
"EditorConfig.EditorConfig",
|
||||
],
|
||||
|
@ -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;
|
||||
|
477
src/battle/actor/goomba.c
Normal file
477
src/battle/actor/goomba.c
Normal file
@ -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);
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "battle/battle.h"
|
||||
|
||||
s32 count_targets(Actor* actor, s32 targetHomeIndex, s32 targetSelectionFlags) {
|
||||
BattleStatus* battleStatus = BATTLE_STATUS;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "battle/battle.h"
|
||||
|
||||
void dispatch_event_actor(Actor* actor, Event event);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "battle/battle.h"
|
||||
|
||||
INCLUDE_ASM(s32, "code_1AC760", dispatch_event_partner);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 <file.midx>")
|
||||
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")
|
@ -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])};")
|
||||
|
@ -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/
|
||||
|
198
tools/star_rod_idx_to_c.py
Executable file
198
tools/star_rod_idx_to_c.py
Executable file
@ -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())
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user