From 1e175b5ce7fd6f7a62dd8aafc160bc8985e3ca3f Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Sun, 26 Feb 2023 03:15:54 +0100 Subject: [PATCH] Make most used and useful features built-ins (#242) * made all bools u8 for consistency with fixCollisionBugs * preview blue coins + visible secrets * preserve indexing * star number * hud cap timer * red coins radar + secrets radar * extended pause display * autogen * only sDynosBuiltinFuncs is sensitive to indexing * rebased --- actors/bowser_key/bowser_key_left.rgba16.png | Bin 0 -> 902 bytes actors/bowser_key/bowser_key_right.rgba16.png | Bin 0 -> 1034 bytes actors/bowser_key/model.inc.c | 8 + autogen/lua_definitions/constants.lua | 13 +- autogen/lua_definitions/functions.lua | 20 ++ autogen/lua_definitions/structs.lua | 14 +- data/behavior_data.c | 21 ++ data/behavior_table.c | 2 + data/dynos_bin_behavior.cpp | 2 + data/dynos_bin_common.cpp | 2 + data/dynos_mgr_builtin.cpp | 14 ++ data/dynos_mgr_builtin_externs.h | 2 + data/dynos_mgr_builtin_tex.cpp | 2 + docs/lua/constants.md | 5 +- docs/lua/functions-2.md | 72 ++++++ docs/lua/functions.md | 4 + docs/lua/structs.md | 14 +- include/behavior_data.h | 2 + include/behavior_table.h | 2 + include/object_constants.h | 1 + include/object_fields.h | 1 + src/game/behavior_actions.h | 4 + src/game/behaviors/blue_coin.inc.c | 83 ++++++- src/game/behaviors/hidden_star.inc.c | 4 + src/game/behaviors/mushroom_1up.inc.c | 4 + src/game/behaviors/sparkle_spawn_star.inc.c | 2 + src/game/behaviors/spawn_star.inc.c | 46 ++++ src/game/camera.c | 6 +- src/game/hardcoded.c | 11 + src/game/hardcoded.h | 13 +- src/game/hud.c | 138 +++++++++++ src/game/ingame_menu.c | 218 +++++++++++++++++- src/game/interaction.c | 8 + src/game/mario_actions_cutscene.c | 6 +- src/game/obj_behaviors.c | 2 + src/pc/lua/smlua_cobject_autogen.c | 19 +- src/pc/lua/smlua_constants_autogen.c | 7 +- src/pc/lua/smlua_functions_autogen.c | 64 +++++ 38 files changed, 813 insertions(+), 23 deletions(-) create mode 100644 actors/bowser_key/bowser_key_left.rgba16.png create mode 100644 actors/bowser_key/bowser_key_right.rgba16.png diff --git a/actors/bowser_key/bowser_key_left.rgba16.png b/actors/bowser_key/bowser_key_left.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..2f9ca909ace0449478879e478d280e73fbe209e4 GIT binary patch literal 902 zcmV;119|+3P)pII7vi7RCwC$n9WZUQ540mVEktk6OC~xglJf3qH$rwjT=qeX<}j` zQDf8=+oBfG0!qRH6LCc(CZa$;C>Bvlp;*w`LXA6JX-F{9_IlsUd+qdfhSKse3*X{| zHbdwB?z!i_fdV3eovP@Uhx$*TtpMK#!9IWyZ!P|QZ4aODUlOUgy-OhHC(0|p#`<97i?_htYK1n`*vQf2`4y8-L> z03JICIB<>czF%KMA(jgvZA|Q-x&`RJ1IW4$GXOWYU!H$CfII}LaW7y?GvK!YDmF%! zg!~0%>3xi5n(w~VV858ZMF7)}*9f72U~V)wP)Brwzs!vhu2Ee-DJy=5)I9?**v|mu z0G0)iA)59B_FV>C8KO%JeIDwv{!YK(UuYAx75DcL_m0CVl70jvl>hB$fv zaIi({UQYj70VIG-r2xi}oI3)@R!{5Rsqem({Z;|Y^g?wHkVeXa??Rz_qy5IyZ(7XX z z!Uil7K(b2@;N}3x_Pa~%HwMtS0ZSVNUlg1R1dstFIY4@=0EA**69AvS1=|83 zG%x-N;u(;~V9USc?7_su+_43Kt*4LzPyh-*0Vn_kpa2wr0#E=7KmjNK1z-yR`}#)c c{{%4VABOjd45}J$)Bpeg07*qoM6N<$f?-C5r~m)} literal 0 HcmV?d00001 diff --git a/actors/bowser_key/bowser_key_right.rgba16.png b/actors/bowser_key/bowser_key_right.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..acd881e7749be4e50fabc2ea7cae8ac24b75d9eb GIT binary patch literal 1034 zcmV+l1oiugP)pIyh%hsRCwC$nM+SoQ4q%mFm7EKKL%gG4`Jd0&_sOdwfMQz(K}a+b7O>H{Km!6I$V*x%lt-~Vi)Ng;=Ullhw=ddqP3U4m+Vr0J zo&SI4JT8EUU}U6yz;4q)+}{9SEx9=E%0a^bdLIG~Hss-lM}UR^e5nTfVTXiONWTIm zzG zgEaeZ^*%SI5-|1*&_lG-1=&AoLNUS^@kc zZ!7?*!uo3(h$P)?(;sNcDkh=^f1V@-x^4kFD?w>XJqQ@DBLMQ>$NozT7+^^PC^nD^ zYfY5A6>zIvD8dgjFxL%-&n_mxwZsz|AsZYQ0jo{|RulnN(%)-mbpR&nGzG{24t;=C zjS8VQ0jeaf*Ce)y+4tNBG@TQP%L^$^5XhN4eG9Yza}r=dEkp?rlmJ>40`7=e+l1sD z*8y*y-cEjnqWE}>BIJj75T71^b0@$Csjx*VY_y2y)L9QlNM25HIZko~;AaGY-=W_L zD-vLx{NJSHTV#X3QLG`K?f_O7i^LU_^eV-rqWH-LfLw%p_80(~LR0{x3IY1=Z57~+ zQ`?f46#$kNCcbvSlmHnf2^xSvBmmce zY6BHy_iX`CMseX-^2_%QfUrVz>UYA1Y6Ahx6KGAzA-r31cvpLeQiR?p0EFUE8BA*MF6WTL>+*oh&^GoDZCI_fj_N~CIGo5C({Xv3N0xB z@Ji^h>J%aYEJOfAtAms-CQ$*Rbb?}orX5qh8BNv+fS83y0L$)h8`5?)QvfWn0sh(G zN?+oz?U)xMuRIqrfVL1-8!~n|cloIz0 z2I$EGWC5}OS%55n(EtNRZ$^B(4R#(y!vKbA<@rxW@LfLcIY7Xv6Ryl!vjc$7eroXN zrF{orm<9ZL)zBjBt@#hy(9ng50r=Xr*_n@q0Q8%~Us^9Z#btEQ0lc>GP+wg5>N5iq z7eEb +## [bhv_blue_coin_number_loop](#bhv_blue_coin_number_loop) + +### Lua Example +`bhv_blue_coin_number_loop()` + +### Parameters +- None + +### Returns +- None + +### C Prototype +`void bhv_blue_coin_number_loop(void);` + +[:arrow_up_small:](#) + +
+ ## [bhv_blue_coin_sliding_jumping_init](#bhv_blue_coin_sliding_jumping_init) ### Lua Example @@ -1024,6 +1042,24 @@
+## [bhv_blue_coin_switch_init](#bhv_blue_coin_switch_init) + +### Lua Example +`bhv_blue_coin_switch_init()` + +### Parameters +- None + +### Returns +- None + +### C Prototype +`void bhv_blue_coin_switch_init(void);` + +[:arrow_up_small:](#) + +
+ ## [bhv_blue_coin_switch_loop](#bhv_blue_coin_switch_loop) ### Lua Example @@ -7760,6 +7796,24 @@
+## [bhv_star_number_loop](#bhv_star_number_loop) + +### Lua Example +`bhv_star_number_loop()` + +### Parameters +- None + +### Returns +- None + +### C Prototype +`void bhv_star_number_loop(void);` + +[:arrow_up_small:](#) + +
+ ## [bhv_star_spawn_init](#bhv_star_spawn_init) ### Lua Example @@ -10097,6 +10151,24 @@
+## [spawn_star_number](#spawn_star_number) + +### Lua Example +`spawn_star_number()` + +### Parameters +- None + +### Returns +- None + +### C Prototype +`void spawn_star_number(void);` + +[:arrow_up_small:](#) + +
+ ## [spawn_triangle_break_particles](#spawn_triangle_break_particles) ### Lua Example diff --git a/docs/lua/functions.md b/docs/lua/functions.md index cddbb6a0..7e9ae5ca 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -81,8 +81,10 @@ - [bhv_blue_bowser_flame_init](functions-2.md#bhv_blue_bowser_flame_init) - [bhv_blue_bowser_flame_loop](functions-2.md#bhv_blue_bowser_flame_loop) - [bhv_blue_coin_jumping_loop](functions-2.md#bhv_blue_coin_jumping_loop) + - [bhv_blue_coin_number_loop](functions-2.md#bhv_blue_coin_number_loop) - [bhv_blue_coin_sliding_jumping_init](functions-2.md#bhv_blue_coin_sliding_jumping_init) - [bhv_blue_coin_sliding_loop](functions-2.md#bhv_blue_coin_sliding_loop) + - [bhv_blue_coin_switch_init](functions-2.md#bhv_blue_coin_switch_init) - [bhv_blue_coin_switch_loop](functions-2.md#bhv_blue_coin_switch_loop) - [bhv_blue_fish_movement_loop](functions-2.md#bhv_blue_fish_movement_loop) - [bhv_blue_flames_group_loop](functions-2.md#bhv_blue_flames_group_loop) @@ -457,6 +459,7 @@ - [bhv_star_door_loop](functions-2.md#bhv_star_door_loop) - [bhv_star_door_loop_2](functions-2.md#bhv_star_door_loop_2) - [bhv_star_key_collection_puff_spawner_loop](functions-2.md#bhv_star_key_collection_puff_spawner_loop) + - [bhv_star_number_loop](functions-2.md#bhv_star_number_loop) - [bhv_star_spawn_init](functions-2.md#bhv_star_spawn_init) - [bhv_star_spawn_loop](functions-2.md#bhv_star_spawn_loop) - [bhv_static_checkered_platform_loop](functions-2.md#bhv_static_checkered_platform_loop) @@ -585,6 +588,7 @@ - [spawn_mist_particles_variable](functions-2.md#spawn_mist_particles_variable) - [spawn_no_exit_star](functions-2.md#spawn_no_exit_star) - [spawn_red_coin_cutscene_star](functions-2.md#spawn_red_coin_cutscene_star) + - [spawn_star_number](functions-2.md#spawn_star_number) - [spawn_triangle_break_particles](functions-2.md#spawn_triangle_break_particles) - [spawn_wind_particles](functions-2.md#spawn_wind_particles) - [tox_box_move](functions-2.md#tox_box_move) diff --git a/docs/lua/structs.md b/docs/lua/structs.md index 063f04eb..f64ac1c1 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -938,22 +938,33 @@ | exitCastleArea | `integer` | | | exitCastleLevel | [enum LevelNum](constants.md#enum-LevelNum) | | | exitCastleWarpNode | `integer` | | +| extendedPauseDisplay | `integer` | | | fixCollisionBugs | `integer` | | -| fixVanishFloors | `boolean` | | +| fixVanishFloors | `integer` | | +| floatingStarDance | `integer` | | | floorLowerLimit | `integer` | | | floorLowerLimitMisc | `integer` | | | floorLowerLimitShadow | `integer` | | +| hudCapTimer | `integer` | | +| hudRedCoinsRadar | `integer` | | +| hudSecretsRadar | `integer` | | | metalCapDuration | `integer` | | | metalCapDurationCotmc | `integer` | | | metalCapSequence | `integer` | | +| mushroom1UpHeal | `integer` | | | pauseExitAnywhere | `boolean` | | +| previewBlueCoins | `integer` | | | pssSlideStarIndex | `integer` | | | pssSlideStarTime | `integer` | | +| respawnBlueCoinsSwitch | `integer` | | +| showStarNumber | `integer` | | | skipCreditsAt | [enum LevelNum](constants.md#enum-LevelNum) | | +| starHeal | `integer` | | | starPositions | [StarPositions](structs.md#StarPositions) | read-only | | vanishCapDuration | `integer` | | | vanishCapDurationVcutm | `integer` | | | vanishCapSequence | `integer` | | +| visibleSecrets | `integer` | | | wingCapDuration | `integer` | | | wingCapDurationTotwc | `integer` | | | wingCapLookUpReq | `integer` | | @@ -1086,7 +1097,6 @@ | unkC4 | `number` | | | usedObj | [Object](structs.md#Object) | | | vel | [Vec3f](structs.md#Vec3f) | read-only | -| visibleToEnemies | `integer` | | | wall | [Surface](structs.md#Surface) | | | wallKickTimer | `integer` | | | wallNormal | [Vec3f](structs.md#Vec3f) | read-only | diff --git a/include/behavior_data.h b/include/behavior_data.h index 58cb9ffc..63f9262c 100644 --- a/include/behavior_data.h +++ b/include/behavior_data.h @@ -238,6 +238,7 @@ extern const BehaviorScript bhvWhitePuff1[]; extern const BehaviorScript bhvWhitePuff2[]; extern const BehaviorScript bhvWhitePuffSmoke2[]; extern const BehaviorScript bhvPurpleSwitchHiddenBoxes[]; +extern const BehaviorScript bhvBlueCoinNumber[]; extern const BehaviorScript bhvBlueCoinSwitch[]; extern const BehaviorScript bhvHiddenBlueCoin[]; extern const BehaviorScript bhvOpenableCageDoor[]; @@ -407,6 +408,7 @@ extern const BehaviorScript bhvMetalCap[]; extern const BehaviorScript bhvNormalCap[]; extern const BehaviorScript bhvVanishCap[]; extern const BehaviorScript bhvStar[]; +extern const BehaviorScript bhvStarNumber[]; extern const BehaviorScript bhvStarSpawnCoordinates[]; extern const BehaviorScript bhvHiddenRedCoinStar[]; extern const BehaviorScript bhvRedCoin[]; diff --git a/include/behavior_table.h b/include/behavior_table.h index a18b6710..15f9145c 100644 --- a/include/behavior_table.h +++ b/include/behavior_table.h @@ -541,6 +541,8 @@ enum BehaviorId { id_bhvYoshi, id_RM_Scroll_Texture, id_editor_Scroll_Texture, + id_bhvBlueCoinNumber, + id_bhvStarNumber, id_bhv_max_count // must be the last in the list }; diff --git a/include/object_constants.h b/include/object_constants.h index e0b33e8b..7c5d1128 100644 --- a/include/object_constants.h +++ b/include/object_constants.h @@ -157,6 +157,7 @@ #define BLUE_COIN_SWITCH_ACT_IDLE 0 #define BLUE_COIN_SWITCH_ACT_RECEDING 1 #define BLUE_COIN_SWITCH_ACT_TICKING 2 + #define BLUE_COIN_SWITCH_ACT_RESPAWNING 3 /* Moving Blue Coin */ /* oAction */ diff --git a/include/object_fields.h b/include/object_fields.h index f87b3574..16612909 100644 --- a/include/object_fields.h +++ b/include/object_fields.h @@ -914,6 +914,7 @@ #define /*0x0F4*/ oStarSpawnDisFromHome OBJECT_FIELD_F32(0x1B) #define /*0x0F8*/ oStarSpawnUnkFC OBJECT_FIELD_F32(0x1D) #define /*0x0FC*/ oStarSpawnExtCutsceneFlags OBJECT_FIELD_S16(0x1E, 0) +#define /*0x100*/ oStarBehavior OBJECT_FIELD_CVPTR(0x1F) /* Hidden Star */ // Secrets/Red Coins diff --git a/src/game/behavior_actions.h b/src/game/behavior_actions.h index 506bece5..d5356944 100644 --- a/src/game/behavior_actions.h +++ b/src/game/behavior_actions.h @@ -225,6 +225,8 @@ void bhv_ship_part_3_loop(void); void bhv_sunken_ship_part_loop(void); void bhv_white_puff_1_loop(void); void bhv_white_puff_2_loop(void); +void bhv_blue_coin_number_loop(void); +void bhv_blue_coin_switch_init(void); void bhv_blue_coin_switch_loop(void); void bhv_hidden_blue_coin_loop(void); void bhv_openable_cage_door_loop(void); @@ -387,6 +389,8 @@ void bhv_metal_cap_loop(void); void bhv_normal_cap_init(void); void bhv_normal_cap_loop(void); void bhv_vanish_cap_init(void); +void bhv_star_number_loop(void); +void spawn_star_number(void); void bhv_collect_star_init(void); void bhv_collect_star_loop(void); void bhv_star_spawn_init(void); diff --git a/src/game/behaviors/blue_coin.inc.c b/src/game/behaviors/blue_coin.inc.c index b52fddd5..ea6cf1c9 100644 --- a/src/game/behaviors/blue_coin.inc.c +++ b/src/game/behaviors/blue_coin.inc.c @@ -32,6 +32,17 @@ void bhv_hidden_blue_coin_loop(void) { o->oAction++; // Set to HIDDEN_BLUE_COIN_ACT_ACTIVE } + // Show blue coins if a Mario is standing on the blue coins switch + cur_obj_disable_rendering(); + if (gLevelValues.previewBlueCoins) { + for (s32 i = 0; i != MAX_PLAYERS; ++i) { + if (gMarioStates[i].marioObj && gMarioStates[i].marioObj->platform == blueCoinSwitch) { + cur_obj_enable_rendering(); + break; + } + } + } + break; case HIDDEN_BLUE_COIN_ACT_ACTIVE: // Become tangible @@ -47,7 +58,12 @@ void bhv_hidden_blue_coin_loop(void) { // After 200 frames of waiting and 20 2-frame blinks (for 240 frames total), // delete the object. if (cur_obj_wait_then_blink(200, 20)) { - obj_mark_for_deletion(o); + if (gLevelValues.respawnBlueCoinsSwitch) { + o->oAction = HIDDEN_BLUE_COIN_ACT_INACTIVE; + cur_obj_unhide(); + } else { + obj_mark_for_deletion(o); + } } break; @@ -56,6 +72,46 @@ void bhv_hidden_blue_coin_loop(void) { o->oInteractStatus = 0; } +/** + * Update function for bhvBlueCoinNumber. + */ +void bhv_blue_coin_number_loop(void) { + + // Check if the blue coins switch still exists + struct Object *blueCoinSwitch = o->oHiddenBlueCoinSwitch; + if (blueCoinSwitch == NULL || blueCoinSwitch->activeFlags == ACTIVE_FLAG_DEACTIVATED || blueCoinSwitch->behavior != smlua_override_behavior(bhvBlueCoinSwitch)) { + obj_mark_for_deletion(o); + return; + } + + // Show the number of blue coins remaining if a Mario is standing on the switch + cur_obj_disable_rendering(); + cur_obj_hide(); + if (gLevelValues.previewBlueCoins) { + for (s32 i = 0; i != MAX_PLAYERS; ++i) { + if (gMarioStates[i].marioObj && gMarioStates[i].marioObj->platform == blueCoinSwitch) { + cur_obj_enable_rendering(); + cur_obj_unhide(); + obj_set_pos(o, blueCoinSwitch->header.gfx.pos[0], blueCoinSwitch->header.gfx.pos[1] + 100.f * blueCoinSwitch->header.gfx.scale[1], blueCoinSwitch->header.gfx.pos[2]); + obj_set_angle(o, 0, 0, 0); + obj_scale(o, 1.f); + o->oAnimState = o->oBehParams2ndByte = count_objects_with_behavior(bhvHiddenBlueCoin); + break; + } + } + } +} + +/** + * Init function for bhvBlueCoinSwitch. + */ +void bhv_blue_coin_switch_init(void) { + struct Object *blueCoinNumber = spawn_object(o, MODEL_NUMBER, bhvBlueCoinNumber); + blueCoinNumber->activeFlags |= ACTIVE_FLAG_INITIATED_TIME_STOP; // to make sure it's updated even during time stop + blueCoinNumber->oHiddenBlueCoinSwitch = o; + o->oHomeY = o->oPosY; +} + /** * Update function for bhvBlueCoinSwitch. */ @@ -67,6 +123,7 @@ void bhv_blue_coin_switch_loop(void) { sync_object_init_field(o, &o->oGravity); sync_object_init_field(o, &o->oTimer); sync_object_init_field(o, &o->oPosY); + sync_object_init_field(o, &o->oHomeY); } // The switch's model is 1/3 size. @@ -128,10 +185,30 @@ void bhv_blue_coin_switch_loop(void) { // Delete the switch (which stops the sound) after the last coin is collected, // or after the coins unload after the 240-frame timer expires. - if (cur_obj_nearest_object_with_behavior(bhvHiddenBlueCoin) == NULL || o->oTimer > 240) { - obj_mark_for_deletion(o); + bool noBlueCoin = cur_obj_nearest_object_with_behavior(bhvHiddenBlueCoin) == NULL; + if (noBlueCoin || o->oTimer > 240) { + if (!noBlueCoin && gLevelValues.respawnBlueCoinsSwitch) { + o->oAction = BLUE_COIN_SWITCH_ACT_RESPAWNING; + o->oPosY = o->oHomeY - 120.0f; + o->oVelY = 20.0f; + o->oGravity = 0.0f; + cur_obj_play_sound_2(SOUND_GENERAL_SWITCH_DOOR_OPEN); + network_send_object(o); + } else { + obj_mark_for_deletion(o); + } } + break; + case BLUE_COIN_SWITCH_ACT_RESPAWNING: + cur_obj_move_using_fvel_and_gravity(); + if (o->oPosY >= o->oHomeY) { + o->oPosY = o->oHomeY; + o->oAction = BLUE_COIN_SWITCH_ACT_IDLE; + } + load_object_collision_model(); + cur_obj_unhide(); + break; } } diff --git a/src/game/behaviors/hidden_star.inc.c b/src/game/behaviors/hidden_star.inc.c index 5d97d24b..2d4a269a 100644 --- a/src/game/behaviors/hidden_star.inc.c +++ b/src/game/behaviors/hidden_star.inc.c @@ -89,6 +89,10 @@ void bhv_hidden_star_trigger_loop(void) { network_send_collect_item(o); } } + if (gLevelValues.visibleSecrets) { + obj_set_model(o, MODEL_PURPLE_MARBLE); + obj_set_billboard(o); + } } void bhv_bowser_course_red_coin_star_loop(void) { diff --git a/src/game/behaviors/mushroom_1up.inc.c b/src/game/behaviors/mushroom_1up.inc.c index 92d5c3dd..7f72a2dc 100644 --- a/src/game/behaviors/mushroom_1up.inc.c +++ b/src/game/behaviors/mushroom_1up.inc.c @@ -6,6 +6,10 @@ void bhv_1up_interact(void) { play_sound(SOUND_GENERAL_COLLECT_1UP, gGlobalSoundSource); marioState->numLives++; o->activeFlags = ACTIVE_FLAG_DEACTIVATED; + if (gLevelValues.mushroom1UpHeal) { + marioState->healCounter = 31; + marioState->hurtCounter = 0; + } #ifdef VERSION_SH queue_rumble_data(5, 80); #endif diff --git a/src/game/behaviors/sparkle_spawn_star.inc.c b/src/game/behaviors/sparkle_spawn_star.inc.c index 55b260bc..ef31f16b 100644 --- a/src/game/behaviors/sparkle_spawn_star.inc.c +++ b/src/game/behaviors/sparkle_spawn_star.inc.c @@ -30,6 +30,7 @@ void bhv_spawned_star_init(void) { u32 models[] = { MODEL_STAR }; network_send_spawn_objects(spawn_objects, models, 1); } + spawn_star_number(); } void set_sparkle_spawn_star_hitbox(void) { @@ -142,6 +143,7 @@ void bhv_spawned_star_loop(void) { cur_obj_move_using_fvel_and_gravity(); o->oFaceAngleYaw += o->oAngleVelYaw; o->oInteractStatus = 0; + spawn_star_number(); } void bhv_spawn_star_no_level_exit(struct Object* object, u32 params, u8 networkSendEvent) { diff --git a/src/game/behaviors/spawn_star.inc.c b/src/game/behaviors/spawn_star.inc.c index 203e29e0..011c443f 100644 --- a/src/game/behaviors/spawn_star.inc.c +++ b/src/game/behaviors/spawn_star.inc.c @@ -1,5 +1,47 @@ // spawn_default_star.c.inc +void bhv_star_number_loop(void) { + + // Check if the star still exists + struct Object *star = o->parentObj; + const BehaviorScript *starBhv = (const BehaviorScript *) o->oStarBehavior; + if (star == NULL || star->activeFlags == ACTIVE_FLAG_DEACTIVATED || starBhv != smlua_override_behavior(star->behavior)) { + obj_mark_for_deletion(o); + return; + } + + // Show the star number + if (gLevelValues.showStarNumber) { + obj_set_pos(o, star->header.gfx.pos[0], star->header.gfx.pos[1] + 150.f * star->header.gfx.scale[1], star->header.gfx.pos[2]); + obj_set_angle(o, 0, 0, 0); + obj_scale(o, 1.f); + o->oAnimState = o->oBehParams2ndByte = ((star->oBehParams >> 24) & 0xFF) + 1; + o->header.gfx.node.flags = star->header.gfx.node.flags; + } else { + cur_obj_disable_rendering(); + cur_obj_hide(); + } +} + +void spawn_star_number(void) { + + // Check if the star already has a number + struct Object *starNumber = obj_get_first_with_behavior_id(id_bhvStarNumber); + for (; starNumber; starNumber = obj_get_next_with_same_behavior_id(starNumber)) { + if (starNumber->parentObj == o) { + break; + } + } + + // If not, spawn a number + if (!starNumber) { + starNumber = spawn_object(o, MODEL_NUMBER, bhvStarNumber); + starNumber->parentObj = o; + starNumber->activeFlags |= ACTIVE_FLAG_INITIATED_TIME_STOP; // to make sure it's updated even during time stop + starNumber->oStarBehavior = (const void *) smlua_override_behavior(o->behavior); + } +} + static struct ObjectHitbox sCollectStarHitbox = { /* interactType: */ INTERACT_STAR_OR_KEY, /* downOffset: */ 0, @@ -25,6 +67,7 @@ void bhv_collect_star_init(void) { } obj_set_hitbox(o, &sCollectStarHitbox); + spawn_star_number(); } void bhv_collect_star_loop(void) { @@ -34,6 +77,7 @@ void bhv_collect_star_loop(void) { mark_obj_for_deletion(o); o->oInteractStatus = 0; } + spawn_star_number(); } void bhv_star_spawn_init(void) { @@ -54,6 +98,7 @@ void bhv_star_spawn_init(void) { o->activeFlags |= ACTIVE_FLAG_INITIATED_TIME_STOP; } cur_obj_become_intangible(); + spawn_star_number(); } void bhv_star_spawn_loop(void) { @@ -112,6 +157,7 @@ void bhv_star_spawn_loop(void) { } break; } + spawn_star_number(); } struct Object *spawn_star(struct Object *obj, f32 x, f32 y, f32 z) { diff --git a/src/game/camera.c b/src/game/camera.c index 96cfcc58..bc775111 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -5331,7 +5331,11 @@ u8 get_cutscene_from_mario_status(struct Camera *c) { cutscene = determine_dance_cutscene(c); break; case ACT_STAR_DANCE_WATER: - cutscene = determine_dance_cutscene(c); + if (gMarioStates[0].actionArg & 1) { // No exit + cutscene = CUTSCENE_DANCE_DEFAULT; + } else { + cutscene = determine_dance_cutscene(c); + } break; case ACT_STAR_DANCE_NO_EXIT: cutscene = CUTSCENE_DANCE_DEFAULT; diff --git a/src/game/hardcoded.c b/src/game/hardcoded.c index 59acaf00..8bb10077 100644 --- a/src/game/hardcoded.c +++ b/src/game/hardcoded.c @@ -44,6 +44,17 @@ extern Trajectory sThiTinyMetalBallTraj[]; struct LevelValues gDefaultLevelValues = { .fixCollisionBugs = 0, .fixVanishFloors = 0, + .hudCapTimer = 0, + .hudRedCoinsRadar = 0, + .hudSecretsRadar = 0, + .starHeal = 0, + .mushroom1UpHeal = 0, + .floatingStarDance = 0, + .previewBlueCoins = 0, + .respawnBlueCoinsSwitch = 0, + .visibleSecrets = 0, + .showStarNumber = 0, + .extendedPauseDisplay = 0, .entryLevel = LEVEL_CASTLE_GROUNDS, .exitCastleLevel = LEVEL_CASTLE, .exitCastleArea = 1, diff --git a/src/game/hardcoded.h b/src/game/hardcoded.h index 2b4434a6..20f6fa73 100644 --- a/src/game/hardcoded.h +++ b/src/game/hardcoded.h @@ -41,7 +41,18 @@ struct StarPositions { struct LevelValues { u8 fixCollisionBugs; u8 wingCapLookUpReq; - bool fixVanishFloors; + u8 fixVanishFloors; + u8 hudCapTimer; + u8 hudRedCoinsRadar; + u8 hudSecretsRadar; + u8 starHeal; + u8 mushroom1UpHeal; + u8 floatingStarDance; + u8 previewBlueCoins; + u8 respawnBlueCoinsSwitch; + u8 visibleSecrets; + u8 showStarNumber; + u8 extendedPauseDisplay; enum LevelNum entryLevel; enum LevelNum exitCastleLevel; s16 exitCastleArea; diff --git a/src/game/hud.c b/src/game/hud.c index e1a94156..3d66dcab 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -8,6 +8,7 @@ #include "level_update.h" #include "camera.h" #include "print.h" +#include "engine/math_util.h" #include "engine/surface_load.h" #include "ingame_menu.h" #include "hud.h" @@ -15,9 +16,13 @@ #include "area.h" #include "save_file.h" #include "print.h" +#include "hardcoded.h" #include "pc/configfile.h" #include "pc/network/network.h" #include "pc/utils/misc.h" +#include "pc/lua/smlua.h" +#include "pc/lua/utils/smlua_obj_utils.h" +#include "data/dynos_mgr_builtin_externs.h" extern bool gDjuiInMainMenu; u8 gOverrideHideHud; @@ -313,6 +318,131 @@ void render_hud_mario_lives(void) { print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(54), HUD_TOP_Y, "%d", gHudDisplay.lives); } +static void render_hud_icon(Vtx *vtx, const u8 *texture, u32 fmt, u32 siz, s32 texW, s32 texH, s32 x, s32 y, s32 w, s32 h, s32 tileX, s32 tileY, s32 tileW, s32 tileH) { + create_dl_ortho_matrix(); + if (!vtx) { + vtx = alloc_display_list(sizeof(Vtx) * 4); + vtx[0] = (Vtx) {{{ x, y - h, 0 }, 0, { tileX << 5, (tileY + tileH) << 5 }, { 0xFF, 0xFF, 0xFF, 0xFF }}}; + vtx[1] = (Vtx) {{{ x + w, y - h, 0 }, 0, { (tileX + tileW) << 5, (tileY + tileH) << 5 }, { 0xFF, 0xFF, 0xFF, 0xFF }}}; + vtx[2] = (Vtx) {{{ x + w, y, 0 }, 0, { (tileX + tileW) << 5, tileY << 5 }, { 0xFF, 0xFF, 0xFF, 0xFF }}}; + vtx[3] = (Vtx) {{{ x, y, 0 }, 0, { tileX << 5, tileY << 5 }, { 0xFF, 0xFF, 0xFF, 0xFF }}}; + } + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); + gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetTextureFilter(gDisplayListHead++, G_TF_POINT); + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + switch (siz) { + case G_IM_SIZ_4b: gDPLoadTextureBlock(gDisplayListHead++, texture, fmt, G_IM_SIZ_4b, texW, texH, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); break; + case G_IM_SIZ_8b: gDPLoadTextureBlock(gDisplayListHead++, texture, fmt, G_IM_SIZ_8b, texW, texH, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); break; + case G_IM_SIZ_16b: gDPLoadTextureBlock(gDisplayListHead++, texture, fmt, G_IM_SIZ_16b, texW, texH, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); break; + case G_IM_SIZ_32b: gDPLoadTextureBlock(gDisplayListHead++, texture, fmt, G_IM_SIZ_32b, texW, texH, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); break; + } + gSPVertex(gDisplayListHead++, vtx, 4, 0); + gSP2Triangles(gDisplayListHead++, 0, 1, 2, 0x0, 0, 2, 3, 0x0); + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); + gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); +} + +/** + * Renders the number of seconds remaining of the current cap power-ups. + */ +void render_hud_cap_timer(void) { + static const u8 *sHudCapIcons[][4] = { + [MARIO_WING_CAP ] = { exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08015E28 }, + [ MARIO_METAL_CAP ] = { exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08014628 }, + [ MARIO_VANISH_CAP] = { exclamation_box_seg8_texture_08012E28, exclamation_box_seg8_texture_08012E28, exclamation_box_seg8_texture_08012E28, exclamation_box_seg8_texture_08012E28 }, + [MARIO_WING_CAP | MARIO_METAL_CAP ] = { exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08014628 }, + [MARIO_WING_CAP | MARIO_VANISH_CAP] = { exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08012E28, exclamation_box_seg8_texture_08012E28 }, + [ MARIO_METAL_CAP | MARIO_VANISH_CAP] = { exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08012E28, exclamation_box_seg8_texture_08012E28 }, + [MARIO_WING_CAP | MARIO_METAL_CAP | MARIO_VANISH_CAP] = { exclamation_box_seg8_texture_08015E28, exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08014628, exclamation_box_seg8_texture_08012E28 }, + }; + struct MarioState *m = &gMarioStates[0]; + u32 capFlags = m->flags & MARIO_SPECIAL_CAPS; + if (capFlags) { + s32 capTimer = m->capTimer; + if (capTimer > 0) { + s32 capSeconds = (capTimer + 29) / 30; + const u8 **capIcons = sHudCapIcons[capFlags]; + gDPSetEnvColor(gDisplayListHead++, 0xFF, 0xFF, 0xFF, 0xFF); + render_hud_icon(NULL, capIcons[0], G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(22), HUD_TOP_Y - 4, 5, 16, 0, 0, 10, 32); + render_hud_icon(NULL, capIcons[1], G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(27), HUD_TOP_Y - 4, 3, 16, 10, 0, 6, 32); + render_hud_icon(NULL, capIcons[2], G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(30), HUD_TOP_Y - 4, 3, 16, 16, 0, 6, 32); + render_hud_icon(NULL, capIcons[3], G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(33), HUD_TOP_Y - 4, 5, 16, 22, 0, 10, 32); + print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(38), HUD_TOP_Y - 20, "*"); // 'X' glyph + print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(54), HUD_TOP_Y - 20, "%d", capSeconds); + } + } +} + +static void render_hud_radar(struct MarioState *m, struct Object *target, const u8 *iconTexture, u32 fmt, u32 siz, s32 texW, s32 texH, s32 x, s32 y, s32 tileX, s32 tileY, s32 tileW, s32 tileH, u8 r, u8 g, u8 b) { + + // Icon + gDPSetEnvColor(gDisplayListHead++, r, g, b, 0xFF); + render_hud_icon(NULL, iconTexture, fmt, siz, texW, texH, x, y + 2, 12, 12, tileX, tileY, tileW, tileH); + + // Direction + s16 angle = atan2s( + target->oPosZ - m->pos[2], + target->oPosX - m->pos[0] + ) - atan2s( + m->pos[2] - gCamera->pos[2], + m->pos[0] - gCamera->pos[0] + ); + f32 invSqrt2 = 1.f / sqrtf(2.f); + Vtx *vtx = alloc_display_list(sizeof(Vtx) * 4); + for (s32 i = 0; i != 4; ++i) { + s16 a = angle + ((i * 0x4000) - 0x6000); + vtx[i] = (Vtx) { { { + x + 6 + 12 * coss(angle + 0x4000) + 8 * invSqrt2 * coss(a), + y - 4 + 12 * sins(angle + 0x4000) + 8 * invSqrt2 * sins(a), 0, + }, 0, { + 256 * (((i + 1) / 2) % 2), // 0, 256, 256, 0 + 256 * (((i + 2) / 2) % 2), // 256, 256, 0, 0 + }, { 0xFF, 0xFF, 0xFF, 0xFF } } }; + } + gDPSetEnvColor(gDisplayListHead++, 0xFF, 0xFF, 0xFF, 0xFF); + render_hud_icon(vtx, texture_hud_char_arrow_up, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 8, 0, 0, 8, 8, 0, 0, 8, 8); + + // Distance + s32 dist = vec3f_dist(&target->oPosX, m->pos); + print_text_fmt_int(x + 24, y - 12, "%d", dist); +} + +/** + * Renders the direction and distance to the nearest red coin. + */ +void render_hud_red_coins_and_secrets_radar(void) { + struct MarioState *m = &gMarioStates[0]; + if (m->marioObj && gCamera) { + s32 y = 31; + + // Red coins radar + if (gLevelValues.hudRedCoinsRadar) { + static const u8 *sRedCoinTextures[] = { + coin_seg3_texture_03005780, + coin_seg3_texture_03005F80, + coin_seg3_texture_03006780, + coin_seg3_texture_03006F80, + }; + struct Object *redCoin = obj_get_nearest_object_with_behavior_id(m->marioObj, id_bhvRedCoin); + if (redCoin) { + render_hud_radar(m, redCoin, sRedCoinTextures[(gGlobalTimer / 2) % 4], G_IM_FMT_IA, G_IM_SIZ_16b, 32, 32, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(24), y, 0, 0, 32, 32, 0xFF, 0x00, 0x00); + y += 30; + } + } + + // Secrets radar + if (gLevelValues.hudSecretsRadar) { + struct Object *secret = obj_get_nearest_object_with_behavior_id(m->marioObj, id_bhvHiddenStarTrigger); + if (secret) { + render_hud_radar(m, secret, texture_hud_char_S, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 16, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(24), y, 0, 0, 14, 16, 0xFF, 0xFF, 0xFF); + y += 30; + } + } + } +} + /** * Renders the amount of coins collected. */ @@ -502,6 +632,14 @@ void render_hud(void) { render_hud_mario_lives(); } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_LIVES && showHud && gLevelValues.hudCapTimer) { + render_hud_cap_timer(); + } + + if (hudDisplayFlags & HUD_DISPLAY_FLAG_LIVES && showHud) { + render_hud_red_coins_and_secrets_radar(); + } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT && showHud) { render_hud_coins(); } diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index 39ea54e9..1e94b619 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -26,10 +26,12 @@ #include "text_strings.h" #include "types.h" #include "macros.h" +#include "hardcoded.h" #include "pc/cheats.h" #include "pc/network/network.h" #include "pc/djui/djui.h" #include "pc/utils/misc.h" +#include "data/dynos_mgr_builtin_externs.h" #ifdef BETTERCAMERA #include "bettercamera.h" #endif @@ -944,6 +946,14 @@ void handle_menu_scrolling(s8 scrollDirection, s8 *currentIndex, s8 minIndex, s8 } } + // Clamp currentIndex to prevent out of bounds access + if (currentIndex[0] < minIndex) { + currentIndex[0] = minIndex; + } + if (currentIndex[0] > maxIndex) { + currentIndex[0] = maxIndex; + } + if (gMenuHoldKeyTimer == 10) { gMenuHoldKeyTimer = 8; gMenuHoldKeyIndex = 0; @@ -2661,6 +2671,25 @@ void render_pause_castle_menu_box(s16 x, s16 y) { gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } +void render_pause_castle_menu_box_extended(s16 x, s16 y) { + create_dl_translation_matrix(MENU_MTX_PUSH, x - 98, y - 32, 0); + create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.5f, 0.8f, 1.0f); + gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 105); + gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box); + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); + + create_dl_translation_matrix(MENU_MTX_PUSH, x + 6, y - 28, 0); + create_dl_rotation_matrix(MENU_MTX_NOPUSH, DEFAULT_DIALOG_BOX_ANGLE, 0, 0, 1.0f); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); + gSPDisplayList(gDisplayListHead++, dl_draw_triangle); + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); + + create_dl_translation_matrix(MENU_MTX_PUSH, x - 9, y - 101, 0); + create_dl_rotation_matrix(MENU_MTX_NOPUSH, 270.0f, 0, 0, 1.0f); + gSPDisplayList(gDisplayListHead++, dl_draw_triangle); + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); +} + void highlight_last_course_complete_stars(void) { u8 courseDone; @@ -2824,6 +2853,186 @@ void render_pause_castle_main_strings(s16 x, s16 y) { gSPDisplayList(gDisplayListHead++, dl_ia_text_end); } +#define INDEX_CASTLE_STARS (COURSE_COUNT) +#define INDEX_FLAGS (COURSE_COUNT + 1) +#define INDEX_MIN (-1) +#define INDEX_MAX (INDEX_FLAGS + 1) + +static u32 pause_castle_get_stars(s32 index) { + + // Main courses (0-14), Secret courses (15-24) + if (index >= 0 && index < INDEX_CASTLE_STARS) { + return save_file_get_star_flags(gCurrSaveFileNum - 1, index); + } + + // Castle stars (25) + if (index == INDEX_CASTLE_STARS) { + return save_file_get_star_flags(gCurrSaveFileNum - 1, -1); + } + + // Flags (26) + if (index == INDEX_FLAGS) { + return save_file_get_flags(); + } + + return 0; +} + +static void render_pause_castle_course_name(const u8 *courseName, s16 x, s16 y) { + s16 width = 0; + for (const u8 *c = courseName; *c != DIALOG_CHAR_TERMINATOR; c++) { + width += gDialogCharWidths[*c]; + } + print_generic_string(x - width / 2, y, courseName); +} + +static void render_pause_castle_flag_icon(const u8 *texture, s16 texW, s16 texH, s16 x, s16 y, s16 w, s16 h) { + gDPLoadTextureBlock(gDisplayListHead++, texture, G_IM_FMT_RGBA, G_IM_SIZ_16b, texW, texH, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); + gSPTextureRectangle(gDisplayListHead++, (x) << 2, (SCREEN_HEIGHT - h - y) << 2, (x + w) << 2, (SCREEN_HEIGHT - y) << 2, G_TX_RENDERTILE, 0, 0, ((0x400 * texW) / w), ((0x400 * texH) / h)); +} + +static void render_pause_castle_flag(s16 x, s16 y, u32 flag) { + if (save_file_get_flags() & flag) { + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); + } else { + gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gDialogTextAlpha / 3); + } + switch (flag) { + case SAVE_FLAG_HAVE_WING_CAP: + render_pause_castle_flag_icon(exclamation_box_seg8_texture_08015E28, 32, 32, x, y, 12, 12); + break; + + case SAVE_FLAG_HAVE_METAL_CAP: + render_pause_castle_flag_icon(exclamation_box_seg8_texture_08014628, 32, 32, x, y, 12, 12); + break; + + case SAVE_FLAG_HAVE_VANISH_CAP: + render_pause_castle_flag_icon(exclamation_box_seg8_texture_08012E28, 32, 32, x, y, 12, 12); + break; + + case SAVE_FLAG_HAVE_KEY_1 | SAVE_FLAG_UNLOCKED_BASEMENT_DOOR: + case SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR: + render_pause_castle_flag_icon(bowser_key_left_texture, 32, 64, x, y, 6, 12); + render_pause_castle_flag_icon(bowser_key_right_texture, 32, 64, x + 6, y, 6, 12); + break; + } + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); +} + +static void render_pause_castle_course_stars_extended(s16 x, s16 y) { + bool isMainCourse = COURSE_IS_MAIN_COURSE(gDialogLineNum + 1); + u32 stars = pause_castle_get_stars(gDialogLineNum); + u8 str[32]; + + // Build the stars string + s32 lastCollectedStar = 0; + for (s32 i = 0; i != (isMainCourse ? 6 : 7); ++i) { + if (stars & (1 << i)) { + str[2 * i] = DIALOG_CHAR_STAR_FILLED; + lastCollectedStar = i + 1; + } else { + str[2 * i] = DIALOG_CHAR_STAR_OPEN; + } + str[2 * i + 1] = DIALOG_CHAR_SPACE; + str[2 * i + 2] = DIALOG_CHAR_TERMINATOR; + } + + // Hide the not collected ones after the last collected for secret courses + if (!isMainCourse) { + str[2 * lastCollectedStar] = DIALOG_CHAR_TERMINATOR; + } + + // Render the 100 coins star next to the coin counter for main courses + if (isMainCourse && (stars & 0x40)) { + const u8 textStar[] = { TEXT_STAR }; + print_generic_string(x + 89, y - 5, textStar); + } + + // Render the stars + print_generic_string(x + 14, y + 13, str); +} + +void render_pause_castle_main_strings_extended(s16 x, s16 y) { + + // Main courses (0-14), Secret courses (15-24), Castle stars (25), Flags (26) + // Indices -1 and 26 are used to loop back respectively to Flags and Course 1 + s8 prevIndex = gDialogLineNum; + handle_menu_scrolling(MENU_SCROLL_VERTICAL, &gDialogLineNum, INDEX_MIN, INDEX_MAX); + s8 scrollDir = (gDialogLineNum >= prevIndex ? +1 : -1); + if (gDialogLineNum >= INDEX_MAX) { + gDialogLineNum = 0; + scrollDir = +1; + } else if (gDialogLineNum <= INDEX_MIN) { + gDialogLineNum = INDEX_FLAGS; + scrollDir = -1; + } + + // Skip courses with 0 star collected + if (gDialogLineNum < INDEX_CASTLE_STARS) { + while (!pause_castle_get_stars(gDialogLineNum)) { + gDialogLineNum += scrollDir; + if (gDialogLineNum >= INDEX_CASTLE_STARS) { + gDialogLineNum = INDEX_CASTLE_STARS; + break; + } + if (gDialogLineNum <= INDEX_MIN) { + gDialogLineNum = INDEX_FLAGS; + break; + } + } + } + + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); + + // Main courses (0-14) + if (gDialogLineNum < COURSE_STAGES_COUNT) { + const u8 *courseName = seg2_course_name_table[gDialogLineNum]; + const u8 textCoin[] = { TEXT_COIN_X }; + u8 textCoinCount[8]; + render_pause_castle_course_name(courseName, 160, y + 30); + render_pause_castle_course_stars_extended(x + 20, y); + print_generic_string(x + 54, y - 5, textCoin); + int_to_str(save_file_get_course_coin_score(gCurrSaveFileNum - 1, gDialogLineNum), textCoinCount); + print_generic_string(x + 74, y - 5, textCoinCount); + } + + // Secret courses (15-24) + else if (gDialogLineNum >= COURSE_STAGES_COUNT && gDialogLineNum < INDEX_CASTLE_STARS) { + const u8 *courseName = seg2_course_name_table[gDialogLineNum]; + render_pause_castle_course_name(courseName + 3, 160, y + 30); + render_pause_castle_course_stars_extended(x + 20, y); + } + + // Castle stars (25) + else if (gDialogLineNum == INDEX_CASTLE_STARS) { + const u8 *courseName = seg2_course_name_table[COURSE_MAX]; + const u8 textStar[] = { TEXT_STAR_X }; + u8 textStarCount[8]; + render_pause_castle_course_name(courseName + 3, 160, y + 30); + print_generic_string(x + 60, y + 13, textStar); + int_to_str(save_file_get_course_star_count(gCurrSaveFileNum - 1, -1), textStarCount); + print_generic_string(x + 80, y + 13, textStarCount); + } + + // Flags (26) + else if (gDialogLineNum == INDEX_FLAGS) { + const u8 textFlags[] = { ASCII_TO_DIALOG('P'), ASCII_TO_DIALOG('R'), ASCII_TO_DIALOG('O'), ASCII_TO_DIALOG('G'), ASCII_TO_DIALOG('R'), ASCII_TO_DIALOG('E'), ASCII_TO_DIALOG('S'), ASCII_TO_DIALOG('S'), DIALOG_CHAR_TERMINATOR }; + const u8 textCaps[] = { ASCII_TO_DIALOG('C'), ASCII_TO_DIALOG('A'), ASCII_TO_DIALOG('P'), ASCII_TO_DIALOG('S'), 0xE6, DIALOG_CHAR_TERMINATOR }; + const u8 textKeys[] = { ASCII_TO_DIALOG('K'), ASCII_TO_DIALOG('E'), ASCII_TO_DIALOG('Y'), ASCII_TO_DIALOG('S'), 0xE6, DIALOG_CHAR_TERMINATOR }; + render_pause_castle_course_name(textFlags, 160, y + 30); + print_generic_string(x + 45, y + 13, textCaps); + render_pause_castle_flag(x + 80, y + 15, SAVE_FLAG_HAVE_WING_CAP); + render_pause_castle_flag(x + 96, y + 15, SAVE_FLAG_HAVE_METAL_CAP); + render_pause_castle_flag(x + 112, y + 15, SAVE_FLAG_HAVE_VANISH_CAP); + print_generic_string(x + 45, y - 5, textKeys); + render_pause_castle_flag(x + 80, y - 3, SAVE_FLAG_HAVE_KEY_1 | SAVE_FLAG_UNLOCKED_BASEMENT_DOOR); + render_pause_castle_flag(x + 96, y - 3, SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR); + } + + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); +} + s8 gCourseCompleteCoinsEqual = 0; s32 gCourseDoneMenuTimer = 0; s32 gCourseCompleteCoins = 0; @@ -2888,8 +3097,13 @@ s16 render_pause_courses_and_castle(void) { case DIALOG_STATE_HORIZONTAL: shade_screen(); print_hud_pause_colorful_str(); - render_pause_castle_menu_box(160, 143); - render_pause_castle_main_strings(104, 60); + if (gLevelValues.extendedPauseDisplay) { + render_pause_castle_menu_box_extended(160, 143); + render_pause_castle_main_strings_extended(84, 60); + } else { + render_pause_castle_menu_box(160, 143); + render_pause_castle_main_strings(104, 60); + } #ifdef VERSION_EU if (gPlayer1Controller->buttonPressed & (A_BUTTON | Z_TRIG | START_BUTTON)) diff --git a/src/game/interaction.c b/src/game/interaction.c index a452541b..a65b6ece 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -909,6 +909,11 @@ u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct O } } + if (gLevelValues.starHeal) { + m->healCounter = 31; + m->hurtCounter = 0; + } + if (noExit) { starGrabAction = ACT_STAR_DANCE_NO_EXIT; } @@ -923,6 +928,9 @@ u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct O if (m->action & ACT_FLAG_AIR) { starGrabAction = ACT_FALL_AFTER_STAR_GRAB; + if (gLevelValues.floatingStarDance && m->pos[1] - m->floorHeight > 1000) { + starGrabAction = ACT_STAR_DANCE_WATER; + } } for (s32 i = 0; i < MAX_PLAYERS; i++) { diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 68c5a0e0..f94a743f 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -719,7 +719,7 @@ void general_star_dance_handler(struct MarioState *m, s32 isInWater) { : gBehaviorValues.dialogs.CollectedStarDialog); m->actionState = 1; } else { - set_mario_action(m, isInWater ? ACT_WATER_IDLE : ACT_IDLE, 0); + set_mario_action(m, isInWater ? ACT_WATER_IDLE : (m->pos[1] <= m->floorHeight ? ACT_IDLE : ACT_FREEFALL), 0); } break; } @@ -738,7 +738,7 @@ void general_star_dance_handler(struct MarioState *m, s32 isInWater) { // look up for dialog set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID); } else { - set_mario_action(m, isInWater ? ACT_WATER_IDLE : ACT_IDLE, 0); + set_mario_action(m, isInWater ? ACT_WATER_IDLE : (m->pos[1] <= m->floorHeight ? ACT_IDLE : ACT_FREEFALL), 0); } if (gServerSettings.stayInLevelAfterStar) { soft_reset_camera(m->area->camera); @@ -766,7 +766,7 @@ s32 act_star_dance_water(struct MarioState *m) { set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_WATER_STAR_DANCE : MARIO_ANIM_WATER_STAR_DANCE); vec3f_copy(m->marioObj->header.gfx.pos, m->pos); vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); - general_star_dance_handler(m, 1); + general_star_dance_handler(m, m->pos[1] < m->waterLevel - 100); if (m->actionState != 2 && m->actionTimer >= 62) { m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN; } diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index f959dd8d..ad443fbe 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -36,7 +36,9 @@ #include "pc/network/network.h" #include "pc/network/network_player.h" #include "pc/network/reservation_area.h" +#include "pc/lua/smlua.h" #include "pc/lua/utils/smlua_model_utils.h" +#include "pc/lua/utils/smlua_obj_utils.h" #include "game/rng_position.h" #include "rumble_init.h" diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index a0c02515..ac43d332 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -733,7 +733,7 @@ static struct LuaObjectField sLakituStateFields[LUA_LAKITU_STATE_FIELD_COUNT] = { "yaw", LVT_S16, offsetof(struct LakituState, yaw), false, LOT_NONE }, }; -#define LUA_LEVEL_VALUES_FIELD_COUNT 27 +#define LUA_LEVEL_VALUES_FIELD_COUNT 38 static struct LuaObjectField sLevelValuesFields[LUA_LEVEL_VALUES_FIELD_COUNT] = { { "cellHeightLimit", LVT_S16, offsetof(struct LevelValues, cellHeightLimit), false, LOT_NONE }, { "coinsRequiredForCoinStar", LVT_S16, offsetof(struct LevelValues, coinsRequiredForCoinStar), false, LOT_NONE }, @@ -742,22 +742,33 @@ static struct LuaObjectField sLevelValuesFields[LUA_LEVEL_VALUES_FIELD_COUNT] = { "exitCastleArea", LVT_S16, offsetof(struct LevelValues, exitCastleArea), false, LOT_NONE }, { "exitCastleLevel", LVT_S32, offsetof(struct LevelValues, exitCastleLevel), false, LOT_NONE }, { "exitCastleWarpNode", LVT_U8, offsetof(struct LevelValues, exitCastleWarpNode), false, LOT_NONE }, + { "extendedPauseDisplay", LVT_U8, offsetof(struct LevelValues, extendedPauseDisplay), false, LOT_NONE }, { "fixCollisionBugs", LVT_U8, offsetof(struct LevelValues, fixCollisionBugs), false, LOT_NONE }, - { "fixVanishFloors", LVT_BOOL, offsetof(struct LevelValues, fixVanishFloors), false, LOT_NONE }, + { "fixVanishFloors", LVT_U8, offsetof(struct LevelValues, fixVanishFloors), false, LOT_NONE }, + { "floatingStarDance", LVT_U8, offsetof(struct LevelValues, floatingStarDance), false, LOT_NONE }, { "floorLowerLimit", LVT_S16, offsetof(struct LevelValues, floorLowerLimit), false, LOT_NONE }, { "floorLowerLimitMisc", LVT_S16, offsetof(struct LevelValues, floorLowerLimitMisc), false, LOT_NONE }, { "floorLowerLimitShadow", LVT_S16, offsetof(struct LevelValues, floorLowerLimitShadow), false, LOT_NONE }, + { "hudCapTimer", LVT_U8, offsetof(struct LevelValues, hudCapTimer), false, LOT_NONE }, + { "hudRedCoinsRadar", LVT_U8, offsetof(struct LevelValues, hudRedCoinsRadar), false, LOT_NONE }, + { "hudSecretsRadar", LVT_U8, offsetof(struct LevelValues, hudSecretsRadar), false, LOT_NONE }, { "metalCapDuration", LVT_U16, offsetof(struct LevelValues, metalCapDuration), false, LOT_NONE }, { "metalCapDurationCotmc", LVT_U16, offsetof(struct LevelValues, metalCapDurationCotmc), false, LOT_NONE }, { "metalCapSequence", LVT_U8, offsetof(struct LevelValues, metalCapSequence), false, LOT_NONE }, + { "mushroom1UpHeal", LVT_U8, offsetof(struct LevelValues, mushroom1UpHeal), false, LOT_NONE }, { "pauseExitAnywhere", LVT_BOOL, offsetof(struct LevelValues, pauseExitAnywhere), false, LOT_NONE }, + { "previewBlueCoins", LVT_U8, offsetof(struct LevelValues, previewBlueCoins), false, LOT_NONE }, { "pssSlideStarIndex", LVT_U8, offsetof(struct LevelValues, pssSlideStarIndex), false, LOT_NONE }, { "pssSlideStarTime", LVT_U16, offsetof(struct LevelValues, pssSlideStarTime), false, LOT_NONE }, + { "respawnBlueCoinsSwitch", LVT_U8, offsetof(struct LevelValues, respawnBlueCoinsSwitch), false, LOT_NONE }, + { "showStarNumber", LVT_U8, offsetof(struct LevelValues, showStarNumber), false, LOT_NONE }, { "skipCreditsAt", LVT_S32, offsetof(struct LevelValues, skipCreditsAt), false, LOT_NONE }, + { "starHeal", LVT_U8, offsetof(struct LevelValues, starHeal), false, LOT_NONE }, { "starPositions", LVT_COBJECT, offsetof(struct LevelValues, starPositions), true, LOT_STARPOSITIONS }, { "vanishCapDuration", LVT_U16, offsetof(struct LevelValues, vanishCapDuration), false, LOT_NONE }, { "vanishCapDurationVcutm", LVT_U16, offsetof(struct LevelValues, vanishCapDurationVcutm), false, LOT_NONE }, { "vanishCapSequence", LVT_U8, offsetof(struct LevelValues, vanishCapSequence), false, LOT_NONE }, + { "visibleSecrets", LVT_U8, offsetof(struct LevelValues, visibleSecrets), false, LOT_NONE }, { "wingCapDuration", LVT_U16, offsetof(struct LevelValues, wingCapDuration), false, LOT_NONE }, { "wingCapDurationTotwc", LVT_U16, offsetof(struct LevelValues, wingCapDurationTotwc), false, LOT_NONE }, { "wingCapLookUpReq", LVT_U8, offsetof(struct LevelValues, wingCapLookUpReq), false, LOT_NONE }, @@ -800,7 +811,7 @@ static struct LuaObjectField sMarioBodyStateFields[LUA_MARIO_BODY_STATE_FIELD_CO { "wingFlutter", LVT_S8, offsetof(struct MarioBodyState, wingFlutter), false, LOT_NONE }, }; -#define LUA_MARIO_STATE_FIELD_COUNT 77 +#define LUA_MARIO_STATE_FIELD_COUNT 76 static struct LuaObjectField sMarioStateFields[LUA_MARIO_STATE_FIELD_COUNT] = { { "action", LVT_U32, offsetof(struct MarioState, action), false, LOT_NONE }, { "actionArg", LVT_U32, offsetof(struct MarioState, actionArg), false, LOT_NONE }, @@ -873,7 +884,6 @@ static struct LuaObjectField sMarioStateFields[LUA_MARIO_STATE_FIELD_COUNT] = { { "unkC4", LVT_F32, offsetof(struct MarioState, unkC4), false, LOT_NONE }, { "usedObj", LVT_COBJECT_P, offsetof(struct MarioState, usedObj), false, LOT_OBJECT }, { "vel", LVT_COBJECT, offsetof(struct MarioState, vel), true, LOT_VEC3F }, - { "visibleToEnemies", LVT_U8, offsetof(struct MarioState, visibleToEnemies), false, LOT_NONE }, { "wall", LVT_COBJECT_P, offsetof(struct MarioState, wall), false, LOT_SURFACE }, { "wallKickTimer", LVT_U8, offsetof(struct MarioState, wallKickTimer), false, LOT_NONE }, { "wallNormal", LVT_COBJECT, offsetof(struct MarioState, wallNormal), true, LOT_VEC3F }, @@ -1544,6 +1554,7 @@ static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = { { "oSpinyTargetYaw", LVT_S32, offsetof(struct Object, oSpinyTargetYaw), false, LOT_NONE }, { "oSpinyTimeUntilTurn", LVT_S32, offsetof(struct Object, oSpinyTimeUntilTurn), false, LOT_NONE }, { "oSpinyTurningAwayFromWall", LVT_S32, offsetof(struct Object, oSpinyTurningAwayFromWall), false, LOT_NONE }, +// { "oStarBehavior", LVT_???, offsetof(struct Object, oStarBehavior), true, LOT_??? }, <--- UNIMPLEMENTED { "oStarSelectorSize", LVT_F32, offsetof(struct Object, oStarSelectorSize), false, LOT_NONE }, { "oStarSelectorTimer", LVT_S32, offsetof(struct Object, oStarSelectorTimer), false, LOT_NONE }, { "oStarSelectorType", LVT_S32, offsetof(struct Object, oStarSelectorType), false, LOT_NONE }, diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 0e5ee625..0c54bada 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -857,7 +857,9 @@ char gSmluaConstants[] = "" "id_bhvYoshi = 534\n" "id_RM_Scroll_Texture = 535\n" "id_editor_Scroll_Texture = 536\n" -"id_bhv_max_count = 537\n" +"id_bhvBlueCoinNumber = 537\n" +"id_bhvStarNumber = 538\n" +"id_bhv_max_count = 539\n" "CAM_MODE_MARIO_ACTIVE = 0x01\n" "CAM_MODE_LAKITU_WAS_ZOOMED_OUT = 0x02\n" "CAM_MODE_MARIO_SELECTED = 0x04\n" @@ -1872,6 +1874,7 @@ char gSmluaConstants[] = "" "BLUE_COIN_SWITCH_ACT_IDLE = 0\n" "BLUE_COIN_SWITCH_ACT_RECEDING = 1\n" "BLUE_COIN_SWITCH_ACT_TICKING = 2\n" +"BLUE_COIN_SWITCH_ACT_RESPAWNING = 3\n" "MOV_BCOIN_ACT_STILL = 0\n" "MOV_BCOIN_ACT_MOVING = 1\n" "MOV_YCOIN_ACT_IDLE = 0\n" @@ -3983,7 +3986,7 @@ char gSmluaConstants[] = "" "COOP_OBJ_FLAG_NETWORK = (1 << 0)\n" "COOP_OBJ_FLAG_LUA = (1 << 1)\n" "COOP_OBJ_FLAG_NON_SYNC = (1 << 2)\n" -"VERSION_NUMBER = 31\n" +"VERSION_NUMBER = 32\n" "MINOR_VERSION_NUMBER = 0\n" "MAX_VERSION_LENGTH = 10\n" ; \ No newline at end of file diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 79431e2e..9cb90305 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -915,6 +915,21 @@ int smlua_func_bhv_blue_coin_jumping_loop(UNUSED lua_State* L) { return 1; } +int smlua_func_bhv_blue_coin_number_loop(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "bhv_blue_coin_number_loop", 0, top); + return 0; + } + + + bhv_blue_coin_number_loop(); + + return 1; +} + int smlua_func_bhv_blue_coin_sliding_jumping_init(UNUSED lua_State* L) { if (L == NULL) { return 0; } @@ -945,6 +960,21 @@ int smlua_func_bhv_blue_coin_sliding_loop(UNUSED lua_State* L) { return 1; } +int smlua_func_bhv_blue_coin_switch_init(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "bhv_blue_coin_switch_init", 0, top); + return 0; + } + + + bhv_blue_coin_switch_init(); + + return 1; +} + int smlua_func_bhv_blue_coin_switch_loop(UNUSED lua_State* L) { if (L == NULL) { return 0; } @@ -6561,6 +6591,21 @@ int smlua_func_bhv_star_key_collection_puff_spawner_loop(UNUSED lua_State* L) { return 1; } +int smlua_func_bhv_star_number_loop(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "bhv_star_number_loop", 0, top); + return 0; + } + + + bhv_star_number_loop(); + + return 1; +} + int smlua_func_bhv_star_spawn_init(UNUSED lua_State* L) { if (L == NULL) { return 0; } @@ -8838,6 +8883,21 @@ int smlua_func_spawn_red_coin_cutscene_star(lua_State* L) { return 1; } +int smlua_func_spawn_star_number(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "spawn_star_number", 0, top); + return 0; + } + + + spawn_star_number(); + + return 1; +} + int smlua_func_spawn_triangle_break_particles(lua_State* L) { if (L == NULL) { return 0; } @@ -28881,8 +28941,10 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "bhv_blue_bowser_flame_init", smlua_func_bhv_blue_bowser_flame_init); smlua_bind_function(L, "bhv_blue_bowser_flame_loop", smlua_func_bhv_blue_bowser_flame_loop); smlua_bind_function(L, "bhv_blue_coin_jumping_loop", smlua_func_bhv_blue_coin_jumping_loop); + smlua_bind_function(L, "bhv_blue_coin_number_loop", smlua_func_bhv_blue_coin_number_loop); smlua_bind_function(L, "bhv_blue_coin_sliding_jumping_init", smlua_func_bhv_blue_coin_sliding_jumping_init); smlua_bind_function(L, "bhv_blue_coin_sliding_loop", smlua_func_bhv_blue_coin_sliding_loop); + smlua_bind_function(L, "bhv_blue_coin_switch_init", smlua_func_bhv_blue_coin_switch_init); smlua_bind_function(L, "bhv_blue_coin_switch_loop", smlua_func_bhv_blue_coin_switch_loop); smlua_bind_function(L, "bhv_blue_fish_movement_loop", smlua_func_bhv_blue_fish_movement_loop); smlua_bind_function(L, "bhv_blue_flames_group_loop", smlua_func_bhv_blue_flames_group_loop); @@ -29257,6 +29319,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "bhv_star_door_loop", smlua_func_bhv_star_door_loop); smlua_bind_function(L, "bhv_star_door_loop_2", smlua_func_bhv_star_door_loop_2); smlua_bind_function(L, "bhv_star_key_collection_puff_spawner_loop", smlua_func_bhv_star_key_collection_puff_spawner_loop); + smlua_bind_function(L, "bhv_star_number_loop", smlua_func_bhv_star_number_loop); smlua_bind_function(L, "bhv_star_spawn_init", smlua_func_bhv_star_spawn_init); smlua_bind_function(L, "bhv_star_spawn_loop", smlua_func_bhv_star_spawn_loop); smlua_bind_function(L, "bhv_static_checkered_platform_loop", smlua_func_bhv_static_checkered_platform_loop); @@ -29394,6 +29457,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "spawn_mist_particles_variable", smlua_func_spawn_mist_particles_variable); smlua_bind_function(L, "spawn_no_exit_star", smlua_func_spawn_no_exit_star); smlua_bind_function(L, "spawn_red_coin_cutscene_star", smlua_func_spawn_red_coin_cutscene_star); + smlua_bind_function(L, "spawn_star_number", smlua_func_spawn_star_number); smlua_bind_function(L, "spawn_triangle_break_particles", smlua_func_spawn_triangle_break_particles); smlua_bind_function(L, "spawn_wind_particles", smlua_func_spawn_wind_particles); smlua_bind_function(L, "tox_box_move", smlua_func_tox_box_move);