Skip to content

Commit a622c69

Browse files
committed
Merge branch 'main' of https://github.com/TP-Ultimate-Edition/TP-Ultimate into blo_editor
2 parents 7a6d276 + 237b8f4 commit a622c69

File tree

8 files changed

+893
-50
lines changed

8 files changed

+893
-50
lines changed

.claude/settings.local.json

Lines changed: 0 additions & 20 deletions
This file was deleted.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ compile_commands.json
3838

3939
# Environment files
4040
.env
41+
4142
# Claude Code
4243
.claude
4344
/.claude
4445
.claude/settings.local.json
46+
47+
# BMG files
48+
*.bmg

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ cd Twilight-Princess-Ultimate-Edition</pre>
169169

170170
<hr>
171171

172+
<hr>
173+
174+
<h2><b>Tools</b></h2>
175+
<h3>BMG Editor</h3>
176+
<p>Update custom_text.xml according to the template and bmg_editor.py will rebuild an iso with your custom text!</p>
177+
178+
<hr>
179+
172180
<h2><b>Credits</b></h2>
173181

174182
<h3>Zelda Reverse Engineering Team</h3>

src/d/actor/d_a_e_kk.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,10 +564,13 @@ void daE_KK_c::executeSpearThrow() {
564564
break;
565565
}
566566
}
567-
if ((s32)mpMorfSO->getFrame() == 0x17) {
567+
// Check animation frame for javelin spawn and use flag to prevent multiple spawns
568+
// Without field_0x67d check: at 60fps the animation could stay on frame 0x17 for multiple
569+
// game frames, causing fopAcM_createChild to spawn 2-3 javelins instead of 1
570+
if ((s32)mpMorfSO->getFrame() == 0x17 && field_0x67d == 0) {
568571
fopAcM_createChild(PROC_E_KK, fopAcM_GetID(this), 0xFF0001, &field_0x698,
569572
fopAcM_GetRoomNo(this), &shape_angle, NULL, -1, NULL);
570-
field_0x67d = 1;
573+
field_0x67d = 1; // Set flag so we only spawn once per throw animation
571574
mCyl.OffTgNoHitMark();
572575
mCyl.OffTgShield();
573576
mCreatureSound.startCreatureSound(Z2SE_EN_KK_THROW, 0, -1);
@@ -1006,7 +1009,9 @@ void daE_KK_c::executeWeaponMove() {
10061009
-1.0f, -1.0f, 0);
10071010
}
10081011
mStts.Init(0xFF, 0, this);
1009-
mTimer = (s16)cM_rndF(20.0f) + 30;
1012+
// Javelin despawn timer: 30-50 frames. Timers decrement by 1 each frame, not by DELTA_TIME,
1013+
// so we multiply the assignment by SCALE_TIME to maintain the intended duration
1014+
mTimer = ((s16)cM_rndF(20.0f) + 30) * SCALE_TIME;
10101015
mMoveMode = 2;
10111016
}
10121017
goto end;

src/d/actor/d_a_e_sm2.cpp

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @file d_a_e_sm2.cpp
3-
*
3+
* ChuChu Slime Enemy
44
*/
55

66
#include "d/dolzel_rel.h" // IWYU pragma: keep
@@ -143,7 +143,8 @@ static void sm2_delete(e_sm2_class* i_this) {
143143
} else {
144144
i_this->action = ACTION_ROOF;
145145
i_this->mode = 0;
146-
i_this->timers[0] = 30.0f + cM_rndF(60.0f);
146+
// Wait timer before appearing from ceiling (SCALE_TIME)
147+
i_this->timers[0] = (30.0f + cM_rndF(60.0f)) * SCALE_TIME;
147148
actor->current.pos = actor->home.pos;
148149
i_this->color_alpha = 1.0f;
149150
i_this->field_0x6b0 = 1.0f;
@@ -236,9 +237,11 @@ static void normal_move(e_sm2_class* i_this) {
236237
if (i_this->timers[0] == 0) {
237238
if (i_this->field_0x6a8 == 0) {
238239
i_this->mCurrentAngleYTarget = cM_rndF(65536.0f);
239-
i_this->timers[0] = 23.0f + cM_rndF(3.0f);
240+
// Wait timer before moving on ceiling (SCALE_TIME)
241+
i_this->timers[0] = (23.0f + cM_rndF(3.0f)) * SCALE_TIME;
240242
} else {
241-
i_this->timers[0] = 8.0f + cM_rndF(3.0f);
243+
// Wait timer for damaged state (SCALE_TIME)
244+
i_this->timers[0] = (8.0f + cM_rndF(3.0f)) * SCALE_TIME;
242245
}
243246

244247
i_this->mCurrentAngleYTargetStep = 0;
@@ -262,14 +265,16 @@ static void normal_move(e_sm2_class* i_this) {
262265
}
263266

264267
if (i_this->field_0x6a8 == 0) {
265-
i_this->timers[0] = 23.0f + cM_rndF(3.0f);
268+
// Return to wait timer after moving (SCALE_TIME)
269+
i_this->timers[0] = (23.0f + cM_rndF(3.0f)) * SCALE_TIME;
266270
} else {
267271
s16 sp8 = actor->current.angle.y - i_this->mCurrentAngleYTarget;
268272
if (sp8 < 0x1000 && sp8 > -0x1000 && i_this->dist_to_pl < (400.0f + (100.0f * i_this->size))) {
269273
i_this->action = ACTION_ATTACK;
270274
i_this->mode = 0;
271275
} else {
272-
i_this->timers[0] = 8.0f + cM_rndF(3.0f);
276+
// Return to wait timer for damaged state (SCALE_TIME)
277+
i_this->timers[0] = (8.0f + cM_rndF(3.0f)) * SCALE_TIME;
273278
}
274279
}
275280
}
@@ -329,7 +334,8 @@ static void attack(e_sm2_class* i_this) {
329334

330335
if (i_this->acch.ChkGroundHit()) {
331336
i_this->mode = 2;
332-
i_this->timers[0] = BREG_S(3) + 10;
337+
// Ground impact recovery timer after jump (SCALE_TIME)
338+
i_this->timers[0] = (BREG_S(3) + 10) * SCALE_TIME;
333339
i_this->field_0x82c = 5.0f;
334340
i_this->field_0x6aa = 23;
335341

@@ -348,7 +354,8 @@ static void attack(e_sm2_class* i_this) {
348354
if (actor->speedF < 1.0f) {
349355
i_this->action = ACTION_NORMAL_MOVE;
350356
i_this->mode = 0;
351-
i_this->timers[0] = cM_rndF(20.0f);
357+
// Wait timer before next movement (SCALE_TIME)
358+
i_this->timers[0] = cM_rndF(20.0f) * SCALE_TIME;
352359
}
353360
break;
354361
case 10:
@@ -389,7 +396,8 @@ static s8 combine(e_sm2_class* i_this) {
389396
if (combine_actor == NULL || combine_actor->action != ACTION_COMBINE) {
390397
i_this->action = ACTION_NORMAL_MOVE;
391398
i_this->mode = 0;
392-
i_this->timers[0] = 20.0f + cM_rndF(20.0f);
399+
// Fallback timer when combine partner is lost (SCALE_TIME)
400+
i_this->timers[0] = (20.0f + cM_rndF(20.0f)) * SCALE_TIME;
393401
return cc_co_ON;
394402
}
395403

@@ -409,7 +417,8 @@ static s8 combine(e_sm2_class* i_this) {
409417
switch (i_this->mode) {
410418
case 0:
411419
if (i_this->timers[0] == 0) {
412-
i_this->timers[0] = 8.0f + cM_rndF(3.0f);
420+
// Wait timer during combine mode (SCALE_TIME)
421+
i_this->timers[0] = (8.0f + cM_rndF(3.0f)) * SCALE_TIME;
413422
i_this->mode = 1;
414423
}
415424
break;
@@ -419,7 +428,8 @@ static s8 combine(e_sm2_class* i_this) {
419428

420429
if (i_this->timers[0] == 0) {
421430
i_this->mode = 0;
422-
i_this->timers[0] = 8.0f + cM_rndF(3.0f);
431+
// Return to wait in combine mode (SCALE_TIME)
432+
i_this->timers[0] = (8.0f + cM_rndF(3.0f)) * SCALE_TIME;
423433
}
424434
break;
425435
case 5:
@@ -497,17 +507,22 @@ static s8 roof(e_sm2_class* i_this) {
497507
if (i_this->field_0x5b8 != 0) {
498508
if (dComIfGs_isSwitch(i_this->field_0x5b8, fopAcM_GetRoomNo(actor))) {
499509
i_this->mode = 2;
500-
i_this->timers[0] = 2.0f + cM_rndF(50.0f);
501-
i_this->timers[1] = i_this->timers[0] + 50;
510+
// Delay before emerging from ground via switch (SCALE_TIME)
511+
i_this->timers[0] = (2.0f + cM_rndF(50.0f)) * SCALE_TIME;
512+
// Extended timer for full emergence sequence (SCALE_TIME)
513+
i_this->timers[1] = i_this->timers[0] + 50 * SCALE_TIME;
502514
}
503515
} else if (fopAcM_searchPlayerDistanceXZ(actor) < (100.0f * i_this->field_0x5b6)) {
504516
i_this->mode = 2;
505517
if (strcmp(dComIfGp_getStartStageName(), "D_SB07") == 0) {
506-
i_this->timers[0] = 2.0f + cM_rndF(50.0f);
518+
// Delay before emerging from ground near player (SCALE_TIME)
519+
i_this->timers[0] = (2.0f + cM_rndF(50.0f)) * SCALE_TIME;
507520
} else {
508-
i_this->timers[0] = 2;
521+
// Quick emergence in other stages (SCALE_TIME)
522+
i_this->timers[0] = 2 * SCALE_TIME;
509523
}
510-
i_this->timers[1] = i_this->timers[0] + 50;
524+
// Extended timer for full emergence sequence (SCALE_TIME)
525+
i_this->timers[1] = i_this->timers[0] + 50 * SCALE_TIME;
511526
}
512527
break;
513528
case 2:
@@ -530,7 +545,8 @@ static s8 roof(e_sm2_class* i_this) {
530545

531546
if (i_this->field_0x830 > 0.9f * (1.3f + KREG_F(17))) {
532547
i_this->mode = 3;
533-
i_this->timers[0] = 5;
548+
// Brief pause after emerging fully from ground (SCALE_TIME)
549+
i_this->timers[0] = 5 * SCALE_TIME;
534550
}
535551
}
536552
break;
@@ -542,7 +558,8 @@ static s8 roof(e_sm2_class* i_this) {
542558
if (i_this->acch.ChkGroundHit()) {
543559
i_this->action = ACTION_NORMAL_MOVE;
544560
i_this->mode = 0;
545-
i_this->timers[0] = 40.0f + cM_rndF(30.0f);
561+
// Wait timer after landing from ground emergence (SCALE_TIME)
562+
i_this->timers[0] = (40.0f + cM_rndF(30.0f)) * SCALE_TIME;
546563
i_this->field_0x6a8 = 0;
547564
i_this->field_0x830 = 1.0f;
548565
i_this->field_0x6aa = 20;
@@ -645,7 +662,8 @@ static void fail(e_sm2_class* i_this) {
645662
if (i_this->acch.ChkGroundHit()) {
646663
if (i_this->mode == 0) {
647664
i_this->mode = 1;
648-
i_this->timers[1] = 25;
665+
// Death animation duration timer (SCALE_TIME)
666+
i_this->timers[1] = 25 * SCALE_TIME;
649667
}
650668

651669
cLib_addCalc2(&i_this->field_0x6b0, 0.5f + BREG_F(9), 0.05f, 0.05f + VREG_F(7));
@@ -664,7 +682,8 @@ static void fail(e_sm2_class* i_this) {
664682
if (i_this->mode == 1) {
665683
if (actor->eventInfo.checkCommandCatch()) {
666684
i_this->mode = 2;
667-
i_this->timers[0] = KREG_S(7) + 9;
685+
// Bottle catch sequence timer (SCALE_TIME)
686+
i_this->timers[0] = (KREG_S(7) + 9) * SCALE_TIME;
668687
} else {
669688
static u8 item_no[] = {
670689
fpcNm_ITEM_CHUCHU_GREEN,
@@ -861,7 +880,8 @@ static void damage_check(e_sm2_class* i_this) {
861880
i_this->type = new_color_type;
862881
sm_hit_actor->type = new_color_type;
863882

864-
sm_hit_actor->timers[0] = 100;
883+
// Invulnerability timer for other ChuChu after combining (SCALE_TIME)
884+
sm_hit_actor->timers[0] = 100 * SCALE_TIME;
865885
sm_hit_actor->combine_actor_pid = fopAcM_GetID(actor);
866886
return;
867887
}
@@ -1053,12 +1073,14 @@ static void action(e_sm2_class* i_this) {
10531073
MtxPosition(&work, &offset);
10541074
actor->speed.x = offset.x;
10551075
actor->speed.z = offset.z;
1056-
actor->speed.y += actor->gravity;
1076+
// Frame-based accumulator: Gravity velocity increments each frame
1077+
actor->speed.y += actor->gravity * DELTA_TIME;
10571078
if (actor->speed.y < -100.0f) {
10581079
actor->speed.y = -100.0f;
10591080
}
10601081

1061-
actor->current.pos += actor->speed;
1082+
// Frame-based accumulator: Position increments each frame based on velocity
1083+
actor->current.pos += actor->speed * DELTA_TIME;
10621084

10631085
cXyz* cc_move_p = i_this->ccStts.GetCCMoveP();
10641086
if (cc_move_p != NULL) {
@@ -1276,7 +1298,8 @@ static void action(e_sm2_class* i_this) {
12761298
static f32 asp[] = {500.0f, 400.0f, 300.0f, 200.0f, 100.0f};
12771299
static f32 asp2[] = {3500.0f, 3000.0f, 2500.0f, 2000.0f, 1500.0f};
12781300

1279-
i_this->field_0x828 += (s16)(asp2[i_this->sizetype] + (i_this->field_0x82c * asp[i_this->sizetype]));
1301+
// Frame-based accumulator: Wobble rotation angle increments each frame
1302+
i_this->field_0x828 += (s16)((asp2[i_this->sizetype] + (i_this->field_0x82c * asp[i_this->sizetype])) * DELTA_TIME);
12801303

12811304
for (int i = 0; i < 8; i++) {
12821305
if (i_this->action != ACTION_FAIL) {
@@ -1612,7 +1635,8 @@ static int daE_SM2_Create(fopAc_ac_c* i_this) {
16121635
a_this->is_roof = TRUE;
16131636
} else {
16141637
a_this->action = ACTION_NORMAL_MOVE;
1615-
a_this->timers[0] = cM_rndF(20.0f);
1638+
// Initial wait timer after spawning (SCALE_TIME)
1639+
a_this->timers[0] = cM_rndF(20.0f) * SCALE_TIME;
16161640
a_this->combine_off_timer = 100.0f + cM_rndF(100.0f);
16171641
}
16181642

src/d/actor/d_a_obj_item.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -931,15 +931,17 @@ int daItem_c::itemActionForRupee() {
931931

932932
if (mAcch.ChkGroundHit()) {
933933
RotateYBase();
934-
speedF *= 0.95f;
934+
// Frame-based decay: Exponential friction using powf for framerate independence
935+
speedF *= powf(0.95f, DELTA_TIME);
935936
}
936937

937938
if (field_0x94b >= 2) {
938939
clrFlag(FLAG_UNK_2_e);
939940
}
940941

941942
if (field_0x94b == 0) {
942-
shape_angle.x += getData().mRotateXSpeed;
943+
// Frame-based accumulator: Rotation angle increments each frame
944+
shape_angle.x += getData().mRotateXSpeed * DELTA_TIME;
943945
} else {
944946
shape_angle.x = 0;
945947
}

0 commit comments

Comments
 (0)