From ead24c6c417b7ece6b4a13c2846ea6667a1247ed Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 5 Jul 2023 16:56:04 -0600 Subject: [PATCH 01/12] Added Leveled Code --- soh/include/functions.h | 8 +- soh/include/global.h | 3 + soh/include/leveled_actor_level_table.h | 6 + soh/include/leveled_overlays.h | 15 + soh/include/leveled_stat_math.h | 22 + soh/include/z64actor.h | 20 +- soh/include/z64player.h | 2 +- soh/include/z64save.h | 7 +- .../cosmetics/CosmeticsEditor.cpp | 2 +- soh/soh/Enhancements/debugger/actorViewer.cpp | 6 +- .../Enhancements/debugger/debugSaveEditor.cpp | 10 +- .../GameInteractor_RawAction.cpp | 6 +- soh/soh/Enhancements/mods.cpp | 4 +- soh/soh/OTRGlobals.cpp | 44 + soh/soh/OTRGlobals.h | 1 + soh/soh/SaveManager.cpp | 11 +- soh/soh/SohMenuBar.cpp | 84 +- soh/src/code/leveled_actor_level_table.c | 791 +++++++++++++++++ soh/src/code/leveled_overlays.c | 811 ++++++++++++++++++ soh/src/code/leveled_stat_math.c | 253 ++++++ soh/src/code/z_actor.c | 45 +- soh/src/code/z_collision_check.c | 9 + soh/src/code/z_en_item00.c | 2 +- soh/src/code/z_lifemeter.c | 27 +- soh/src/code/z_message_PAL.c | 10 +- soh/src/code/z_parameter.c | 102 ++- soh/src/code/z_player_lib.c | 43 + soh/src/code/z_sram.c | 4 +- .../ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c | 12 +- .../actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 15 +- .../overlays/actors/ovl_Boss_Fd/z_boss_fd.c | 18 +- .../overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c | 52 +- .../actors/ovl_Boss_Ganon/z_boss_ganon.c | 31 +- .../actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 27 +- .../ovl_Boss_Ganondrof/z_boss_ganondrof.c | 34 +- .../actors/ovl_Boss_Goma/z_boss_goma.c | 11 +- .../overlays/actors/ovl_Boss_Mo/z_boss_mo.c | 13 +- .../overlays/actors/ovl_Boss_Sst/z_boss_sst.c | 3 +- .../overlays/actors/ovl_Boss_Tw/z_boss_tw.c | 21 +- .../overlays/actors/ovl_Boss_Va/z_boss_va.c | 16 +- soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c | 10 +- soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c | 10 +- .../actors/ovl_En_Bdfire/z_en_bdfire.c | 3 +- .../actors/ovl_En_Bigokuta/z_en_bigokuta.c | 1 + .../overlays/actors/ovl_En_Bili/z_en_bili.c | 10 +- .../ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c | 2 +- .../actors/ovl_En_Bubble/z_en_bubble.c | 3 +- soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c | 2 + .../actors/ovl_En_Dekubaba/z_en_dekubaba.c | 1 + .../actors/ovl_En_Dekunuts/z_en_dekunuts.c | 6 +- soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c | 8 +- .../actors/ovl_En_Dodongo/z_en_dodongo.c | 1 + soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c | 9 +- soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c | 7 +- .../actors/ovl_En_Firefly/z_en_firefly.c | 45 +- .../actors/ovl_En_Floormas/z_en_floormas.c | 8 +- soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c | 4 +- soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c | 4 +- .../overlays/actors/ovl_En_GirlA/z_en_girla.c | 2 +- .../overlays/actors/ovl_En_Goma/z_en_goma.c | 4 +- soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c | 17 +- soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c | 2 + .../actors/ovl_En_Peehat/z_en_peehat.c | 1 + soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c | 8 +- soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c | 3 + soh/src/overlays/actors/ovl_En_Si/z_en_si.c | 2 + soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c | 1 + soh/src/overlays/actors/ovl_En_St/z_en_st.c | 3 +- .../overlays/actors/ovl_En_Test/z_en_test.c | 4 +- .../overlays/actors/ovl_En_Tite/z_en_tite.c | 2 + soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c | 1 + soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c | 6 +- soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c | 1 + soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c | 4 +- soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c | 3 +- .../actors/ovl_player_actor/z_player.c | 67 +- .../misc/ovl_kaleido_scope/z_kaleido_debug.c | 2 + .../ovl_kaleido_scope/z_kaleido_equipment.c | 5 + .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 2 +- 79 files changed, 2636 insertions(+), 239 deletions(-) create mode 100644 soh/include/leveled_actor_level_table.h create mode 100644 soh/include/leveled_overlays.h create mode 100644 soh/include/leveled_stat_math.h create mode 100644 soh/src/code/leveled_actor_level_table.c create mode 100644 soh/src/code/leveled_overlays.c create mode 100644 soh/src/code/leveled_stat_math.c diff --git a/soh/include/functions.h b/soh/include/functions.h index 9bc1d061563..7b388ea98a2 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -394,6 +394,7 @@ void TitleCard_InitBossName(PlayState* play, TitleCardContext* titleCtx, void* t void TitleCard_InitPlaceName(PlayState* play, TitleCardContext* titleCtx, void* texture, s32 x, s32 y, s32 width, s32 height, s32 delay); s32 func_8002D53C(PlayState* play, TitleCardContext* titleCtx); +void Actor_RefreshLeveledStats(Actor* actor, Player* player); void Actor_Kill(Actor* actor); void Actor_SetFocus(Actor* actor, f32 offset); void Actor_SetScale(Actor* actor, f32 scale); @@ -547,7 +548,7 @@ s32 func_800354B4(PlayState* play, Actor* actor, f32 range, s16 arg3, s16 arg4, void func_8003555C(PlayState* play, Vec3f* pos, Vec3f* velocity, Vec3f* accel); void func_800355B8(PlayState* play, Vec3f* pos); u8 func_800355E4(PlayState* play, Collider* collider); -u8 Actor_ApplyDamage(Actor* actor); +u16 Actor_ApplyDamage(Actor* actor); void Actor_SetDropFlag(Actor* actor, ColliderInfo* colBody, s32 freezeFlag); void Actor_SetDropFlagJntSph(Actor* actor, ColliderJntSph* colBody, s32 freezeFlag); void func_80035844(Vec3f* arg0, Vec3f* arg1, Vec3s* arg2, s32 arg3); @@ -1064,6 +1065,10 @@ void Minimap_Draw(PlayState* play); void Map_Update(PlayState* play); void Interface_ChangeAlpha(u16 alphaType); void Interface_SetSceneRestrictions(PlayState* play); +Gfx* Gfx_TextureIA8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); void Inventory_SwapAgeEquipment(void); void Interface_InitHorsebackArchery(PlayState* play); void func_800849EC(PlayState* play); @@ -1122,6 +1127,7 @@ void func_8008EE08(Player* player); void func_8008EEAC(PlayState* play, Actor* actor); s32 func_8008EF44(PlayState* play, s32 ammo); s32 Player_IsBurningStickInRange(PlayState* play, Vec3f* pos, f32 radius, f32 arg3); +void Player_GainExperience(PlayState* play, u16 experience); s32 Player_GetStrength(void); u8 Player_GetMask(PlayState* play); Player* Player_UnsetMask(PlayState* play); diff --git a/soh/include/global.h b/soh/include/global.h index 43f56ed0e99..035b23af562 100644 --- a/soh/include/global.h +++ b/soh/include/global.h @@ -8,6 +8,9 @@ #include "functions.h" #include "variables.h" #include "macros.h" +#include "leveled_overlays.h" +#include "leveled_stat_math.h" +#include "leveled_actor_level_table.h" #include "soh/OTRGlobals.h" #include "soh/Enhancements/gameconsole.h" #include "soh/Enhancements/gameplaystats.h" diff --git a/soh/include/leveled_actor_level_table.h b/soh/include/leveled_actor_level_table.h new file mode 100644 index 00000000000..7472b50c06c --- /dev/null +++ b/soh/include/leveled_actor_level_table.h @@ -0,0 +1,6 @@ +#ifndef LEVELED_ACTOR_LEVEL_TABLE_H +#define LEVELED_ACTOR_LEVEL_TABLE_H +#include "z64.h" +#endif + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride); \ No newline at end of file diff --git a/soh/include/leveled_overlays.h b/soh/include/leveled_overlays.h new file mode 100644 index 00000000000..26f01abe4fc --- /dev/null +++ b/soh/include/leveled_overlays.h @@ -0,0 +1,15 @@ +#ifndef LEVELED_OVERLAYS_H +#define LEVELED_OVERLAYS_H +#include "z64.h" +#endif + +void ActorDamageNumber_New(Actor* actor, u16 damage); +void ActorExperienceNumber_New(Actor* actor, u16 experience); +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff); +void ActorDamageNumber_Draw(PlayState* play, Actor* actor); +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor); +void Actor_LevelUpDraw(PlayState* play, Actor* actor); +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b); +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a); +void Leveled_KaleidoEquip_Stats(PlayState* play); +void Leveled_Interface_DrawNextLevel(PlayState* play); \ No newline at end of file diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h new file mode 100644 index 00000000000..772856522e0 --- /dev/null +++ b/soh/include/leveled_stat_math.h @@ -0,0 +1,22 @@ +#ifndef LEVELED_STAT_MATH_H +#define LEVELED_STAT_MATH_H +#define HEALTH_ATTACK_MULTIPLIER 7 +#include "z64.h" +#endif + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power); +u16 GetActorStat_Attack(u16 attack, u8 power); +u8 GetActorStat_Power(u8 level); +u8 GetActorStat_Courage(u8 level); +u8 GetActorStat_PlayerPower(u8 level); +u8 GetActorStat_PlayerCourage(u8 level); +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level); +u8 GetPlayerStat_BonusHearts(u8 level); +u8 GetPlayerStat_MagicUnits(u8 level); +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level); +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level); +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); +f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack); +u16 Leveled_GoldSkulltulaExperience(u8 tokens); +void Leveled_SetPlayerModifiedStats(Player* player); \ No newline at end of file diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 09e6a64ed93..72b01d41881 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -53,14 +53,14 @@ typedef struct { } DamageTable; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ u8 mass; } CollisionCheckInfoInit; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ s16 cylYShift; @@ -74,8 +74,8 @@ typedef struct { /* 0x12 */ s16 cylHeight; // Used for various purposes /* 0x14 */ s16 cylYShift; // Unused. Purpose inferred from Cylinder16 and CollisionCheck_CylSideVsLineSeg /* 0x16 */ u8 mass; // Used to compute displacement for OC collisions - /* 0x17 */ u8 health; // Note: some actors may use their own health variable instead of this one - /* 0x18 */ u8 damage; // Amount to decrement health by + /* 0x17 */ u16 health; // Note: some actors may use their own health variable instead of this one + /* 0x18 */ u16 damage; // Amount to decrement health by /* 0x19 */ u8 damageEffect; // Stores what effect should occur when hit by a weapon /* 0x1A */ u8 atHitEffect; // Stores what effect should occur when AT connects with an AC /* 0x1B */ u8 acHitEffect; // Stores what effect should occur when AC is touched by an AT @@ -178,6 +178,18 @@ typedef struct Actor { /* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw` /* 0x138 */ ActorResetFunc reset; /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom + u16 exp; // Experience + u8 level; // Actor Level + u8 power; // e.g. Strength + u8 courage; // e.g. Defense + s8 powerModifier; // Modifies Power + s8 courageModifier; // Modifies Courage + u16 maximumHealth; // Health after Leveled math for enemies (Some don't use this) + u16 floatingNumber[7]; + u8 floatingNumberLife[7]; + Vec2f floatingNumberPosition[7]; + Vec2f floatingNumberVelocity[7]; + bool ignoreExpReward; // Actor handles exp reward differently } Actor; // size = 0x14C typedef enum { diff --git a/soh/include/z64player.h b/soh/include/z64player.h index 4aaf5758653..761f6643145 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -646,7 +646,7 @@ typedef struct Player { /* 0x089A */ s16 unk_89A; /* 0x089C */ s16 unk_89C; /* 0x089E */ u16 unk_89E; - /* 0x08A0 */ u8 unk_8A0; + /* 0x08A0 */ u16 unk_8A0; /* 0x08A1 */ u8 unk_8A1; /* 0x08A2 */ s16 unk_8A2; /* 0x08A4 */ f32 unk_8A4; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 4fa02f15752..25a8170b285 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -182,7 +182,7 @@ typedef struct { /* 0x002E */ s16 healthCapacity; // "max_life" /* 0x0030 */ s16 health; // "now_life" /* 0x0032 */ s8 magicLevel; // 0 for no magic/new load, 1 for magic, 2 for double magic - /* 0x0033 */ s8 magic; // current magic available for use + /* 0x0033 */ u8 magic; // current magic available for use /* 0x0034 */ s16 rupees; /* 0x0036 */ u16 swordHealth; /* 0x0038 */ u16 naviTimer; @@ -319,6 +319,11 @@ typedef struct { /* */ u8 seedIcons[5]; /* */ u16 randomizerInf[9]; /* */ u16 adultTradeItems; + u32 experience; + s16 heartContainers; + s16 healthCapacity2; // Modified max health + u8 magicUnits; // Modified magic units per magic level + s16 showNeededExpTimer; // Shows EXP required in the hud when greater than 0 // #endregion } SaveContext; // size = 0x1428 diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index b678a253c32..e9f6b02363c 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -1138,7 +1138,7 @@ void Draw_Placements(){ DrawPositionsRadioBoxes("gHeartsCount"); DrawPositionSlider("gHeartsCount",-22,ImGui::GetWindowViewport()->Size.y,-125,ImGui::GetWindowViewport()->Size.x); DrawScaleSlider("gHeartsCount",0.7f); - UIWidgets::EnhancementSliderInt("Heart line length : %d", "##HeartLineLength", "gHeartsLineLength", 0, 20, "", 10); + UIWidgets::EnhancementSliderInt("Heart line length : %d", "##HeartLineLength", "gHeartsLineLength", 0, 20, "", 15); UIWidgets::Tooltip("This will set the length of a row of hearts. Set to 0 for unlimited length."); ImGui::NewLine(); ImGui::EndTable(); diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index de35a6acdf3..2b9702c65a1 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -166,6 +166,10 @@ void ActorViewerWindow::DrawElement() { ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str()); ImGui::Text("Description: %s", GetActorDescription(display->id).c_str()); ImGui::Text("Category: %s", acMapping[display->category]); + if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { + ImGui::Text("Level: %d", display->level); + ImGui::Text("EXP: %d", display->exp); + } ImGui::Text("ID: %d", display->id); ImGui::Text("Parameters: %d", display->params); }); @@ -191,7 +195,7 @@ void ActorViewerWindow::DrawElement() { }); if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { - ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health); + ImGui::InputScalar("Enemy Health", ImGuiDataType_U16, &display->colChkInfo.health); UIWidgets::InsertHelpHoverText("Some actors might not use this!"); } diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index c019facb4d2..15c605b8f1d 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -336,12 +336,12 @@ void DrawInfoTab() { gSaveContext.healthCapacity = healthIntermediary; } UIWidgets::InsertHelpHoverText("Maximum health. 16 units per full heart"); - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; // Clamp health to new max + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; // Clamp health to new max } const uint16_t healthMin = 0; - const uint16_t healthMax = gSaveContext.healthCapacity; + const uint16_t healthMax = gSaveContext.healthCapacity2; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); ImGui::SliderScalar("Health", ImGuiDataType_S16, &gSaveContext.health, &healthMin, &healthMax); UIWidgets::InsertHelpHoverText("Current health. 16 units per full heart"); @@ -383,7 +383,7 @@ void DrawInfoTab() { ImGui::EndCombo(); } UIWidgets::InsertHelpHoverText("Current magic level"); - gSaveContext.magicCapacity = gSaveContext.magicLevel * 0x30; // Set to get the bar drawn in the UI + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; // Set to get the bar drawn in the UI if (gSaveContext.magic > gSaveContext.magicCapacity) { gSaveContext.magic = gSaveContext.magicCapacity; // Clamp magic to new max } @@ -391,7 +391,7 @@ void DrawInfoTab() { const uint8_t magicMin = 0; const uint8_t magicMax = gSaveContext.magicCapacity; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderScalar("Magic", ImGuiDataType_S8, &gSaveContext.magic, &magicMin, &magicMax); + ImGui::SliderScalar("Magic", ImGuiDataType_U8, &gSaveContext.magic, &magicMin, &magicMax); UIWidgets::InsertHelpHoverText("Current magic. 48 units per magic level"); ImGui::InputScalar("Rupees", ImGuiDataType_S16, &gSaveContext.rupees); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index e7db940cfc9..510976ecaa7 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -45,17 +45,17 @@ void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { if (hearts > 0) { - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveledHeartUnits", 4) << 2); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveledHeartUnits", 4) << 2); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } } void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { - gSaveContext.health = hearts * 0x10; + gSaveContext.health = hearts * CVarGetInteger("gLeveledHeartUnits", 4) << 2; } void GameInteractor::RawAction::SetLinkInvisibility(bool active) { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2048ead3168..dcdce243d40 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -43,8 +43,8 @@ void RegisterInfiniteMoney() { void RegisterInfiniteHealth() { GameInteractor::Instance->RegisterGameHook([]() { if (CVarGetInteger("gInfiniteHealth", 0) != 0) { - if (gSaveContext.health < gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health < gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; } } }); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index b044c05339b..c38b15f3380 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1793,6 +1793,50 @@ extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSi return 0; } +extern "C" int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor) { + std::string postfix; + + if (!actor) + return 0; + + if (gSaveContext.language == LANGUAGE_FRA) { + postfix = ""; + } else if (gSaveContext.language == LANGUAGE_GER) { + postfix = ""; + } else { + postfix = ""; + if (CVarGetInteger("gLeveledNaviLevel", 1)) { + postfix += " \x05" + "F" + "Lv" + + std::to_string(actor->level); + } + if (CVarGetInteger("gLeveledNaviMaxHP", 1) && actor->maximumHealth > 0) { + postfix += " \x05" + "A" + "MaxHP " + + std::to_string(actor->maximumHealth); + } + } + std::string str; + std::string FixedBaseStr(src); + int FoundControlChar = FixedBaseStr.find_first_of("\x01"); + + if (FoundControlChar != std::string::npos) { + FixedBaseStr = FixedBaseStr.insert(FoundControlChar, postfix); + } + + str = FixedBaseStr; + + if (!str.empty()) { + memset(buffer, 0, maxBufferSize); + const int copiedCharLen = std::min(maxBufferSize - 1, str.length()); + memcpy(buffer, str.c_str(), copiedCharLen); + return copiedCharLen; + } + return 0; +} + extern "C" void Randomizer_LoadSettings(const char* spoilerFileName) { OTRGlobals::Instance->gRandomizer->LoadRandomizerSettings(spoilerFileName); } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index bef92f30605..3d8d0d7b2a2 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -114,6 +114,7 @@ void Controller_BlockGameInput(); void Controller_UnblockGameInput(); void* getN64WeirdFrame(s32 i); int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSize); +int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor); u32 SpoilerFileExists(const char* spoilerFileName); Sprite* GetSeedTexture(uint8_t index); void Randomizer_LoadSettings(const char* spoilerFileName); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index f8b6715994f..07d282eb349 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -457,9 +457,13 @@ void SaveManager::InitFileNormal() { for (int i = 0; i < ARRAY_COUNT(gSaveContext.playerName); i++) { gSaveContext.playerName[i] = 0x3E; } + gSaveContext.healthCapacity2 = 9999; + gSaveContext.magicUnits = 9999; + gSaveContext.experience = 0; + gSaveContext.showNeededExpTimer = 0; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0x30; - gSaveContext.health = 0x30; + gSaveContext.health = 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 0; @@ -600,6 +604,7 @@ void SaveManager::InitFileDebug() { for (int i = 0; i < ARRAY_COUNT(gSaveContext.playerName); i++) { gSaveContext.playerName[i] = sPlayerName[i]; } + gSaveContext.experience = 80000; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0xE0; gSaveContext.health = 0xE0; @@ -919,6 +924,7 @@ void SaveManager::CreateDefaultGlobal() { } void SaveManager::LoadBaseVersion1() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1060,6 +1066,7 @@ void SaveManager::LoadBaseVersion1() { } void SaveManager::LoadBaseVersion2() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1268,6 +1275,7 @@ void SaveManager::LoadBaseVersion2() { } void SaveManager::LoadBaseVersion3() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1659,6 +1667,7 @@ void SaveManager::LoadBaseVersion4() { } void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSave) { + SaveManager::Instance->SaveData("experience", gSaveContext.experience); SaveManager::Instance->SaveData("entranceIndex", saveContext->entranceIndex); SaveManager::Instance->SaveData("linkAge", saveContext->linkAge); SaveManager::Instance->SaveData("cutsceneIndex", saveContext->cutsceneIndex); diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 337a848e0ab..ea6d31d4561 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -419,7 +419,7 @@ void DrawSettingsMenu() { } if (LUS::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports()) { - UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows", "gEnableMultiViewports", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); + PaddedEnhancementCheckbox("Allow multi-windows", "gEnableMultiViewports", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::Tooltip("Allows windows to be able to be dragged off of the main game window. Requires a reload to take effect."); } @@ -531,8 +531,8 @@ void DrawEnhancementsMenu() { static const char* forceSkipScarecrowText = "This setting is forcefully enabled because a savefile\nwith \"Skip Scarecrow Song\" is loaded"; UIWidgets::Tooltip("Skip the part where the Ocarina playback is called when you play a song"); - UIWidgets::PaddedEnhancementCheckbox("Skip Scarecrow Song", "gSkipScarecrow", true, false, - forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark); + PaddedEnhancementCheckbox("Skip Scarecrow Song", "gSkipScarecrow", true, false, + forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song."); UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", true, false); UIWidgets::Tooltip("When loading a save, places Link at the last entrance he went through.\n" @@ -648,8 +648,8 @@ void DrawEnhancementsMenu() { OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_ENABLE_BOMBCHU_DROPS) == 1; static const char* forceEnableBombchuDropsText = "This setting is forcefully enabled because a savefile\nwith \"Enable Bombchu Drops\" is loaded."; - UIWidgets::PaddedEnhancementCheckbox("Enable Bombchu Drops", "gBombchuDrops", true, false, - forceEnableBombchuDrops, forceEnableBombchuDropsText, UIWidgets::CheckboxGraphics::Checkmark); + PaddedEnhancementCheckbox("Enable Bombchu Drops", "gBombchuDrops", true, false, + forceEnableBombchuDrops, forceEnableBombchuDropsText, UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Bombchus will sometimes drop in place of bombs"); UIWidgets::PaddedEnhancementCheckbox("No Heart Drops", "gNoHeartDrops", true, false); UIWidgets::Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series"); @@ -659,8 +659,8 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("All regular enemies and mini-bosses move and act twice as fast."); UIWidgets::PaddedEnhancementCheckbox("Always Win Goron Pot", "gGoronPot", true, false); UIWidgets::Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot"); - UIWidgets::PaddedEnhancementCheckbox("Always Win Dampe Digging Game", "gDampeWin", true, false, SaveManager::Instance->IsRandoFile(), - "This setting is always enabled in randomizer files", UIWidgets::CheckboxGraphics::Checkmark); + PaddedEnhancementCheckbox("Always Win Dampe Digging Game", "gDampeWin", true, false, SaveManager::Instance->IsRandoFile(), + "This setting is always enabled in randomizer files", UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Always win the heart piece/purple rupee on the first dig in Dampe's grave digging game, just like in rando\nIn a rando file, this is unconditionally enabled"); UIWidgets::Spacer(0); @@ -850,8 +850,8 @@ void DrawEnhancementsMenu() { OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BLUE_FIRE_ARROWS); static const char* forceEnableBlueFireArrowsText = "This setting is forcefully enabled because a savefile\nwith \"Blue Fire Arrows\" is loaded."; - UIWidgets::PaddedEnhancementCheckbox("Blue Fire Arrows", "gBlueFireArrows", true, false, - forceEnableBlueFireArrows, forceEnableBlueFireArrowsText, UIWidgets::CheckboxGraphics::Checkmark); + PaddedEnhancementCheckbox("Blue Fire Arrows", "gBlueFireArrows", true, false, + forceEnableBlueFireArrows, forceEnableBlueFireArrowsText, UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Allows Ice Arrows to melt red ice.\nMay require a room reload if toggled during gameplay."); // Sunlight Arrows @@ -859,8 +859,8 @@ void DrawEnhancementsMenu() { OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SUNLIGHT_ARROWS); static const char* forceEnableSunLightArrowsText = "This setting is forcefully enabled because a savefile\nwith \"Sunlight Arrows\" is loaded."; - UIWidgets::PaddedEnhancementCheckbox("Sunlight Arrows", "gSunlightArrows", true, false, - forceEnableSunLightArrows, forceEnableSunLightArrowsText, UIWidgets::CheckboxGraphics::Checkmark); + PaddedEnhancementCheckbox("Sunlight Arrows", "gSunlightArrows", true, false, + forceEnableSunLightArrows, forceEnableSunLightArrowsText, UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Allows Light Arrows to activate sun switches.\nMay require a room reload if toggled during gameplay."); UIWidgets::PaddedEnhancementCheckbox("Disable Crit wiggle", "gDisableCritWiggle", true, false); @@ -1285,7 +1285,7 @@ void DrawDeveloperToolsMenu() { }; UIWidgets::PaddedEnhancementCheckbox("Better Debug Warp Screen", "gBetterDebugWarpScreen", true, false); UIWidgets::Tooltip("Optimized debug warp screen, with the added ability to chose entrances and time of day"); - UIWidgets::PaddedEnhancementCheckbox("Debug Warp Screen Translation", "gDebugWarpScreenTranslation", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); + PaddedEnhancementCheckbox("Debug Warp Screen Translation", "gDebugWarpScreenTranslation", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::Tooltip("Translate the Debug Warp Screen based on the game language"); UIWidgets::PaddedSeparator(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); @@ -1399,11 +1399,11 @@ void DrawRandomizerMenu() { if (ImGui::BeginMenu("Rando Enhancements")) { - UIWidgets::EnhancementCheckbox("Rando-Relevant Navi Hints", "gRandoRelevantNavi", false, "", UIWidgets::CheckboxGraphics::Cross, true); + EnhancementCheckbox("Rando-Relevant Navi Hints", "gRandoRelevantNavi", false, "", UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::Tooltip( "Replace Navi's overworld quest hints with rando-related gameplay hints." ); - UIWidgets::PaddedEnhancementCheckbox("Random Rupee Names", "gRandomizeRupeeNames", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); + PaddedEnhancementCheckbox("Random Rupee Names", "gRandomizeRupeeNames", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::Tooltip( "When obtaining rupees, randomize what the rupee is called in the textbox." ); @@ -1429,8 +1429,8 @@ void DrawRandomizerMenu() { "This setting is disabled because a savefile is loaded without any key\n" "shuffle settings set to \"Any Dungeon\", \"Overworld\" or \"Anywhere\""; - UIWidgets::PaddedEnhancementCheckbox("Key Colors Match Dungeon", "gRandoMatchKeyColors", true, false, - disableKeyColors, disableKeyColorsText, UIWidgets::CheckboxGraphics::Cross, true); + PaddedEnhancementCheckbox("Key Colors Match Dungeon", "gRandoMatchKeyColors", true, false, + disableKeyColors, disableKeyColorsText, UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::Tooltip( "Matches the color of small keys and boss keys to the dungeon they belong to. " "This helps identify keys from afar and adds a little bit of flair.\n\nThis only " @@ -1478,6 +1478,58 @@ void SohMenuBar::DrawElement() { DrawRandomizerMenu(); ImGui::PopStyleVar(1); + + if (ImGui::BeginMenu("Leveled")) { + if (ImGui::BeginMenu("Floating Numbers")) { + EnhancementCheckbox("Enemy Damage", "gLeveledFloatingNumberEnemyDamage", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + ImGui::EndMenu(); + } + + EnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Enemy Level Affects Base Attack", "gLeveledEnemyAttackScalesWithLevel", + false, "", UIWidgets::CheckboxGraphics::Checkmark, true); + UIWidgets::Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " + "Power(Strength) stat. \nThis will increase difficulty a bit."); + EnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + + if (ImGui::BeginMenu("Heart Capacity in Units")) { + CVarGetInteger("gLeveledHeartUnits", 4); + UIWidgets::EnhancementRadioButton("4", "gLeveledHeartUnits", 1); + UIWidgets::EnhancementRadioButton("8", "gLeveledHeartUnits", 2); + UIWidgets::EnhancementRadioButton("12", "gLeveledHeartUnits", 3); + UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveledHeartUnits", 4); + UIWidgets::EnhancementRadioButton("20", "gLeveledHeartUnits", 5); + UIWidgets::EnhancementRadioButton("24", "gLeveledHeartUnits", 6); + UIWidgets::EnhancementRadioButton("28", "gLeveledHeartUnits", 7); + UIWidgets::EnhancementRadioButton("32", "gLeveledHeartUnits", 8); + UIWidgets::EnhancementRadioButton("36", "gLeveledHeartUnits", 9); + UIWidgets::EnhancementRadioButton("40", "gLeveledHeartUnits", 10); + UIWidgets::EnhancementRadioButton("44", "gLeveledHeartUnits", 11); + UIWidgets::EnhancementRadioButton("48", "gLeveledHeartUnits", 12); + ImGui::EndMenu(); + } + UIWidgets::Tooltip("Sets how much health units a heart is worth."); + + // if (ImGui::Button("Add 2000 EXP")){ + // gSaveContext.experience += 2000; + // } + + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); } } diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c new file mode 100644 index 00000000000..f722cffe86f --- /dev/null +++ b/soh/src/code/leveled_actor_level_table.c @@ -0,0 +1,791 @@ +#include "global.h" + +///////////////////////////////////////////////// +////////////////// BOSSES ///////////////////// +///////////////////////////////////////////////// +void Leveled_Gohma(PlayState* play, Actor*actor) { + actor->level = 7; + actor->exp = 120; + + if (actor->category == ACTORCAT_ENEMY) { // Larva + actor->level = 5; + if (actor->params < 10) { + actor->exp = 5; + } + } +} + +void Leveled_Dodongo(PlayState* play, Actor*actor) { + if (actor->category == ACTORCAT_BOSS) { // King Dodongo + actor->level = 15; + actor->exp = 500; + } else { + actor->level = 14; + actor->exp = 18; + } +} + +void Leveled_Barinade(PlayState* play, Actor*actor) { + actor->level = 21; + if (actor->params == -1) { // Body + actor->exp = 850; + } +} + +void Leveled_PhantomGanon(PlayState* play, Actor* actor) { + actor->level = 28; + if (actor->params == 1) + actor->exp = 1400; +} + +void Leveled_Volvagia(PlayState* play, Actor* actor) { + actor->level = 35; + if (actor->id == ACTOR_BOSS_FD2) + actor->exp = 2475; +} + +void Leveled_Morpha(PlayState* play, Actor* actor) { + actor->level = 40; + actor->exp = 3500; +} + +void Leveled_BongoBongo(PlayState* play, Actor* actor) { + actor->level = 45; + actor->exp = 4666; +} + +void Leveled_Twinrova(PlayState* play, Actor* actor) { + actor->level = 52; + if (actor->params == 2) + actor->exp = 6000; +} + +void Leveled_Ganondorf(PlayState* play, Actor* actor) { + actor->level = 62; + actor->exp = 9999; +} + +void Leveled_Ganon(PlayState* play, Actor* actor) { + actor->level = 66; +} + +///////////////////////////////////////////////// +/////////////// MINI-BOSSES /////////////////// +///////////////////////////////////////////////// +void Leveled_BigOctorock(PlayState* play, Actor*actor) { + actor->level = 20; + actor->exp = 320; +} + +void Leveled_FlareDancer(PlayState* play, Actor* actor) { + actor->level = 34; + if (actor->id == ACTOR_EN_FW) { + actor->exp = 1000; + } +} + +void Leveled_DarkLink(PlayState* play, Actor* actor) { + actor->level = GET_PLAYER(play)->actor.level; + actor->exp = (u16)((f32)GetPlayerStat_NextLevelExpAtLevel(GET_PLAYER(play)->actor.level) * 0.50f); +} + +void Leveled_DeadHand(PlayState* play, Actor* actor) { + actor->level = 44; + + if (actor->id == ACTOR_EN_DH) + actor->exp = 2465; +} + +void Leveled_IronKnuckle(PlayState* play, Actor* actor) { + actor->level = 50; + actor->exp = 2733; + if (actor->params == 0) { + actor->level = 52; + actor->exp = 3100; + } + if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 55; + actor->exp = 2850; + } +} +///////////////////////////////////////////////// +////////////// NORMAL ENEMIES ///////////////// +///////////////////////////////////////////////// +void Leveled_Dekubaba(PlayState* play, Actor*actor) { + if (actor->params == 1) { // Big baba + actor->level = 24; + actor->exp = 32; + if (LINK_IS_CHILD) { + actor->level = 5; + actor->exp = 8; + } + } else { + actor->level = 2; + actor->exp = 3; + if (play->sceneNum == SCENE_BMORI1) { // Forest Temple + actor->level = 23; + actor->exp = 19; + } + } +} + +void Leveled_StickDekuBaba(PlayState* play, Actor*actor) { + actor->level = 1; + actor->exp = 1; +} + +void Leveled_Skulltula(PlayState* play, Actor*actor) { + actor->level = 3; + actor->exp = 3; + if (play->sceneNum == SCENE_BMORI1) { // Forest Temple + actor->level = 21; + actor->exp = 19; + } + if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 38; + actor->exp = 30; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 44; + actor->exp = 34; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 50; + } + if (actor->params == 1) { // Big + actor->level += 3; + actor->exp += 2; + } +} + +void Leveled_Wall_Skulltula(PlayState* play, Actor* actor) { + if (((play, actor->params & 0xE000) >> 0xD) != 0) { // Gold Skulltula + actor->level = gSaveContext.inventory.gsTokens + 1; + actor->exp = 0; + } else { + actor->level = 3; + actor->exp = 2; + if (play->sceneNum == SCENE_BMORI1) { // Forest Temple + actor->level = 24; + actor->exp = 18; + } + } +} + +void Leveled_DekuScrub(PlayState* play, Actor*actor) { + actor->level = 8; + actor->exp = 7; +} + +void Leveled_Tektite(PlayState* play, Actor*actor) { + if (actor->params == -2) { // Blue + actor->level = 12; + actor->exp = 12; + if (LINK_IS_ADULT) { + actor->level = 25; + actor->exp = 21; + } + if (play->sceneNum == SCENE_MIZUSIN) { // Water Temple + actor->level = 36; + actor->exp = 44; + } + } else { + actor->level = 9; + actor->exp = 9; + if (LINK_IS_ADULT) { + actor->level = 27; + actor->exp = 24; + } + } +} + +void Leveled_Guay(PlayState* play, Actor*actor) { + actor->level = 8; + actor->exp = 4; + if (LINK_IS_ADULT) { + actor->level = 21; + actor->exp = 13; + } +} + +void Leveled_Octorock(PlayState* play, Actor*actor) { + actor->level = 13; + actor->exp = 12; + if (actor->params != 0) { + actor->level = 0; + actor->exp = 0; + } +} + +void Leveled_Armos(PlayState* play, Actor* actor) { + actor->level = 12; + actor->exp = 17; + if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 45; + actor->exp = 60; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 70; + } +} + +void Leveled_BabyDodongo(PlayState* play, Actor*actor) { + actor->level = 11; + actor->exp = 7; +} + +void Leveled_Keese(PlayState* play, Actor*actor) { + if (actor->params == 4) { // Ice Keese + actor->level = 34; + actor->exp = 33; + } else { + actor->level = 9; + actor->exp = 5; + + if (play->sceneNum == SCENE_HIDAN) { // Fire Temple + actor->level = 28; + actor->exp = 22; + } else if (play->sceneNum == SCENE_ICE_DOUKUTO) { // Ice Cavern + actor->level = 31; + actor->exp = 26; + } else if (play->sceneNum == SCENE_MIZUSIN) { // Water Temple + actor->level = 34; + actor->exp = 29; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 42; + actor->exp = 36; + } else if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 38; + actor->exp = 33; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 38; + } + if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 44; + } + } +} + +void Leveled_Peahat(PlayState* play, Actor*actor) { + actor->level = 18; + actor->exp = 47; + if (actor->params == 1) { // Larva + actor->level = 13; + } +} +void Leveled_Poh(PlayState* play, Actor*actor) { + actor->level = 10; + actor->exp = 14; + if (actor->params == 2 || actor->params == 3) { // Composer + actor->level = 15; + actor->exp = 25; + } +} + +void Leveled_Poh_Field(PlayState* play, Actor*actor) { + actor->level = 24; + actor->exp = 25; +} + +void Leveled_ReDead(PlayState* play, Actor*actor) { + if (actor->params >= -1) { + actor->level = 25; + actor->exp = 45; + if (play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 42; + actor->exp = 171; + } else if (play->sceneNum == SCENE_GANON_SONOGO || play->sceneNum == SCENE_GANONTIKA_SONOGO) { + actor->level = 50; + actor->exp = 230; + } + } else { // Gibdo + actor->level = 43; + actor->exp = 183; + } +} + +void Leveled_Shabom(PlayState* play, Actor*actor) { + actor->level = 15; +} + +void Leveled_Bari(PlayState* play, Actor*actor) { + actor->level = 18; + actor->exp = 25; +} + +void Leveled_Biri(PlayState* play, Actor*actor) { + actor->level = 16; + actor->exp = 17; +} + +void Leveled_Stinger(PlayState* play, Actor*actor) { + actor->level = 17; + actor->exp = 19; + if (play->sceneNum == SCENE_MIZUSIN) { // Water Temple + actor->level = 37; + actor->exp = 85; + } +} + +void Leveled_JabuTentacle(PlayState* play, Actor*actor) { + actor->level = 19; + if (actor->params < 3) { + actor->exp = 61; + } +} + +void Leveled_Tailpasaran(PlayState* play, Actor*actor) { + actor->level = 19; + actor->exp = 22; +} + +void Leveled_Stalchild(PlayState* play, Actor*actor) { + actor->level = 6 + (u16)(play, actor->params / 2); + actor->exp = 3 + (u16)(play, actor->params / 2.5); +} + +void Leveled_Beamos(PlayState* play, Actor*actor) { + if (actor->params == 0) { // Big + actor->level = 48; + actor->exp = 90; + } else { + actor->level = 11; + actor->exp = 17; + if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 43; + actor->exp = 80; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 47; + actor->exp = 87; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 90; + } else if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 120; + } + } +} + +void Leveled_Wolfos(PlayState* play, Actor* actor) { + if (actor->params == 0) { + actor->level = 10; + actor->exp = 24; + if (LINK_IS_ADULT) { + actor->level = 20; + actor->exp = 29; + } + if (play->sceneNum == SCENE_BMORI1) { // Forest Temple + actor->level = 26; + actor->exp = 43; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 47; + actor->exp = 158; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 165; + } + } else { // White + actor->level = 37; + actor->exp = 126; + } + if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 270; + } +} + +void Leveled_Lizalfos(PlayState* play, Actor*actor) { + if (actor->params == -2) { // Dinolfos + actor->level = 52; + actor->exp = 250; + } else if (actor->params == -1) { // Lizalfos + actor->level = 47; + actor->exp = 210; + } else { // Miniboss Lizalfos + actor->level = 13; + actor->exp = 140; + actor->ignoreExpReward = true; + } + if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 320; + } +} + +void Leveled_Moblins(PlayState* play, Actor*actor) { + if (actor->params == -1) { // Spear + actor->level = 24; + actor->exp = 32; + } else if (actor->params == 0) { // Club + actor->level = 25; + actor->exp = 47; + } else { // Spear patrol + actor->level = 24; + actor->exp = 24; + } +} + +void Leveled_Bubble(PlayState* play, Actor*actor) { + if (actor->params == -1) { // Blue + actor->level = 25; + actor->exp = 29; + } else if (actor->params == -2) { // Red + actor->level = 32; + actor->exp = 47; + if ( play->sceneNum == SCENE_HAKADAN) { // Shadow Temple + actor->level = 40; + actor->exp = 75; + } + } else if (actor->params == -3) { // White + actor->level = 45; + actor->exp = 76; + } else if (actor->params == -4) { // Big Green + actor->level = 41; + actor->exp = 82; + } else { // Green + actor->level = 26; + actor->exp = 31; + if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 41; + actor->exp = 70; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 45; + actor->exp = 77; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 85; + } + } + if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 100; + } +} + +void Leveled_Stalfos(PlayState* play, Actor*actor) { + actor->level = 27; + actor->exp = 64; + + if (play->sceneNum == SCENE_BMORI1) { // Forest Temple + actor->ignoreExpReward = true; + actor->exp = 250; + }else if (play->sceneNum == SCENE_HAKADAN) { // Shadow Temple + actor->level = 43; + actor->exp = 214; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 48; + actor->exp = 236; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON || + play->sceneNum == SCENE_GANON_SONOGO || + play->sceneNum == SCENE_GANONTIKA_SONOGO) { // Ganon's Castle + actor->level = 50; + actor->exp = 260; + } else if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 345; + } +} + +void Leveled_Floormaster(PlayState* play, Actor*actor) { + actor->level = 26; + actor->exp = 41; + actor->ignoreExpReward = true; + if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 42; + actor->exp = 140; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 47; + actor->exp = 160; + } +} + +void Leveled_Wallmaster(PlayState* play, Actor*actor) { + actor->level = 27; + actor->exp = 44; + if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 41; + actor->exp = 129; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 47; + actor->exp = 138; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 150; + } +} + +void Leveled_Poh_Sisters(PlayState* play, Actor* actor) { + actor->level = 28; + actor->exp = 127; +} + +void Leveled_LikeLike(PlayState* play, Actor* actor) { + actor->level = 32; + actor->exp = 99; + if (play->sceneNum == SCENE_MIZUSIN) { // Water Temple + actor->level = 38; + actor->exp = 142; + } else if (play->sceneNum == SCENE_HAKADANCH || + play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple + actor->level = 43; + actor->exp = 161; + } else if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 48; + actor->exp = 182; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 190; + } else if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 260; + } +} + +void Leveled_TorchSlug(PlayState* play, Actor* actor) { + actor->level = 33; + actor->exp = 67; + if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple + actor->level = 48; + actor->exp = 163; + } else if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 170; + } else if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 240; + } +} +void Leveled_Freezer(PlayState* play, Actor* actor) { + actor->level = 35; + actor->exp = 101; + if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle + actor->level = 50; + actor->exp = 160; + } +} + +void Leveled_ShellBlade(PlayState* play, Actor* actor) { + actor->level = 38; + actor->exp = 129; + if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 220; + } +} + +void Leveled_RollingSpike(PlayState* play, Actor* actor) { + actor->level = 37; + actor->exp = 100; + if (play->sceneNum == SCENE_MEN) { + actor->level = 55; + actor->exp = 200; + } +} + +void Leveled_GerudoFighter(PlayState* play, Actor* actor) { + actor->level = 45; + actor->exp = 455; +} + +void Leveled_Leever(PlayState* play, Actor* actor) { + actor->level = 45; + actor->exp = 15; +} + +void Leveled_Anubis(PlayState* play, Actor* actor) { + actor->level = 48; + if (actor->id == ACTOR_EN_ANUBICE) + actor->exp = 120; +} + + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride) { + u16 actorId = actor->id; + if (actorIdOverride != 0) { + actorId = actorIdOverride; + } + + switch (actorId) { + case ACTOR_BOSS_DODONGO: + case ACTOR_EN_DODONGO: + Leveled_Dodongo(play, actor); + break; + case ACTOR_BOSS_GOMA: + case ACTOR_EN_GOMA: + Leveled_Gohma(play, actor); + break; + case ACTOR_BOSS_VA: + Leveled_Barinade(play, actor); + break; + case ACTOR_BOSS_GANONDROF: + Leveled_PhantomGanon(play, actor); + break; + case ACTOR_BOSS_FD: + case ACTOR_BOSS_FD2: + Leveled_Volvagia(play, actor); + break; + case ACTOR_BOSS_MO: + Leveled_Morpha(play, actor); + break; + case ACTOR_BOSS_SST: + Leveled_BongoBongo(play, actor); + break; + case ACTOR_BOSS_TW: + Leveled_Twinrova(play, actor); + break; + case ACTOR_BOSS_GANON: + Leveled_Ganondorf(play, actor); + break; + case ACTOR_BOSS_GANON2: + Leveled_Ganon(play, actor); + break; + case ACTOR_EN_DEKUBABA: + Leveled_Dekubaba(play, actor); + break; + case ACTOR_EN_KAREBABA: + Leveled_StickDekuBaba(play, actor); + break; + case ACTOR_EN_ST: + Leveled_Skulltula(play, actor); + break; + case ACTOR_EN_DEKUNUTS: + Leveled_DekuScrub(play, actor); + break; + case ACTOR_EN_TITE: + Leveled_Tektite(play, actor); + break; + case ACTOR_EN_CROW: + Leveled_Guay(play, actor); + break; + case ACTOR_EN_OKUTA: + Leveled_Octorock(play, actor); + break; + case ACTOR_EN_AM: + Leveled_Armos(play, actor); + break; + case ACTOR_EN_DODOJR: + Leveled_BabyDodongo(play, actor); + break; + case ACTOR_EN_FIREFLY: + Leveled_Keese(play, actor); + break; + case ACTOR_EN_PEEHAT: + Leveled_Peahat(play, actor); + break; + case ACTOR_EN_POH: + Leveled_Poh(play, actor); + case ACTOR_EN_PO_FIELD: + Leveled_Poh_Field(play, actor); + break; + case ACTOR_EN_RD: + Leveled_ReDead(play, actor); + break; + case ACTOR_EN_SKB: + Leveled_Stalchild(play, actor); + break; + case ACTOR_EN_SW: + Leveled_Wall_Skulltula(play, actor); + break; + case ACTOR_EN_VM: + Leveled_Beamos(play, actor); + break; + case ACTOR_EN_WF: + Leveled_Wolfos(play, actor); + break; + case ACTOR_EN_ZF: + Leveled_Lizalfos(play, actor); + break; + case ACTOR_EN_BUBBLE: + Leveled_Shabom(play, actor); + break; + case ACTOR_EN_BILI: + Leveled_Biri(play, actor); + break; + case ACTOR_EN_VALI: + Leveled_Bari(play, actor); + break; + case ACTOR_EN_TP: + Leveled_Tailpasaran(play, actor); + break; + case ACTOR_EN_BA: + case ACTOR_EN_BX: + Leveled_JabuTentacle(play, actor); + break; + case ACTOR_EN_EIYER: + case ACTOR_EN_WEIYER: + Leveled_Stinger(play, actor); + break; + case ACTOR_EN_BIGOKUTA: + Leveled_BigOctorock(play, actor); + break; + case ACTOR_EN_MB: + Leveled_Moblins(play, actor); + break; + case ACTOR_EN_BB: + Leveled_Bubble(play, actor); + break; + case ACTOR_EN_TEST: + Leveled_Stalfos(play, actor); + break; + case ACTOR_EN_FLOORMAS: + Leveled_Floormaster(play, actor); + break; + case ACTOR_EN_WALLMAS: + Leveled_Wallmaster(play, actor); + break; + case ACTOR_EN_PO_SISTERS: + Leveled_Poh_Sisters(play, actor); + break; + case ACTOR_EN_RR: + Leveled_LikeLike(play, actor); + break; + case ACTOR_EN_BW: + Leveled_TorchSlug(play, actor); + break; + case ACTOR_EN_FZ: + Leveled_Freezer(play, actor); + break; + case ACTOR_EN_SB: + Leveled_ShellBlade(play, actor); + break; + case ACTOR_EN_NY: + Leveled_RollingSpike(play, actor); + break; + case ACTOR_EN_TORCH2: + Leveled_DarkLink(play, actor); + break; + case ACTOR_EN_DH: + case ACTOR_EN_DHA: + Leveled_DeadHand(play, actor); + break; + case ACTOR_EN_FD: + case ACTOR_EN_FW: + case ACTOR_EN_FD_FIRE: + Leveled_FlareDancer(play, actor); + break; + case ACTOR_EN_IK: + Leveled_IronKnuckle(play, actor); + break; + case ACTOR_EN_GELDB: + Leveled_GerudoFighter(play, actor); + break; + case ACTOR_EN_REEBA: + Leveled_Leever(play, actor); + break; + case ACTOR_EN_ANUBICE: + case ACTOR_EN_ANUBICE_FIRE: + Leveled_Anubis(play, actor); + break; + default: + actor->level = GET_PLAYER(play)->actor.level; + break; + } +} \ No newline at end of file diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c new file mode 100644 index 00000000000..8ea4ace8970 --- /dev/null +++ b/soh/src/code/leveled_overlays.c @@ -0,0 +1,811 @@ +#include "leveled_overlays.h" +#include "global.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/do_action_static/do_action_static.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/message_static/message_static.h" +#include "textures/nes_font_static/nes_font_static.h" + + + +Gfx* Gfx_Texture32(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock(displayListHead++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +Gfx* Gfx_Texture4b(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_IA, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +void Leveled_DrawTexI8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_KAL_DISP = Gfx_TextureI8(POLY_KAL_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTexIA8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_KAL_DISP = Gfx_TextureIA8(POLY_KAL_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex32(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_KAL_DISP = Gfx_Texture32(POLY_KAL_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_KAL_DISP = Gfx_Texture4b(POLY_KAL_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_OverlayDrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u16 alpha) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, alpha); + + OVERLAY_DISP = Gfx_Texture4b(OVERLAY_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_New(Actor* actor, u16 damage) { + if (damage == 0 || (actor->category == ACTORCAT_PLAYER && !CVarGetInteger("gLeveledFloatingNumberPlayerDamage", 1)) || (actor->category != ACTORCAT_PLAYER && !CVarGetInteger("gLeveledFloatingNumberEnemyDamage", 1))) + return; + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -8 }; + + actor->floatingNumber[0] = damage; + actor->floatingNumberLife[0] = 30; + actor->floatingNumberPosition[0] = position; + actor->floatingNumberVelocity[0] = velocity; +} + +void ActorExperienceNumber_New(Actor* actor, u16 experience) { + if (experience == 0 || !CVarGetInteger("gLeveledFloatingNumberExpGain", 1)) + return; + + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumber[1] = experience; + actor->floatingNumberLife[1] = 40; + actor->floatingNumberPosition[1] = position; + actor->floatingNumberVelocity[1] = velocity; +} + +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff) { + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumberLife[2] = 100; + if (powerDiff > 0) + actor->floatingNumberLife[3] = 100; + if (courageDiff > 0) + actor->floatingNumberLife[4] = 100; + if (healthDiff > 0) + actor->floatingNumberLife[5] = 100; + if (magicDiff > 0) + actor->floatingNumberLife[6] = 100; + + actor->floatingNumberPosition[2] = position; + actor->floatingNumberVelocity[2] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -4; + velocity.y = -5; + actor->floatingNumberPosition[3] = position; + actor->floatingNumberVelocity[3] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 4; + velocity.y = -5; + actor->floatingNumberPosition[4] = position; + actor->floatingNumberVelocity[4] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -2; + velocity.y = -5; + actor->floatingNumberPosition[5] = position; + actor->floatingNumberVelocity[5] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 2; + velocity.y = -5; + actor->floatingNumberPosition[6] = position; + actor->floatingNumberVelocity[6] = velocity; +} + +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[1]; + u8 digit[] = { 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[1] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 32000) + val = 32000; + + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[1].x += actor->floatingNumberVelocity[1].x; + actor->floatingNumberPosition[1].y += actor->floatingNumberVelocity[1].y; + + if (actor->floatingNumberVelocity[1].y < 0) { + actor->floatingNumberVelocity[1].y += 0.4f; + } + + // Life + actor->floatingNumberLife[1]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[1].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + if (spBC.z <= -0) { + spBC.x = 160 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.y = 200 + actor->floatingNumberPosition[1].y; + } + + spBC.z = spBC.z * 1.0f; + + // Color + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 190, 190, 0, 255); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + // Draw + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[0]; + u8 digit[] = { 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[0] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 9999) + val = 9999; + + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[0].x += actor->floatingNumberVelocity[0].x; + actor->floatingNumberPosition[0].y += actor->floatingNumberVelocity[0].y; + + actor->floatingNumberVelocity[0].y += 2.0f; + if (actor->floatingNumberPosition[0].y >= 0) { + actor->floatingNumberPosition[0].y = 0; + actor->floatingNumberVelocity[0].y = -actor->floatingNumberVelocity[0].y * 0.5f; + if (actor->floatingNumberVelocity[0].y >= -4.0f) + actor->floatingNumberVelocity[0].y = 0; + } + + // Life + actor->floatingNumberLife[0]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[0].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[0].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + // Color + if (actor->category == ACTORCAT_PLAYER) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 100, 0, 255); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + } + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width - (digits - 1) * width * 0.5f, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Actor_LevelUpDraw(PlayState* play, Actor* actor) { + u8 width = 8; + Vec3f spBC; + f32 spB4; + for (s8 i = 2; i < 7; i++) { + if (actor->floatingNumberLife[i] <= 0) + continue; + + OPEN_DISPS(play->state.gfxCtx); + + // Velocity + actor->floatingNumberPosition[i].x += actor->floatingNumberVelocity[i].x; + actor->floatingNumberPosition[i].y += actor->floatingNumberVelocity[i].y; + + if (actor->floatingNumberVelocity[i].y < 0) { + actor->floatingNumberVelocity[i].y += 0.75f; + if (actor->floatingNumberVelocity[i].y > 0) + actor->floatingNumberVelocity[i].y = 0; + } + if (actor->floatingNumberVelocity[i].x < 0) { + actor->floatingNumberVelocity[i].x += 0.5f; + if (actor->floatingNumberVelocity[i].x > 0) + actor->floatingNumberVelocity[i].x = 0; + } + if (actor->floatingNumberVelocity[i].x > 0) { + actor->floatingNumberVelocity[i].x -= 0.5f; + if (actor->floatingNumberVelocity[i].x < 0) + actor->floatingNumberVelocity[i].x = 0; + } + + // Life + actor->floatingNumberLife[i]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 165 + actor->floatingNumberPosition[i].x - 24; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[i].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + if (spBC.z < 0) { + spBC.x = 160 + actor->floatingNumberPosition[i].x - 24; + spBC.y = 200 + actor->floatingNumberPosition[i].y; + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (i == 2) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 16, -99, -99, 8, 16, + 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar45LatinCapitalLetterETex, 8, 16, -99, -99, 8, 16, + 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar56LatinCapitalLetterVTex, 8, 16, -99, -99, 8, 16, + 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar55LatinCapitalLetterUTex, 8, 16, -99, -99, 8, 16, + 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar50LatinCapitalLetterPTex, 8, 16, -99, -99, 8, 16, + 1 << 10, 1 << 10); + + for (u8 j = 0; j < 3; j++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 8, (s16)spBC.x, (s16)spBC.y, + 8, 8, 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar45LatinCapitalLetterETex, 8, 8, (s16)spBC.x + 5, + (s16)spBC.y, 8, 8, 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar56LatinCapitalLetterVTex, 8, 8, (s16)spBC.x + 10, + (s16)spBC.y, 8, 8, 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar45LatinCapitalLetterETex, 8, 8, (s16)spBC.x + 15, + (s16)spBC.y, 8, 8, 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 8, (s16)spBC.x + 20, + (s16)spBC.y, 8, 8, 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar55LatinCapitalLetterUTex, 8, 8, (s16)spBC.x + 30, + (s16)spBC.y, 8, 8, 1 << 10, 1 << 10); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)gMsgChar50LatinCapitalLetterPTex, 8, 8, (s16)spBC.x + 35, + (s16)spBC.y, 8, 8, 1 << 10, 1 << 10); + } + } + + if (i == 3) { + OVERLAY_DISP = + Gfx_Texture32(OVERLAY_DISP, (u8*)gSilverGauntletsIconTex, 32, 32, -99, -99, 32, 32, 1 << 10, 1 << 10); + + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, (u8*)gSilverGauntletsIconTex, 8, 8, (s16)spBC.x, (s16)spBC.y, 8, 8, + 1 << 10, 1 << 10); + } + if (i == 4) { + OVERLAY_DISP = + Gfx_Texture32(OVERLAY_DISP, (u8*)gHylianShieldIconTex, 32, 32, -99, -99, 32, 32, 1 << 10, 1 << 10); + + OVERLAY_DISP = + Gfx_Texture32(OVERLAY_DISP, (u8*)gHylianShieldIconTex, 8, 8, (s16)spBC.x, (s16)spBC.y, 8, 8, + 1 << 10, 1 << 10); + } + if (i == 5) { + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 70, 0, 255); + + OVERLAY_DISP = + Gfx_TextureIA8(OVERLAY_DISP, (u8*)gHeartFullTex, 16, 16, -99, -99, 16, 16, 1 << 10, 1 << 10); + + OVERLAY_DISP = + Gfx_TextureIA8(OVERLAY_DISP, (u8*)gHeartFullTex, 8, 8, (s16)spBC.x, (s16)spBC.y, 8, 8, + 1 << 10, 1 << 10); + } + + if (i == 6) { + OVERLAY_DISP = + Gfx_Texture32(OVERLAY_DISP, (u8*)gBigMagicJarIconTex, 24, 24, -99, -99, 24, 24, 1 << 10, 1 << 10); + + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, (u8*)gBigMagicJarIconTex, 8, 8, (s16)spBC.x, (s16)spBC.y, 24, 24, + 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); + } +} + +extern const char* _gAmmoDigit0Tex[]; +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_KAL_DISP++); + + for (s8 i = 0; i < digits; i++) { + POLY_KAL_DISP = + Gfx_TextureIA8(POLY_KAL_DISP, (u8*)_gAmmoDigit0Tex[digit[i]], 8, 8, x - i * 6 + 6 * (digits - 1), y, 8, + 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + u8 width = 7; + + extern const char* digitTextures[]; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, r, g, b, a); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_KAL_DISP++); + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 0, 255); + + for (j = 0; j < 4; j++) { + POLY_KAL_DISP = Gfx_TextureI8(POLY_KAL_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1) + (j % 2) * 2 - 1, y + (j / 2) * 2 - 1, 8, 16, 1 << 10, 1 << 10); + } + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + POLY_KAL_DISP = Gfx_TextureI8(POLY_KAL_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1), y, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_KaleidoEquip_Stats(PlayState* play) { + extern const char* digitTextures[]; + Player* player = GET_PLAYER(play); + u16 statY = 70; + u8 attack = 1; + if (CUR_EQUIP_VALUE(EQUIP_SWORD) == 1) + attack = HEALTH_ATTACK_MULTIPLIER; + if (CUR_EQUIP_VALUE(EQUIP_SWORD) == 2) + attack = HEALTH_ATTACK_MULTIPLIER << 1; + if (CUR_EQUIP_VALUE(EQUIP_SWORD) == 3) { + attack = HEALTH_ATTACK_MULTIPLIER << 2; + if (gBitFlags[3] & gSaveContext.inventory.equipment) + attack = HEALTH_ATTACK_MULTIPLIER; + } + + // Initialize textures + Leveled_DrawTexIA8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTexIA8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTexIA8(play, (u8*)gMsgChar2BPlusSignTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTexIA8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + + Leveled_DrawTex32(play, (u8*)gKokiriSwordIconTex, 32, 32, -192, -268, 32, 32); + Leveled_DrawTex32(play, (u8*)gSilverGauntletsIconTex, 32, 32, -192, -268, 32, 32); + Leveled_DrawTex32(play, (u8*)gHylianShieldIconTex, 32, 32, -192, -268, 32, 32); + Leveled_DrawTexIA8(play, (u8*)gHeartFullTex, 16, 16, -114, -222, 16, 16, 255, 255, 255); + Leveled_DrawTex32(play, (u8*)gBigMagicJarIconTex, 24, 24, -114, -222, 24, 24); + Leveled_DrawTexI8(play, (u8*)digitTextures[1], 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTex32(play, (u8*)gGoronsBraceletIconTex, 32, 32, -192, -268, 32, 32); + Leveled_DrawTex4b(play, (u8*)gNextDoActionENGTex, 48, 16, -192, -268, 48, 16); + + // Values and Icons + // Level + for (u8 i = 0; i < 10; i++) { + Leveled_DrawTexIA8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 8, 92, statY, 8, 16, 255, 255, 255); + Leveled_DrawTexIA8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 8, 95, statY, 8, 16, 255, 255, 255); + } + Leveled_BigValueNumberDraw(play, 100, statY - 6, player->actor.level, 255, 255, 255, 255); + statY += 10; + // Health + Leveled_DrawTexIA8(play, (u8*)gHeartFullTex, 8, 8, 92, statY, 16, 16, 255, 70, 0); + Leveled_DrawTexI8(play, (u8*)digitTextures[1], 8, 11, 116, statY - 2, 8, 16, 255, 255, 255); + Leveled_ValueNumberDraw(play, 100, statY, gSaveContext.health, 255, 255, 255); + Leveled_ValueNumberDraw(play, 123, statY, gSaveContext.healthCapacity2, 120, 255, 0); + statY += 8; + // Magic + if (gSaveContext.magicCapacity > 0) { + Leveled_DrawTex32(play, (u8*)gBigMagicJarIconTex, 8, 8, 92, statY, 24, 24); + Leveled_DrawTexI8(play, (u8*)digitTextures[1], 8, 11, 116, statY - 2, 8, 16, 255, 255, 255); + Leveled_ValueNumberDraw(play, 100, statY, gSaveContext.magic, 255, 255, 255); + Leveled_ValueNumberDraw(play, 123, statY, gSaveContext.magicCapacity, 120, 255, 0); + statY += 8; + } + // Attack + Leveled_DrawTex32(play, (u8*)gKokiriSwordIconTex, 10, 12, 90, statY - 1, 10, 8); + Leveled_ValueNumberDraw(play, 100, statY, GetActorStat_Attack(attack, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255)), 255, 255, 255); + statY += 8; + // Power + Leveled_DrawTex32(play, (u8*)gSilverGauntletsIconTex, 8, 7, 92, statY, 8, 7); + Leveled_ValueNumberDraw(play, 100, statY, player->actor.power, 255, 255, 255); + if (player->actor.powerModifier > 0){ + for (u8 i = 0; i < 10; i++) { + Leveled_DrawTexIA8(play, (u8*)gMsgChar2BPlusSignTex, 8, 8, 114, statY, 8, 16, 120, 255, 0); + } + Leveled_ValueNumberDraw(play, 118, statY, player->actor.powerModifier, 120, 255, 0); + } else if (player->actor.powerModifier < 0){ + for (u8 i = 0; i < 10; i++) { + Leveled_DrawTexIA8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 8, 114, statY, 8, 16, 255, 0, 0); + } + Leveled_ValueNumberDraw(play, 118, statY, -player->actor.powerModifier, 255, 0, 0); + } + statY += 8; + // Courage + Leveled_DrawTex32(play, (u8*)gHylianShieldIconTex, 8, 7, 92, statY, 8, 7); + Leveled_ValueNumberDraw(play, 100, statY, player->actor.courage, 255, 255, 255); + if (player->actor.courageModifier > 0){ + for (u8 i = 0; i < 10; i++) { + Leveled_DrawTexIA8(play, (u8*)gMsgChar2BPlusSignTex, 8, 8, 114, statY, 8, 16, 120, 255, 0); + } + Leveled_ValueNumberDraw(play, 118, statY, player->actor.courageModifier, 120, 255, 0); + } else if (player->actor.courageModifier < 0){ + for (u8 i = 0; i < 10; i++) { + Leveled_DrawTexIA8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 8, 114, statY, 8, 16, 255, 0, 0); + } + Leveled_ValueNumberDraw(play, 118, statY, -player->actor.courageModifier, 255, 0, 0); + } + statY += 67; + if (gSaveContext.magicCapacity > 0) { + statY -= 8; + } + // EXP + Leveled_DrawTex32(play, (u8*)gGoronsBraceletIconTex, 8, 7, 92, statY, 10, 7); + Leveled_ValueNumberDraw(play, 100, statY, gSaveContext.experience, 255, 255, 255); + statY += 8; + // Next LV + Leveled_DrawTex4b(play, (u8*)gNextDoActionENGTex, 24, 10, 84, statY, 24, 7); + Leveled_ValueNumberDraw(play, 112, statY, + GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience), 255, 255, 255); +} + +void Leveled_Interface_DrawNextLevel(PlayState* play) { + if (gSaveContext.showNeededExpTimer > 0){ + gSaveContext.showNeededExpTimer--; + } else { + return; + } + if (play->pauseCtx.state != 0) + return; + + Player* player = GET_PLAYER(play); + u16 posX = OTRGetRectDimensionFromLeftEdge(10); + u16 posY = 58; + extern const char* digitTextures[]; + s32 val = GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience); + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + s32 j; + + Leveled_OverlayDrawTex4b(play, (u8*)gNextDoActionENGTex, 48, 16, -192, -268, 48, 16, 255 - play->pauseCtx.alpha); // Load texture + Leveled_OverlayDrawTex4b(play, (u8*)gNextDoActionENGTex, 48, 16, posX, posY, 48, 16, 255 - play->pauseCtx.alpha); // Draw texture + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 999999) + val = 999999; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Color + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + s16 numbersPosX = 38; + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, 255 - play->pauseCtx.alpha); + + for (j = 0; j < 4; j++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, posX - i * width + width * (digits - 1) + numbersPosX + (j % 2) * 2 - 1, posY + (j / 2) * 2 - 1, 8, 16, 1 << 10, 1 << 10); + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255 - play->pauseCtx.alpha); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, posX - i * width + width * (digits - 1) + numbersPosX, posY, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} \ No newline at end of file diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c new file mode 100644 index 00000000000..4c2d59acb8c --- /dev/null +++ b/soh/src/code/leveled_stat_math.c @@ -0,0 +1,253 @@ +#include "global.h" + +static u32 sExpTable[] = { 0, 30, 65, 110, 167, 240, 334, 452, 598, 777, 993, + 1251, 1555, 1910, 2320, 2791, 3329, 3937, 4621, 5388, 6242, 7189, + 8235, 9386, 10647, 12026, 13528, 15160, 16929, 18840, 20902, 23120, 25501, + 28054, 30784, 33700, 36809, 40119, 43637, 47371, 51329, 55520, 59951, 64630, + 69567, 74770, 80248, 86008, 92061, 98416, 105080, 112065, 119379, 127032, 135033, + 143392, 152119, 161225, 170719, 180611, 190912, 201632, 212783, 224374, 236417, 248922, + 261901, 275365, 289325, 303792, 318779, 334297, 350358, 366973, 384155, 401916, 420268, + 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, + 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 }; +/* Old table +{ 0, 40, 89, 154, 241, 355, 504, 694, 930, 1219, 1568, 1982, 2468, + 3033, 3681, 4420, 5257, 6196, 7245, 8409, 9695, 11110, 12659, 14349, 16185, 18175, + 20324, 22640, 25127, 27792, 30642, 33683, 36921, 40362, 44012, 47879, 51967, 56284, 60836, + 65628, 70668, 75961, 81513, 87332, 93422, 99792, 106445, 113390, 120632, 128177, 136032, 144203, + 152697, 161518, 170675, 180173, 190017, 200216, 210774, 221698, 232995, 244670, 256730, 269181, 282030, + 295282, 308944, 323022, 337522, 352451, 367815, 383621, 399873, 416580, 433746, 451379, 469484, 488067, + 507136, 526696, 546754, 567315, 588386, 609974, 632084, 654723, 677897, 701612, 725875, 750692, 776069, + 802012, 828528, 855623, 883303, 911574, 940443, 969916, 999999 };*/ + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power) { + return GetActorStat_Attack(attack, power) / (1 + (float)power / 30.0f); +} + +u16 GetActorStat_Attack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.14f + (power - 2) * 0.0006f)); +} + +f32 GetActorStat_EnemyAttack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.007f + (power - 2) * 0.0002f)); +} + +u8 GetActorStat_Power(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u8 GetActorStat_Courage(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerPower(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerCourage(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level){ + return GetActorStat_Attack(baseHealth * HEALTH_ATTACK_MULTIPLIER, GetActorStat_PlayerPower(level)); +} + +u8 GetPlayerStat_BonusHearts(u8 level){ + if (CVarGetInteger("gLeveledHeartsWithLevelUp", 1) == 0){ + return 0; + } + + u8 bonusHearts = (level + 1) / 8; + if (bonusHearts > 10){ + bonusHearts = 10; + } + return bonusHearts; +} + +u8 GetPlayerStat_MagicUnits(u8 level){ + if (CVarGetInteger("gLeveledMagicWithLevelUp", 1) == 0){ + return 48; + } + + u8 maximumMagic = 12 + (u8)((f32)level / 2.8f) * 2; + if (maximumMagic > 72){ + maximumMagic = 72; + } + return maximumMagic; +} + +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level){ + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + u16 baseHearts = baseHealth / 16; + return (baseHearts + GetPlayerStat_BonusHearts(level)) * heartUnits; +} + +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - sExpTable[level - 1]; +} + +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - currentExp; + + if (nextLv < 0) + nextLv = 0; + return nextLv; +} + +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) { + f32 damage = GetActorStat_Attack(attack, power); + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.01f; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.97f; + } + } + return damage; +} + +f32 Leveled_DamageFormulaOnPlayer(f32 attack, u8 power, u8 courage) { + f32 damage = attack; + + if (CVarGetInteger("gLeveledEnemyAttackScalesWithLevel", 1) == 1){ + damage = GetActorStat_EnemyAttack(attack, power); + + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + f32 multAddition = CLAMP_MIN((0.07f - (power * 0.0005f)) - (f32)i / (100.0f - (power * 0.33f)), 0); + damage *= 1.04f + multAddition; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } else { + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.05f + CLAMP_MIN(0.05f - (f32)i / 100.0f, 0); + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } + return damage; +} + +f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack) { + f32 damage; + if (actor->category == ACTORCAT_PLAYER) { + damage = Leveled_DamageFormulaOnPlayer(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } else { + damage = Leveled_DamageFormula(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } + + + if (damage >= 1.25f) + damage += Rand_ZeroOne() - 0.2f; + + if (damage >= 6) + damage += Rand_ZeroFloat(damage * 0.12f) - damage * 0.06f; + + if (damage > 9999) + damage = 9999; + + damage = (u16)(damage + 0.5f); + + return CLAMP_MIN(damage, attack > 0 ? 1 : 0); +} + +u16 Leveled_GoldSkulltulaExperience(u8 tokens) { + u16 experience = 5; + u8 i; + + for (i = 0; i < tokens; i++) { + experience += 5 + 5 * (u16)((f32)i / 10.0); + } + return experience; +} + +void Leveled_SetPlayerModifiedStats(Player* player) { + s8 powerModifier = 0; + s8 courageModifier = 0; + + if (CVarGetInteger("gLeveledEquipmentStats", 1) == 1){ + switch (CUR_EQUIP_VALUE(EQUIP_SWORD)){ + case PLAYER_SWORD_MASTER: + courageModifier += 1; + break; + + case PLAYER_SWORD_BGS: + if (gBitFlags[3] & gSaveContext.inventory.equipment){ + powerModifier -= 7; + courageModifier -= 12; + } else { + powerModifier += 2; + courageModifier -= 8; + } + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1){ + case PLAYER_TUNIC_GORON: + powerModifier += 3; + courageModifier -= 3; + break; + + case PLAYER_TUNIC_ZORA: + powerModifier -= 3; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_SHIELD)){ + case PLAYER_SHIELD_DEKU: + courageModifier += 1; + break; + + case PLAYER_SHIELD_HYLIAN: + courageModifier += 2; + break; + + case PLAYER_SHIELD_MIRROR: + powerModifier -= 2; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_BOOTS) - 1){ + case PLAYER_BOOTS_IRON: + powerModifier += 2; + courageModifier += 1; + break; + + case PLAYER_BOOTS_HOVER: + courageModifier -= 1; + break; + + default: + break; + } + } + + player->actor.powerModifier = powerModifier; + player->actor.courageModifier = courageModifier; +} \ No newline at end of file diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 3b3b4a970f7..29d8cff680f 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1183,12 +1183,35 @@ void Actor_SetObjectDependency(PlayState* play, Actor* actor) { gSegments[6] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[actor->objBankIndex].segment); } +void Actor_RefreshLeveledStats(Actor* actor, Player* player) { + if (actor->category == ACTORCAT_PLAYER) { + actor->power = GetActorStat_PlayerPower(actor->level); + actor->courage = GetActorStat_PlayerCourage(actor->level); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, actor->level); + gSaveContext.magicUnits = GetPlayerStat_MagicUnits(actor->level); + Leveled_SetPlayerModifiedStats(player); + } else { + actor->power = GetActorStat_Power(actor->level); + actor->powerModifier = 1; + actor->courage = GetActorStat_Courage(actor->level); + actor->courageModifier = 0; + } +} + void Actor_Init(Actor* actor, PlayState* play) { Actor_SetWorldToHome(actor); Actor_SetShapeRotToWorld(actor); Actor_SetFocus(actor, 0.0f); Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); Actor_SetScale(actor, 0.01f); + actor->level = 0; + actor->exp = 0; + actor->ignoreExpReward = false; + for (u8 i = 0; i < 5; i++) { + actor->floatingNumber[i] = 0; + actor->floatingNumberLife[i] = 0; + } actor->targetMode = 3; actor->minVelocityY = -20.0f; actor->xyzDistToPlayerSq = FLT_MAX; @@ -1212,6 +1235,13 @@ void Actor_Init(Actor* actor, PlayState* play) { //Actor_SetObjectDependency(play, actor); actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + actor->maximumHealth = actor->colChkInfo.health; + } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } } @@ -2136,7 +2166,12 @@ s32 Actor_NotMounted(PlayState* play, Actor* horse) { void func_8002F698(PlayState* play, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6) { Player* player = GET_PLAYER(play); - player->unk_8A0 = arg6; + u16 damage = arg6; + if (actor != NULL && damage != 0) { + damage = Leveled_DamageModify(&player->actor, actor, arg6); + } + + player->unk_8A0 = damage; player->unk_8A1 = arg5; player->unk_8A2 = arg3; player->unk_8A4 = arg2; @@ -3438,6 +3473,9 @@ Actor* Actor_Find(ActorContext* actorCtx, s32 actorId, s32 actorCategory) { */ void Enemy_StartFinishingBlow(PlayState* play, Actor* actor) { play->actorCtx.freezeFlashTimer = 5; + if (!actor->ignoreExpReward) { + Player_GainExperience(play, actor->exp); + } SoundSource_PlaySfxAtFixedWorldPos(play, &actor->world.pos, 20, NA_SE_EN_LAST_DAMAGE); } @@ -4546,12 +4584,15 @@ u8 func_800355E4(PlayState* play, Collider* collider) { } } -u8 Actor_ApplyDamage(Actor* actor) { +u16 Actor_ApplyDamage(Actor* actor) { if (actor->colChkInfo.damage >= actor->colChkInfo.health) { actor->colChkInfo.health = 0; } else { actor->colChkInfo.health -= actor->colChkInfo.damage; } + if (actor->colChkInfo.damage > 0) { + ActorDamageNumber_New(actor, actor->colChkInfo.damage); + } return actor->colChkInfo.health; } diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c index c4716c947fa..6aef1f42fda 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -3044,6 +3044,15 @@ void CollisionCheck_ApplyDamage(PlayState* play, CollisionCheckContext* colChkCt collider->actor->colChkInfo.damageEffect = tbl->table[i] >> 4 & 0xF; } if (!(collider->acFlags & AC_HARD)) { + Actor* attacker = collider->ac; + + if (collider->actor->category != ACTORCAT_PLAYER) + damage *= HEALTH_ATTACK_MULTIPLIER; + + if (info->acHit->atFlags & AT_TYPE_PLAYER) + attacker = &GET_PLAYER(play)->actor; + + damage = (u16)Leveled_DamageModify(collider->actor, attacker, damage); collider->actor->colChkInfo.damage += damage; } diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index cfe5a96d3aa..08ec717c459 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1595,7 +1595,7 @@ s16 func_8001F404(s16 dropId) { } // clang-format on - if (dropId == ITEM00_HEART && gSaveContext.healthCapacity == gSaveContext.health) { + if (dropId == ITEM00_HEART && gSaveContext.healthCapacity2 == gSaveContext.health) { return ITEM00_RUPEE_GREEN; } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 8d5e41a2063..c5766dd8dee 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -385,12 +385,17 @@ void HealthMeter_Draw(PlayState* play) { f32 temp2; f32 temp3; f32 temp4; + u8 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + if (heartUnits < 4) { + heartUnits = 4; + CVarSetInteger("gLeveledHeartUnits", 1); + } InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = gSaveContext.health % 0x10; - s16 totalHeartCount = gSaveContext.healthCapacity / 0x10; - s16 fullHeartCount = gSaveContext.health / 0x10; + s32 curHeartFraction = (s32)((f32)gSaveContext.health / heartUnits * 16) % 0x10; + s16 totalHeartCount = gSaveContext.healthCapacity2 / heartUnits; + s16 fullHeartCount = gSaveContext.health / heartUnits; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -405,7 +410,7 @@ void HealthMeter_Draw(PlayState* play) { OPEN_DISPS(gfxCtx); - if (!(gSaveContext.health % 0x10)) { + if (!(gSaveContext.health % heartUnits)) { fullHeartCount--; } @@ -615,7 +620,7 @@ void HealthMeter_Draw(PlayState* play) { } offsetX += 10.0f; - s32 lineLength = CVarGetInteger("gHeartsLineLength", 10); + s32 lineLength = CVarGetInteger("gHeartsLineLength", 15); if (lineLength != 0 && (i+1)%lineLength == 0) { offsetX = PosX_anchor; offsetY += 10.0f; @@ -652,15 +657,9 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { u32 HealthMeter_IsCritical(void) { s32 var; - if (gSaveContext.healthCapacity <= 0x50) { - var = 0x10; - } else if (gSaveContext.healthCapacity <= 0xA0) { - var = 0x18; - } else if (gSaveContext.healthCapacity <= 0xF0) { - var = 0x20; - } else { - var = 0x2C; - } + s32 heartValue = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + + var = (s32)(heartValue + (f32)(((gSaveContext.healthCapacity2 / heartValue) - 3) * heartValue) * 0.103f); if ((var >= gSaveContext.health) && (gSaveContext.health > 0)) { return true; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 327e85df2d8..3251194fbce 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -1663,6 +1663,11 @@ void Message_OpenText(PlayState* play, u16 textId) { textId == 0x4D)) { Message_FindMessage(play, textId); msgCtx->msgLength = font->msgLength = GetEquipNowMessage(font->msgBuf, font->msgOffset, sizeof(font->msgBuf)); + } else if ((CVarGetInteger("gLeveledNaviLevel", 1) || CVarGetInteger("gLeveledNaviMaxHP", 1)) && + (textId > 0x0600 && textId < 0x06FF) && play->actorCtx.targetCtx.targetedActor != NULL) { + Message_FindMessage(play, textId); + msgCtx->msgLength = font->msgLength = GetLeveledNaviEnemyInfo( + font->msgBuf, font->msgOffset, sizeof(font->msgBuf), play->actorCtx.targetCtx.targetedActor); } else { Message_FindMessage(play, textId); msgCtx->msgLength = font->msgLength; @@ -3322,7 +3327,7 @@ void Message_Update(PlayState* play) { } if ((msgCtx->textId >= 0xC2 && msgCtx->textId < 0xC7) || (msgCtx->textId >= 0xFA && msgCtx->textId < 0xFE)) { - gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; // Refill 20 hearts } if (msgCtx->textId == 0x301F || msgCtx->textId == 0xA || msgCtx->textId == 0xC || msgCtx->textId == 0xCF || msgCtx->textId == 0x21C || msgCtx->textId == 9 || msgCtx->textId == 0x4078 || @@ -3359,9 +3364,10 @@ void Message_Update(PlayState* play) { msgCtx->textboxEndType = TEXTBOX_ENDTYPE_DEFAULT; } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; gSaveContext.inventory.questItems ^= 0x40000000; gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { if (sLastPlayedSong == OCARINA_SONG_SARIAS) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 3895cb3a940..d9d555e77e9 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2223,14 +2223,17 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.sohStats.heartPieces++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART_CONTAINER) { + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; gSaveContext.sohStats.heartContainers++; + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - Health_ChangeBy(play, 0x10); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + Health_ChangeBy(play, heartUnits); } return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_SMALL) { @@ -2366,7 +2369,7 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { slot = SLOT(item); if (item == RG_MAGIC_SINGLE) { gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = 0x30; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Magic_Fill(play); return Return_Item_Entry(giEntry, RG_NONE); } else if (item == RG_MAGIC_DOUBLE) { @@ -2374,7 +2377,7 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = 0x60; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Magic_Fill(play); return Return_Item_Entry(giEntry, RG_NONE); @@ -2391,7 +2394,7 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { if (item == RG_DOUBLE_DEFENSE) { gSaveContext.isDoubleDefenseAcquired = true; gSaveContext.inventory.defenseHearts = 20; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; return Return_Item_Entry(giEntry, RG_NONE); } @@ -3008,13 +3011,17 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } } + if (healthChange < 0) { + ActorDamageNumber_New(GET_PLAYER(play), -healthChange); + } + gSaveContext.health += healthChange; - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; } - - heartCount = gSaveContext.health % 0x10; + + heartCount = gSaveContext.health / CVarGetInteger("gLeveledHeartUnits", 4) << 2; healthLevel = heartCount; if (heartCount != 0) { @@ -3149,7 +3156,7 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { void Magic_Fill(PlayState* play) { if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; - gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30; + gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; gSaveContext.magicState = 9; } } @@ -3288,7 +3295,7 @@ void Interface_UpdateMagicBar(PlayState* play) { switch (gSaveContext.magicState) { case 8: - temp = gSaveContext.magicLevel * 0x30; + temp = gSaveContext.magicLevel * gSaveContext.magicUnits; if (gSaveContext.magicCapacity != temp) { if (gSaveContext.magicCapacity < temp) { gSaveContext.magicCapacity += 8; @@ -3316,6 +3323,9 @@ void Interface_UpdateMagicBar(PlayState* play) { // "Storage MAGIC_NOW=%d (%d)" osSyncPrintf("蓄電 MAGIC_NOW=%d (%d)\n", gSaveContext.magic, gSaveContext.magicFillTarget); + if (gSaveContext.magicFillTarget > gSaveContext.magicCapacity) { + gSaveContext.magicFillTarget = gSaveContext.magicCapacity; + } if (gSaveContext.magic >= gSaveContext.magicFillTarget) { gSaveContext.magic = gSaveContext.magicFillTarget; gSaveContext.magicState = gSaveContext.prevMagicState; @@ -3538,7 +3548,7 @@ void Interface_DrawMagicBar(PlayState* play) { s16 rMagicBarX; s16 PosX_MidEnd; s16 rMagicFillX; - s32 lineLength = CVarGetInteger("gHeartsLineLength", 10); + s32 lineLength = CVarGetInteger("gHeartsLineLength", 15); if (CVarGetInteger("gMagicBarPosType", 0) != 0) { magicBarY = CVarGetInteger("gMagicBarPosY", 0)+Y_Margins; if (CVarGetInteger("gMagicBarPosType", 0) == 1) {//Anchor Left @@ -3565,7 +3575,8 @@ void Interface_DrawMagicBar(PlayState* play) { rMagicFillX = -9999; } else if (CVarGetInteger("gMagicBarPosType", 0) == 5) {//Anchor To life meter magicBarY = R_MAGIC_BAR_SMALL_Y-2 + - magicDrop*(lineLength == 0 ? 0 : (gSaveContext.healthCapacity-1)/(0x10*lineLength)) + + magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity2 - 1) / + ((CVarGetInteger("gLeveledHeartUnits", 4) << 2) * lineLength)) + CVarGetInteger("gMagicBarPosY", 0) + getHealthMeterYOffset(); s16 xPushover = CVarGetInteger("gMagicBarPosX", 0) + getHealthMeterXOffset() + R_MAGIC_BAR_X-1; PosX_Start = xPushover; @@ -3574,9 +3585,11 @@ void Interface_DrawMagicBar(PlayState* play) { rMagicFillX = CVarGetInteger("gMagicBarPosX", 0) + getHealthMeterXOffset() + R_MAGIC_FILL_X-1; } } else { - if ((gSaveContext.healthCapacity-1)/0x10 >= lineLength && lineLength != 0) { + if ((gSaveContext.healthCapacity2 - 1) / (CVarGetInteger("gLeveledHeartUnits", 4) << 2) >= lineLength && + lineLength != 0) { magicBarY = magicBarY_original_l + - magicDrop*(lineLength == 0 ? 0 : ((gSaveContext.healthCapacity-1)/(0x10*lineLength) - 1)); + magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity2 - 1) / + ((CVarGetInteger("gLeveledHeartUnits", 4) << 2) * lineLength) - 1)); } else { magicBarY = magicBarY_original_s; } @@ -4867,7 +4880,7 @@ void Interface_Draw(PlayState* play) { if (pauseCtx->debugState == 0) { Interface_InitVertices(play); func_8008A994(interfaceCtx); - if (fullUi || gSaveContext.health != gSaveContext.healthCapacity) { + if (fullUi || gSaveContext.health != gSaveContext.healthCapacity2) { HealthMeter_Draw(play); } @@ -5093,6 +5106,50 @@ void Interface_Draw(PlayState* play) { gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } func_8002C124(&play->actorCtx.targetCtx, play); // Draw Z-Target + + // Draw Damage + Actor* currAct = play->actorCtx.actorLists[ACTORCAT_ENEMY].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_MISC].head; + if (currAct != NULL) { + while (currAct != NULL) { + if (currAct->id == ACTOR_EN_REEBA) + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_NPC].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_BOSS].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + ActorDamageNumber_Draw(play, GET_PLAYER(play)); + + // Draw Experience Gain + + ActorExperienceNumber_Draw(play, GET_PLAYER(play)); + + // Draw Level Up + Actor_LevelUpDraw(play, GET_PLAYER(play)); + if (CVarGetInteger("gMirroredWorld", 0)) { gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } @@ -5190,6 +5247,8 @@ void Interface_Draw(PlayState* play) { gDPPipeSync(OVERLAY_DISP++); + Leveled_Interface_DrawNextLevel(play); // Draw next level + // C-Left Button Icon & Ammo Count if (gSaveContext.equips.buttonItems[1] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); @@ -6239,17 +6298,18 @@ void Interface_Update(PlayState* play) { Map_Update(play); if (gSaveContext.healthAccumulator != 0) { - gSaveContext.healthAccumulator -= 4; - gSaveContext.health += 4; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator -= heartUnits * 0.25f; + gSaveContext.health += heartUnits * 0.25f; - if ((gSaveContext.health & 0xF) < 4) { + if ((gSaveContext.health % (heartUnits)) < heartUnits * 0.25f) { Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); } osSyncPrintf("now_life=%d max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); - if (gSaveContext.health >= gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health >= gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; osSyncPrintf("S_Private.now_life=%d S_Private.max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); gSaveContext.healthAccumulator = 0; diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index ed9b359e186..2887bff7dc7 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -440,6 +440,7 @@ void Player_SetEquipmentData(PlayState* play, Player* this) { this->currentSwordItemId = B_BTN_ITEM; Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemAction)); Player_SetBootData(play, this); + Leveled_SetPlayerModifiedStats(this); } } @@ -517,6 +518,48 @@ s32 Player_GetStrength(void) { } } +void Player_GainExperience(PlayState* play, u16 experience) { + Player* player = GET_PLAYER(play); + bool levelUp = false; + u8 prevPower = player->actor.power; + u8 prevCourage = player->actor.courage; + u16 prevHealthCapacity = gSaveContext.healthCapacity2; + u16 prevMagicUnits = gSaveContext.magicUnits; + + if (player->actor.level == 99) + return; + + if (gSaveContext.experience < 999999) { + if (experience > 0) + gSaveContext.showNeededExpTimer = 60; + + gSaveContext.experience += experience; + ActorExperienceNumber_New(&player->actor, experience); + if (gSaveContext.experience > 999999) + gSaveContext.experience = 999999; + } + + while (GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience) <= 0 && player->actor.level < 99) { + player->actor.level += 1; + if (experience > 0) { + levelUp = true; + } + } + + Actor_RefreshLeveledStats(&player->actor, player); + + if (gSaveContext.magicLevel == 0) { + prevMagicUnits = gSaveContext.magicUnits; + } + + if (levelUp) { + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; + ActorLevelUp_New(&player->actor, player->actor.power - prevPower, player->actor.courage - prevCourage, + gSaveContext.healthCapacity2 - prevHealthCapacity, gSaveContext.magicUnits - prevMagicUnits); + Audio_PlayFanfare(NA_BGM_ITEM_GET); + } +} + u8 Player_GetMask(PlayState* play) { Player* this = GET_PLAYER(play); diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 7e22f898912..da353e26beb 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -120,8 +120,8 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 0x30) { - gSaveContext.health = CVarGetInteger("gFullHealthSpawn", 0) ? gSaveContext.healthCapacity : 0x30; + if (gSaveContext.health < 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2) { + gSaveContext.health = CVarGetInteger("gFullHealthSpawn", 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; } if (gSaveContext.scarecrowLongSongSet) { diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c index aeee629fdfe..3abbf978b1f 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -200,7 +200,7 @@ void BgDyYoseizo_CheckMagicAcquired(BgDyYoseizo* this, PlayState* play) { play->msgCtx.ocarinaMode = OCARINA_MODE_04; if(gSaveContext.n64ddFlag) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); if(Flags_GetTreasure(play, this->fountainType + 1)) { Actor_Kill(&this->actor); @@ -492,7 +492,7 @@ void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, PlayState* play) { } if (this->healingTimer == 110) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->refillTimer = 200; } @@ -741,7 +741,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { switch (actionIndex) { case FAIRY_UPGRADE_MAGIC: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = 0x30; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Interface_ChangeAlpha(9); break; case FAIRY_UPGRADE_DOUBLE_MAGIC: @@ -749,7 +749,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = 0x60; + gSaveContext.magicFillTarget = gSaveContext.magicUnits << 1; gSaveContext.magicLevel = 0; Interface_ChangeAlpha(9); break; @@ -760,7 +760,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } if (!this->healing) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->healing = true; if (actionIndex == 2) { Magic_Fill(play); @@ -789,7 +789,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } this->itemSpawned = true; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Interface_ChangeAlpha(9); gSaveContext.itemGetInf[1] |= sItemGetFlags[actionIndex]; Item_Give(play, sItemIds[actionIndex]); diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index 4a015a4f872..8b23babdf6b 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -177,7 +177,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Animation_PlayLoop(&this->skelAnime, &object_kingdodongo_Anim_00F0D8); this->unk_1F8 = 1.0f; BossDodongo_SetupIntroCutscene(this, play); - this->health = 12; + Actor_GetLevelAndExperience(play, &this->actor, 0); + this->health = GetActorStat_EnemyMaxHealth(12, this->actor.level); this->colorFilterMin = 995.0f; this->actor.colChkInfo.mass = MASS_IMMOVABLE; this->colorFilterMax = 1000.0f; @@ -186,6 +187,9 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Collider_InitJntSph(play, &this->collider); Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items); + for (u8 i = 0; i < this->collider.count; i++) + this->collider.elements[i].info.toucher.damage <<= 1; // Double fire damage (to 1 heart) + if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead u16* LavaFloorTex = ResourceGetDataByName(gDodongosCavernBossLavaFloorTex); u16* LavaFloorRockTex = ResourceGetDataByName(sLavaFloorRockTex); @@ -572,7 +576,9 @@ void BossDodongo_Explode(BossDodongo* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); func_80033E88(&this->actor, play, 4, 10); - this->health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + this->health -= damage; + ActorDamageNumber_New(&this->actor, damage); // make sure not to die from the bomb explosion if (this->health <= 0) { @@ -1257,7 +1263,7 @@ void BossDodongo_SpawnFire(BossDodongo* this, PlayState* play, s16 params) { void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { s32 pad; ColliderInfo* item1; - u8 swordDamage; + u16 swordDamage; s32 damage; ColliderInfo* item2; s16 i; @@ -1292,11 +1298,14 @@ void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { if ((this->actionFunc == BossDodongo_Vulnerable) || (this->actionFunc == BossDodongo_LayDown)) { swordDamage = damage = CollisionCheck_GetSwordDamage(item1->toucher.dmgFlags, play); + swordDamage = Leveled_DamageModify(&this->actor, this->collider.base.actor, swordDamage * HEALTH_ATTACK_MULTIPLIER); + if (damage != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); BossDodongo_SetupDamaged(this); this->unk_1C0 = 5; this->health -= swordDamage; + ActorDamageNumber_New(&this->actor, swordDamage); } } } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index e56858317b2..5b6cabc5ddd 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -1288,12 +1288,15 @@ void BossFd_CollisionCheck(BossFd* this, PlayState* play) { if (headCollider->info.bumperFlags & BUMP_HIT) { headCollider->info.bumperFlags &= ~BUMP_HIT; hurtbox = headCollider->info.acHitInfo; - this->actor.colChkInfo.health -= 2; - if (hurtbox->toucher.dmgFlags & 0x1000) { - this->actor.colChkInfo.health -= 2; - } - if ((s8)this->actor.colChkInfo.health <= 2) { - this->actor.colChkInfo.health = 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + if (hurtbox->toucher.dmgFlags & 0x1000) + damage *= 2; + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health - 1 > damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; } this->work[BFD_DAMAGE_FLASH_TIMER] = 10; this->work[BFD_INVINC_TIMER] = 20; @@ -1479,7 +1482,8 @@ void BossFd_UpdateEffects(BossFd* this, PlayState* play) { diff.z = player->actor.world.pos.z - effect->pos.z; if ((this->timers[3] == 0) && (sqrtf(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)) < 20.0f)) { this->timers[3] = 50; - func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, 0x30); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 0x30); + func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, damage); if (player->isBurning == false) { for (i2 = 0; i2 < ARRAY_COUNT(player->flameTimers); i2++) { player->flameTimers[i2] = Rand_S16Offset(0, 200); diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index b001f09e875..6eef2edd257 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -205,7 +205,7 @@ void BossFd2_Destroy(Actor* thisx, PlayState* play) { void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; s16 temp_rand; - s8 health; + u16 health; osSyncPrintf("UP INIT 1\n"); Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaEmergeAnim); @@ -219,11 +219,11 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { this->timers[0] = 10; if (bossFd != NULL) { health = bossFd->actor.colChkInfo.health; - if (health >= 18) { + if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 0; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 1; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 2; } else { this->work[FD2_FAKEOUT_COUNT] = 3; @@ -232,7 +232,7 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { } void BossFd2_Emerge(BossFd2* this, PlayState* play) { - s8 health; + u16 health; BossFd* bossFd = (BossFd*)this->actor.parent; Player* player = GET_PLAYER(play); s16 i; @@ -256,13 +256,13 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { this->work[FD2_HOLE_COUNTER]++; this->actor.world.pos.y = -200.0f; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { holeTime = 30; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { holeTime = 25; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { holeTime = 20; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { holeTime = 10; } else { holeTime = 5; @@ -322,13 +322,13 @@ void BossFd2_SetupIdle(BossFd2* this, PlayState* play) { Animation_PlayLoop(&this->skelAnime, &gHoleVolvagiaTurnAnim); this->actionFunc = BossFd2_Idle; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { idleTime = 50; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { idleTime = 40; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { idleTime = 40; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { idleTime = 30; } else { idleTime = 20; @@ -382,7 +382,7 @@ void BossFd2_Burrow(BossFd2* this, PlayState* play) { } else { Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 10.0f); if (this->timers[0] == 0) { - if ((this->work[FD2_HOLE_COUNTER] >= 3) && ((s8)bossFd->actor.colChkInfo.health < 24)) { + if ((this->work[FD2_HOLE_COUNTER] >= 3) && (bossFd->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(24, this->actor.level))) { this->work[FD2_HOLE_COUNTER] = 0; this->actionFunc = BossFd2_Wait; bossFd->handoffSignal = FD2_SIGNAL_FLY; @@ -845,9 +845,13 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { hurtbox = this->collider.elements[0].info.acHitInfo; if (!bossFd->faceExposed) { if (hurtbox->toucher.dmgFlags & 0x40000040) { - bossFd->actor.colChkInfo.health -= 2; - if ((s8)bossFd->actor.colChkInfo.health <= 2) { - bossFd->actor.colChkInfo.health = 1; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + + ActorDamageNumber_New(&this->actor, damage); + if (this->actor.colChkInfo.health > damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; } bossFd->faceExposed = true; BossFd2_SetupVulnerable(this, play); @@ -873,7 +877,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { } } else { u8 canKill = false; - u8 damage; + u16 damage; if ((damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play)) == 0) { damage = (hurtbox->toucher.dmgFlags & 0x00001000) ? 4 : 2; @@ -883,15 +887,21 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { if (hurtbox->toucher.dmgFlags & 0x80) { damage = 0; } - if (((s8)bossFd->actor.colChkInfo.health > 2) || canKill) { - bossFd->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + if ((bossFd->actor.colChkInfo.health > 1) || canKill) { + if (bossFd->actor.colChkInfo.health >= damage) { + ActorDamageNumber_New(&this->actor, damage); + bossFd->actor.colChkInfo.health -= damage; + } else { + bossFd->actor.colChkInfo.health = 0; + } osSyncPrintf(VT_FGCOL(GREEN)); osSyncPrintf("damage %d\n", damage); } osSyncPrintf(VT_RST); osSyncPrintf("hp %d\n", bossFd->actor.colChkInfo.health); - if ((s8)bossFd->actor.colChkInfo.health <= 0) { + if (bossFd->actor.colChkInfo.health <= 0) { bossFd->actor.colChkInfo.health = 0; BossFd2_SetupDeath(this, play); this->work[FD2_DAMAGE_FLASH_TIMER] = 10; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index cac446b70e8..151d3345fe9 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -582,7 +582,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { this->unk_198 = 2; this->timers[2] = 110; if (!(gSaveContext.isBossRush && gSaveContext.bossRushOptions[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_NEVER)) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } Audio_QueueSeqCmd(NA_BGM_STOP); } else { @@ -799,7 +799,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { } if (this->csTimer == 25) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } if (this->csTimer == 100) { @@ -1217,7 +1217,7 @@ void BossGanon_SetupTowerCutscene(BossGanon* this, PlayState* play) { this->csState = 100; this->unk_198 = 1; gSaveContext.magic = gSaveContext.magicCapacity; - gSaveContext.health = gSaveContext.healthCapacity; + gSaveContext.health = gSaveContext.healthCapacity2; } else { this->actionFunc = BossGanon_SetupTowerCutscene; } @@ -2282,7 +2282,7 @@ void BossGanon_Wait(BossGanon* this, PlayState* play) { } else if ((this->timers[0] == 0) && !(player->stateFlags1 & 0x2000)) { this->timers[0] = (s16)Rand_ZeroFloat(30.0f) + 30; - if ((s8)this->actor.colChkInfo.health >= 20) { + if (this->actor.colChkInfo.health >= GetActorStat_EnemyMaxHealth(20, this->actor.level)) { BossGanon_SetupChargeLightBall(this, play); } else if (Rand_ZeroOne() >= 0.5f) { if ((Rand_ZeroOne() >= 0.5f) || (this->actor.xzDistToPlayer > 350.0f)) { @@ -2774,7 +2774,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } else if ((this->actionFunc == BossGanon_Vulnerable) && (this->unk_1C2 >= 3)) { if (!(acHitInfo->toucher.dmgFlags & 0x80)) { u8 hitWithSword = false; - u8 damage; + u16 damage; Vec3f sp50; u32 flags; @@ -2794,10 +2794,24 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { hitWithSword = true; } - if (((s8)this->actor.colChkInfo.health >= 3) || hitWithSword) { - this->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + ActorDamageNumber_New(&this->actor, damage); + + if (hitWithSword) { + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + } else { + if (this->actor.colChkInfo.health > damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } } + for (i = 0; i < ARRAY_COUNT(sBossGanonCape->strands); i++) { for (j = 1; j < 12; j++) { sBossGanonCape->strands[i].velocities[j].x = Rand_CenteredFloat(15.0f); @@ -2805,8 +2819,9 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanon_SetupDeathCutscene(this, play); + Player_GainExperience(play, this->actor.exp); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER); func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index 6d5256b9fb2..014ea50c8bd 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -1453,7 +1453,7 @@ void func_80900890(BossGanon2* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { func_808FFDB0(this, play); if (this->unk_334 == 0) { - this->actor.colChkInfo.health = 25; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(25, this->actor.level); } this->unk_336 = 1; } @@ -1923,10 +1923,10 @@ void func_80902348(BossGanon2* this, PlayState* play) { } void func_80902524(BossGanon2* this, PlayState* play) { - s8 temp_v0_4; + u16 temp_v0_4; ColliderInfo* acHitInfo; s16 i; - u8 phi_v1_2; + u16 phi_v1_2; osSyncPrintf("this->no_hit_time %d\n", this->unk_316); if (this->unk_316 != 0 || ((this->unk_334 == 0) && (this->actionFunc == func_80900890))) { @@ -1951,9 +1951,11 @@ void func_80902524(BossGanon2* this, PlayState* play) { this->unk_342 = 5; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + this->actor.colChkInfo.health -= damage; + ActorDamageNumber_New(&this->actor, damage); temp_v0_4 = this->actor.colChkInfo.health; - if (temp_v0_4 < 0x15 && this->unk_334 == 0) { + if (temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level) && this->unk_334 == 0) { func_80900818(this, play); } else { if (temp_v0_4 <= 0) { @@ -1985,11 +1987,20 @@ void func_80902524(BossGanon2* this, PlayState* play) { phi_v1_2 = 2; } } - this->actor.colChkInfo.health -= phi_v1_2; + u8 baseDamage = phi_v1_2; + + phi_v1_2 = + Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, phi_v1_2 * HEALTH_ATTACK_MULTIPLIER); + if (phi_v1_2 <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= phi_v1_2; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, phi_v1_2); temp_v0_4 = this->actor.colChkInfo.health; - if ((temp_v0_4 < 0x15) && (this->unk_334 == 0)) { + if ((temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level)) && (this->unk_334 == 0)) { func_80900818(this, play); - } else if ((temp_v0_4 <= 0) && (phi_v1_2 >= 2)) { + } else if ((temp_v0_4 <= 0) && (baseDamage >= 2)) { func_80901020(this, play); } else { if (temp_v0_4 <= 0) { diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c index aac63ed8c73..9e6fac12936 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -467,7 +467,8 @@ void BossGanondrof_Neutral(BossGanondrof* this, PlayState* play) { if (this->timers[0] == 0) { this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; rand01 = Rand_ZeroOne(); - if (thisx->colChkInfo.health < 5) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(5, this->actor.level); + if (thisx->colChkInfo.health < healthCheck) { if (rand01 < 0.25f) { BossGanondrof_SetupThrow(this, play); } else if (rand01 >= 0.8f) { @@ -638,8 +639,10 @@ void BossGanondrof_Throw(BossGanondrof* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { EnfHG* horseTemp = (EnfHG*)this->actor.child; - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, + Actor* fireTemp = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, this->work[GND_ACTION_STATE], 0, 0, FHGFIRE_ENERGY_BALL); + fireTemp->level = this->actor.level; + Actor_RefreshLeveledStats(fireTemp, GET_PLAYER(play)); this->actor.child = &horseTemp->actor; } @@ -1221,7 +1224,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { this->colliderBody.base.acFlags &= ~AC_HIT; } else { acHit = this->colliderBody.base.acFlags & AC_HIT; - if ((acHit && ((s8)this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { + if ((acHit && (this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { if (acHit) { this->colliderBody.base.acFlags &= ~AC_HIT; hurtbox = this->colliderBody.info.acHitInfo; @@ -1232,7 +1235,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { osSyncPrintf("hit != 0 \n"); } else if (this->actionFunc != BossGanondrof_Charge) { if (this->returnCount == 0) { - u8 dmg; + u16 dmg; u8 canKill = false; s32 dmgFlags = hurtbox->toucher.dmgFlags; @@ -1241,11 +1244,20 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } dmg = CollisionCheck_GetSwordDamage(dmgFlags, play); (dmg == 0) ? (dmg = 2) : (canKill = true); - if (((s8)this->actor.colChkInfo.health > 2) || canKill) { - this->actor.colChkInfo.health -= dmg; + dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, + dmg * HEALTH_ATTACK_MULTIPLIER); + ActorDamageNumber_New(&this->actor, dmg); + if ((this->actor.colChkInfo.health > 2) || canKill) { + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else if (this->actor.colChkInfo.health > 2) { + this->actor.colChkInfo.health = 1; + } else { + this->actor.colChkInfo.health = 0; + } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanondrof_SetupDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME; @@ -1265,7 +1277,13 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { this->work[GND_INVINC_TIMER] = 10; - this->actor.colChkInfo.health -= 2; + u16 dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + ActorDamageNumber_New(&this->actor, dmg); + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else { + this->actor.colChkInfo.health = 0; + } horse->hitTimer = 20; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); } diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c index 72623af224e..226aa942c84 100644 --- a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -1832,9 +1832,16 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM2); } else if (this->actionFunc == BossGoma_FloorStunned && (damage = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags, play)) != 0) { - this->actor.colChkInfo.health -= damage; + damage = + Leveled_DamageModify(&this->actor, this->collider.elements[0].info.acHit->actor, damage * HEALTH_ATTACK_MULTIPLIER); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); - if ((s8)this->actor.colChkInfo.health > 0) { + if (this->actor.colChkInfo.health > 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM1); BossGoma_SetupFloorDamaged(this); EffectSsSibuki_SpawnBurst(play, &this->actor.focus.pos); diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index 317eaf55d87..b7b29a1c4a8 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -1774,7 +1774,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { // "hit 2 !!" osSyncPrintf("Core_Damage_check 当り 2 !!\n"); if ((this->work[MO_TENT_ACTION_STATE] != MO_CORE_UNDERWATER) && (this->work[MO_TENT_INVINC_TIMER] == 0)) { - u8 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); + u16 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); if ((damage != 0) && (this->work[MO_TENT_ACTION_STATE] < MO_CORE_ATTACK)) { // "sword hit !!" @@ -1782,14 +1782,21 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { this->work[MO_TENT_ACTION_STATE] = MO_CORE_STUNNED; this->timers[0] = 25; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + ActorDamageNumber_New(&this->actor, damage); + this->actor.speedXZ = 15.0f; this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; this->work[MO_CORE_DMG_FLASH_TIMER] = 15; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_DAMAGE); - this->actor.colChkInfo.health -= damage; + if (damage < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } this->hitCount++; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index 022227f3292..781d345208b 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -1885,7 +1885,8 @@ void BossSst_HandCrush(BossSst* this, PlayState* play) { func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } if (Animation_OnFrame(&this->skelAnime, 0.0f)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 58b16feb831..33fcede33ac 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -28,7 +28,7 @@ void BossTw_Update(Actor* thisx, PlayState* play); void BossTw_Draw(Actor* thisx, PlayState* play); void BossTw_Reset(void); -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 arg2); +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 arg2); void BossTw_TwinrovaSetupFly(BossTw* this, PlayState* play); void BossTw_DrawEffects(PlayState* play); void BossTw_TwinrovaLaugh(BossTw* this, PlayState* play); @@ -3081,7 +3081,7 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { if (info->toucher.dmgFlags & (DMG_SLINGSHOT | DMG_ARROW)) {} } } else if (this->collider.base.acFlags & AC_HIT) { - u8 damage; + u16 damage; u8 swordDamage; ColliderInfo* info = this->collider.info.acHitInfo; @@ -3095,8 +3095,13 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { swordDamage = true; } + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + ActorDamageNumber_New(&this->actor, damage); + if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) { - if (((s8)this->actor.colChkInfo.health < 3) && !swordDamage) { + if ((this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(3, this->actor.level)) && + !swordDamage) { + damage = 0; } @@ -5169,7 +5174,7 @@ void BossTw_TwinrovaChargeBlast(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { - if ((s8)this->actor.colChkInfo.health < 10) { + if (this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(10, this->actor.level)) { sTwinrovaBlastType = Rand_ZeroFloat(1.99f); } else { if (++sFixedBlatSeq >= 4) { @@ -5270,7 +5275,7 @@ void BossTw_TwinrovaDoneBlastShoot(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); } -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 damage) { if (this->actionFunc != BossTw_TwinrovaStun) { Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0338F0, -15.0f); this->timers[0] = 150; @@ -5284,11 +5289,13 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_024374); this->csState1 = 1; - if ((s8)(this->actor.colChkInfo.health -= damage) < 0) { + if (damage >= this->actor.colChkInfo.health) { this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health == 0) { BossTw_TwinrovaSetupDeathCS(this, play); Enemy_StartFinishingBlow(play, &this->actor); Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index 005759565a5..49e50decb80 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -401,7 +401,7 @@ static s16 sDoorState; static u8 sPhase3StopMoving; static Vec3s sZapperRot; static u16 sPhase2Timer; -static s8 sPhase4HP; +static u16 sPhase4HP; void BossVa_SetupAction(BossVa* this, BossVaActionFunc func) { this->actionFunc = func; @@ -1351,7 +1351,7 @@ void BossVa_SetupBodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 300; sBodyState = 1; - sPhase4HP = 4; + sPhase4HP = GetActorStat_EnemyMaxHealth(4, this->actor.level); if (this->actor.shape.yOffset != 0.0f) { this->timer = -30; } @@ -1389,11 +1389,19 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_DAMAGE); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); - sPhase4HP -= this->actor.colChkInfo.damage; + u16 dmg = this->actor.colChkInfo.damage; + + if (dmg > sPhase4HP) { + sPhase4HP = 0; + } else { + sPhase4HP -= dmg; + } + + ActorDamageNumber_New(&this->actor, dmg); if (sPhase4HP <= 0) { this->timer = 0; sFightPhase++; - sPhase4HP += 3; + sPhase4HP += GetActorStat_EnemyMaxHealth(3, this->actor.level); if (sFightPhase >= PHASE_DEATH) { BossVa_SetupBodyDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c index fc69b615e69..832c06a4686 100644 --- a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c @@ -407,6 +407,7 @@ void func_809B75A0(EnBa* this, PlayState* play2) { Matrix_MultVec3f(&sp74, &this->unk_158[i + 1]); } this->unk_31A = 15; + Player_GainExperience(play, this->actor.exp); EnBa_SetupAction(this, EnBa_Die); } @@ -452,7 +453,14 @@ void EnBa_Update(Actor* thisx, PlayState* play) { if ((this->actor.params < EN_BA_DEAD_BLOB) && (this->collider.base.acFlags & 2)) { this->collider.base.acFlags &= ~2; - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, HEALTH_ATTACK_MULTIPLIER); + ActorDamageNumber_New(&this->actor, damage); + + if (damage > this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; + } if (this->actor.colChkInfo.health == 0) { func_809B75A0(this, play); gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE]++; diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c index ef0127e8582..3aabf0e666c 100644 --- a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c @@ -540,7 +540,9 @@ void EnBb_Damage(EnBb* this, PlayState* play) { Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); if (this->actor.speedXZ == 0.0f) { this->actor.shape.yOffset = 200.0f; - EnBb_SetupDown(this); + if (this->actor.params > ENBB_GREEN) { + EnBb_SetupDown(this); + } } } @@ -1194,7 +1196,7 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { if ((this->action != BB_DOWN) || (this->timer < 190)) { Actor_ApplyDamage(&this->actor); } - if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE)) { + if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE) && (this->actor.params > ENBB_GREEN)) { EnBb_SetupDown(this); } } else { @@ -1211,13 +1213,15 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { EnBb_KillFlameTrail(this); } EnBb_SetupDeath(this, play); + Player_GainExperience(play, this->actor.exp); //! @bug //! Because Din's Fire kills the bubble in a single hit, Actor_SetColorFilter is never called and //! colorFilterParams is never set. And because Din's Fire halts updating during its cutscene, //! EnBb_Death doesn't kill the bubble on the next frame like it should. This combines with //! the bug in EnBb_Draw below to crash the game. } else if ((this->actor.params == ENBB_WHITE) && - ((this->action == BB_WHITE) || (this->action == BB_STUNNED))) { + ((this->action == BB_WHITE) || (this->action == BB_STUNNED)) || + (this->actor.params <= ENBB_GREEN)) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); this->actor.speedXZ = -8.0f; this->maxSpeed = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c index a3d6c923d23..ef41da56d56 100644 --- a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c +++ b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c @@ -181,7 +181,8 @@ void func_809BC598(EnBdfire* this, PlayState* play) { player->flameTimers[i] = Rand_S16Offset(0, 200); } player->isBurning = true; - func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 8); + // Buffed damage from 8 to 16. + func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 16); osSyncPrintf("POWER\n"); } } diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c index f92985a0214..00178039767 100644 --- a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c @@ -174,6 +174,7 @@ void EnBigokuta_Init(Actor* thisx, PlayState* play) { } CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, sColChkInfoInit); + Actor_GetLevelAndExperience(play, &this->actor, ACTOR_EN_BIGOKUTA); this->unk_194 = 1; diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c index 5d3cfd3bf44..e98f3dc681b 100644 --- a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c @@ -562,6 +562,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; } + if (this->actor.colChkInfo.health > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_DAMAGE); + } + damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == BIRI_DMGEFF_DEKUNUT) { @@ -587,7 +591,11 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } else if (damageEffect == BIRI_DMGEFF_SLINGSHOT) { EnBili_SetupRecoil(this); } else { - EnBili_SetupBurnt(this); + // EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F820) { // DMG_ARROW diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c index b92b22d3f54..51958eadff0 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c @@ -230,7 +230,7 @@ void EnBomBowlPit_Reset(EnBomBowlPit* this, PlayState* play) { // "Normal termination"/"completion" osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); if (this->getItemId == GI_HEART_PIECE) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; // "Ah recovery!" (?) osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ あぁ回復! ☆☆☆☆☆ \n" VT_RST); } diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c index cd3c522e38f..c08b49f3bef 100644 --- a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c @@ -115,7 +115,8 @@ u32 func_809CBCEC(EnBubble* this) { } void EnBubble_DamagePlayer(EnBubble* this, PlayState* play) { - s32 damage = -this->colliderSphere.elements[0].info.toucher.damage; + s32 damage = -Leveled_DamageModify(&GET_PLAYER(play)->actor, &this->actor, + this->colliderSphere.elements[0].info.toucher.damage); play->damagePlayer(play, damage); func_8002F7A0(play, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c index a480fd51a4f..90d5b1c3198 100644 --- a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c @@ -673,6 +673,7 @@ void func_809D0424(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } @@ -716,6 +717,7 @@ void func_809D0584(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } else if ((this->unk_220 != 1) && (this->unk_220 != 6)) { diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c index 5d4ee502094..add23a7162a 100644 --- a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -1053,6 +1053,7 @@ void EnDekubaba_UpdateDamage(EnDekubaba* this, PlayState* play) { ((this->actor.colChkInfo.damageEffect != DEKUBABA_DMGEFF_NONE) || (this->actor.colChkInfo.damage != 0))) { phi_s0 = this->actor.colChkInfo.health - this->actor.colChkInfo.damage; + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage); if (this->actionFunc != EnDekubaba_StunnedVertical) { if ((this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) || diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c index 48a66619518..90f174198b3 100644 --- a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c @@ -405,7 +405,11 @@ void EnDekunuts_Gasp(EnDekunuts* this, PlayState* play) { void EnDekunuts_BeDamaged(EnDekunuts* this, PlayState* play) { Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); if (SkelAnime_Update(&this->skelAnime)) { - EnDekunuts_SetupDie(this); + if (this->actor.colChkInfo.health == 0) { + EnDekunuts_SetupDie(this); + } else { + EnDekunuts_SetupRun(this); + } } } diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c index 3daf7b11b73..76259c5a7c2 100644 --- a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -488,11 +488,13 @@ void EnDh_CollisionCheck(EnDh* this, PlayState* play) { lastHealth = this->actor.colChkInfo.health; if (Actor_ApplyDamage(&this->actor) == 0) { EnDh_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { - if (((lastHealth >= 15) && (this->actor.colChkInfo.health < 15)) || - ((lastHealth >= 9) && (this->actor.colChkInfo.health < 9)) || - ((lastHealth >= 3) && (this->actor.colChkInfo.health < 3))) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(3, this->actor.level); + if (((lastHealth >= healthCheck * 5) && (this->actor.colChkInfo.health < healthCheck * 5)) || + ((lastHealth >= healthCheck * 3) && (this->actor.colChkInfo.health < healthCheck * 3)) || + ((lastHealth >= healthCheck) && (this->actor.colChkInfo.health < healthCheck))) { this->retreat++; } diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c index faefacd48af..bb7c085951d 100644 --- a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -664,6 +664,7 @@ void EnDodongo_SetupDeath(EnDodongo* this, PlayState* play) { Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoDieAnim, -8.0f); this->timer = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_DEAD); + Player_GainExperience(play, this->actor.exp); this->actionState = DODONGO_DEATH; this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; this->actor.speedXZ = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 1073dd48441..c24c35612f4 100644 --- a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -635,16 +635,19 @@ void func_80A0329C(EnElf* this, PlayState* play) { { if (CVarGetInteger("gFairyPercentRestore", 0)) { - Health_ChangeBy(play, (gSaveContext.healthCapacity * CVarGetInteger("gFairyHealth", 100) / 100 + 15) / 16 * 16); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + Health_ChangeBy(play, heartUnits + ((gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gFairyHealth", 100) / 100)); } else { - Health_ChangeBy(play, CVarGetInteger("gFairyHealth", 8) * 16); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + Health_ChangeBy(play, CVarGetInteger("gFairyHealth", 8) * heartUnits); } } else { - Health_ChangeBy(play, 128); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + Health_ChangeBy(play, 8 * heartUnits); } if (this->fairyFlags & FAIRY_FLAG_BIG) { Magic_Fill(play); diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c index b3a16b5c617..7aa2947a0a0 100644 --- a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c @@ -220,10 +220,11 @@ s32 EnFd_SpawnCore(EnFd* this, PlayState* play) { return false; } - this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % 8; + u16 healthSpawn = GetActorStat_EnemyMaxHealth(8, this->actor.level); + this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % healthSpawn; if (this->actor.child->colChkInfo.health == 0) { - this->actor.child->colChkInfo.health = 8; + this->actor.child->colChkInfo.health = healthSpawn; } if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) { @@ -778,7 +779,7 @@ void EnFd_Draw(Actor* thisx, PlayState* play) { Matrix_Pop(); if (this->actionFunc != EnFd_Reappear && !(this->fadeAlpha < 0.9f)) { Gfx_SetupDL_25Xlu(play->state.gfxCtx); - clampedHealth = CLAMP(thisx->colChkInfo.health - 1, 0, 23); + clampedHealth = CLAMP((u16)((f32)thisx->colChkInfo.health / GetActorStat_EnemyMaxHealth(24, this->actor.level) * 23), 0, 23); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, primColors[clampedHealth / 8].r, primColors[clampedHealth / 8].g, primColors[clampedHealth / 8].b, (u8)this->fadeAlpha); gDPSetEnvColor(POLY_XLU_DISP++, envColors[clampedHealth / 8].r, envColors[clampedHealth / 8].g, diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c index 6da2e9adc72..ed072268971 100644 --- a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c @@ -634,20 +634,18 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { this->collider.base.acFlags &= ~AC_HIT; Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 1); - if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { - if (Actor_ApplyDamage(&this->actor) == 0) { - Enemy_StartFinishingBlow(play, &this->actor); - this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; - } + if ((this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) && + ((this->actor.colorFilterTimer == 0) || ((this->actor.colorFilterParams & 0x4000) == 0))) { damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == 2) { // Din's Fire if (this->actor.params == KEESE_ICE_FLY) { - this->actor.colChkInfo.health = 0; - Enemy_StartFinishingBlow(play, &this->actor); - EnFirefly_Combust(this, play); - EnFirefly_SetupFall(this); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + EnFirefly_Combust(this, play); + EnFirefly_SetupFall(this); + } } else if (!this->onFire) { EnFirefly_Ignite(this); if (this->actionFunc == EnFirefly_Perch) { @@ -658,17 +656,38 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { if (this->actor.params == KEESE_ICE_FLY) { EnFirefly_SetupFall(this); } else { - EnFirefly_SetupFrozenFall(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + EnFirefly_SetupFrozenFall(this, play); + } } } else if (damageEffect == 1) { // Deku Nuts if (this->actionFunc != EnFirefly_Stunned) { EnFirefly_SetupStunned(this); } - } else { // Fire Arrows - if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { + } else if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { // Fire Arrows + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); EnFirefly_Combust(this, play); + } else { + EnFirefly_SetupFall(this); + } + } else { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + EnFirefly_SetupFall(this); + } + } + + if (this->actor.colChkInfo.health > 0 && damageEffect != 1) { + if (this->actionFunc == EnFirefly_Perch) { + EnFirefly_SetupFlyIdle(this); } - EnFirefly_SetupFall(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_DEAD); + this->actor.flags |= ACTOR_FLAG_UPDATE_WHILE_CULLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); } } } diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c index e6b036ba7c0..40b40ca4a97 100644 --- a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -737,6 +737,11 @@ void EnFloormas_SmDecideAction(EnFloormas* this, PlayState* play) { void EnFloormas_SmShrink(EnFloormas* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0015f)) { + EnFloormas* parent = (EnFloormas*)this->actor.parent; + EnFloormas* child = (EnFloormas*)this->actor.child; + if ((parent->actionFunc == EnFloormas_SmWait) && (child->actionFunc == EnFloormas_SmWait)) { + Player_GainExperience(play, this->actor.exp); + } EnFloormas_SetupSmWait(this); } this->actor.scale.z = this->actor.scale.x; @@ -821,7 +826,8 @@ void EnFloormas_GrabLink(EnFloormas* this, PlayState* play) { } else { func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } } diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index 3960d854473..7ee647a7624 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -151,11 +151,11 @@ s32 EnFw_CheckCollider(EnFw* this, PlayState* play) { } this->collider.base.acFlags &= ~AC_HIT; if (Actor_ApplyDamage(&this->actor) <= 0) { - if (this->actor.parent->colChkInfo.health <= 8) { + if (this->actor.parent->colChkInfo.health <= GetActorStat_EnemyMaxHealth(8, this->actor.level)) { Enemy_StartFinishingBlow(play, &this->actor); this->actor.parent->colChkInfo.health = 0; } else { - this->actor.parent->colChkInfo.health -= 8; + this->actor.parent->colChkInfo.health -= GetActorStat_EnemyMaxHealth(8, this->actor.level); } this->returnToParentTimer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c index b9639605e04..8c6a41c26ba 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -363,6 +363,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { vec.z = this->actor.world.pos.z; EnFz_Damaged(this, play, &vec, 30, 10.0f); EnFz_SetupDespawn(this, play); + Player_GainExperience(play, this->actor.exp); gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FREEZARD]++; } } @@ -371,6 +372,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8); if (this->actor.colChkInfo.health == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD); + Player_GainExperience(play, this->actor.exp); EnFz_SetupMelt(this); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE); @@ -717,7 +719,7 @@ void EnFz_Draw(Actor* thisx, PlayState* play) { s32 pad; s32 index; - index = (6 - this->actor.colChkInfo.health) >> 1; + index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / GetActorStat_EnemyMaxHealth(6, this->actor.level) * 5 + 0.99999f), 0, 6)) >> 1; OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 96d0c5ec3c2..66d88f0dba4 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -638,7 +638,7 @@ s32 EnGirlA_CanBuy_ZoraTunic(PlayState* play, EnGirlA* this) { } s32 EnGirlA_CanBuy_Health(PlayState* play, EnGirlA* this) { - if (gSaveContext.healthCapacity == gSaveContext.health) { + if (gSaveContext.healthCapacity2 == gSaveContext.health) { return CANBUY_RESULT_CANT_GET_NOW; } if (gSaveContext.rupees < this->basePrice) { diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c index af5b6a38208..1c55ea776a7 100644 --- a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c @@ -615,7 +615,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->hurtTimer--; } else { ColliderInfo* acHitInfo; - u8 swordDamage; + u16 swordDamage; if ((this->colCyl1.base.atFlags & 2) && this->actionFunc == EnGoma_Jump) { EnGoma_SetupLand(this); @@ -654,7 +654,9 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { swordDamage = 1; } + swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * HEALTH_ATTACK_MULTIPLIER); this->actor.colChkInfo.health -= swordDamage; + ActorDamageNumber_New(&this->actor, swordDamage); EnGoma_SetupHurt(this, play); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); this->hurtTimer = 13; diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index 2801f78d077..3f7b41ba402 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -677,7 +677,7 @@ void func_80A75C38(EnIk* this, PlayState* play) { f32 temp_f0; u8 pad; u8 pad2; - u8 prevHealth; + u16 prevHealth; s32 temp_v0_3; Vec3f sp38; @@ -712,19 +712,20 @@ void func_80A75C38(EnIk* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); prevHealth = this->actor.colChkInfo.health; Actor_ApplyDamage(&this->actor); + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); if (this->actor.params != 0) { - if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + if ((prevHealth > healthCheck) && (this->actor.colChkInfo.health <= healthCheck)) { this->unk_2FB = 1; BodyBreak_Alloc(&this->bodyBreak, 3, play); } - } else if (this->actor.colChkInfo.health <= 10) { + } else if (this->actor.colChkInfo.health <= healthCheck) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_BOSS); SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EN_LAST_DAMAGE); if (this->switchFlags != 0xFF) { Flags_SetSwitch(play, this->switchFlags); } return; - } else if (prevHealth == 50) { + } else if (prevHealth == GetActorStat_EnemyMaxHealth(50, this->actor.level)) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_ENEMY); } @@ -740,7 +741,7 @@ void func_80A75C38(EnIk* this, PlayState* play) { } } if ((this->actor.params != 0) && (this->unk_2FB != 0)) { - if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + if ((prevHealth > healthCheck) && (this->actor.colChkInfo.health <= healthCheck)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_ARMOR_OFF_DEMO); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_DAMAGE); @@ -762,7 +763,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { this->unk_2FA = this->unk_2FB; func_80A75C38(this, play); - if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); + if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= healthCheck)) { func_80A781CC(&this->actor, play); return; } @@ -1448,7 +1450,8 @@ void func_80A781CC(Actor* thisx, PlayState* play) { Actor_SetScale(&this->actor, 0.01f); } else { // Because no CS in rando, we hide the death of the knuckle by spawning flames and kill the actor - if ((this->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); + if ((this->actor.colChkInfo.health <= healthCheck)) { s32 i; Vec3f pos; Vec3f sp7C = { 0.0f, 0.5f, 0.0f }; diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index 644614d4b78..83f7c4017d3 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -622,6 +622,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { if (this->actor.colorFilterTimer == 0) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { + Player_GainExperience(play, this->actor.exp); EnMb_SetupClubDead(this); } else if (this->state == ENMB_STATE_CLUB_KNEELING) { /* dead code: the setup for this action sets state to something else */ @@ -631,6 +632,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { } } else { if (this->actor.colChkInfo.health == 0) { + Player_GainExperience(play, this->actor.exp); EnMb_SetupSpearDead(this); } else { EnMb_SetupSpearDamaged(this); diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c index 16f447da052..d0e17ee7f5c 100644 --- a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -871,6 +871,7 @@ void EnPeehat_StateExplode(EnPeehat* this, PlayState* play) { if (this->animTimer == 5) { bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0x602, 0, true); + Player_GainExperience(play, this->actor.exp); if (bomb != NULL) { bomb->timer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index 4ae2f95a7fe..38493d00455 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -490,7 +490,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { case 1: Animation_PlayLoop(&this->skelAnime, &gGibdoRedeadGrabAttackAnim); this->unk_304++; - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xFF, 1, 0xC); this->unk_319 = 20; case 0: @@ -524,7 +525,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { this->unk_319--; if (this->unk_319 == 0) { - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xF0, 1, 0xC); this->unk_319 = 20; func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); @@ -721,6 +723,7 @@ void func_80AE3ECC(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); @@ -792,6 +795,7 @@ void func_80AE4114(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, 0, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c index 19baa7332f1..3e6c71062ea 100644 --- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -332,6 +332,7 @@ void EnRr_SetupReleasePlayer(EnRr* this, PlayState* play) { EnRr_SetupDamage(this); } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } @@ -459,6 +460,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { EnRr_SetupDamage(this); } else { this->dropType = dropType; + Player_GainExperience(play, this->actor.exp); EnRr_SetupDeath(this); } return; @@ -752,6 +754,7 @@ void EnRr_Stunned(EnRr* this, PlayState* play) { } else if (this->actor.colChkInfo.health != 0) { this->actionFunc = EnRr_Approach; } else { + Player_GainExperience(play, this->actor.exp); EnRr_SetupDeath(this); } } diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index dcd80ff6c6f..044064f4d4d 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -180,6 +180,8 @@ void func_80AFB950(EnSi* this, PlayState* play) { player->actor.freezeTimer = 10; } else { SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF); + u16 experience = Leveled_GoldSkulltulaExperience(gSaveContext.inventory.gsTokens); + Player_GainExperience(play, experience); Actor_Kill(&this->actor); } } diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c index 7ae647defbb..9a5767a5e98 100644 --- a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -416,6 +416,7 @@ void func_80AFD7B4(EnSkb* this, PlayState* play) { this->unk_283 |= 4; EffectSsDeadSound_SpawnStationary(play, &this->actor.projectedPos, NA_SE_EN_STALKID_DEAD, 1, 1, 0x28); EnSkb_SetupAction(this, func_80AFD880); + Player_GainExperience(play, this->actor.exp); gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALCHILD]++; } diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c index 53bc29b1df3..f247a58792e 100644 --- a/soh/src/overlays/actors/ovl_En_St/z_en_st.c +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -397,7 +397,8 @@ s32 EnSt_CheckHitLink(EnSt* this, PlayState* play) { } this->gaveDamageSpinTimer = 30; - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); return true; diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c index 6b30686433f..a25b6135e0d 100644 --- a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -315,6 +315,8 @@ void EnTest_Destroy(Actor* thisx, PlayState* play) { if ((this->actor.params != STALFOS_TYPE_2) && !Actor_FindNearby(play, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f)) { func_800F5B58(); + if (this->actor.ignoreExpReward) + Player_GainExperience(play, this->actor.exp); } Effect_Delete(play, this->effectIndex); @@ -1508,7 +1510,7 @@ void func_80862E6C(EnTest* this, PlayState* play) { } } else { if (this->actor.home.rot.x == 0) { - this->actor.colChkInfo.health = 10; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(10, this->actor.level); if (this->actor.params == STALFOS_TYPE_4) { this->actor.params = -1; diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c index 5b2bc73ddd7..3e855a6fea8 100644 --- a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c @@ -723,6 +723,7 @@ void EnTite_Stunned(EnTite* this, PlayState* play) { ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) { this->actor.world.rot.y = this->actor.shape.rot.y; if (this->actor.colChkInfo.health == 0) { + Player_GainExperience(play, this->actor.exp); EnTite_SetupDeathCry(this); } else if (this->flipState == TEKTITE_FLIPPED) { EnTite_SetupFlipUpright(this); @@ -865,6 +866,7 @@ void EnTite_CheckDamage(Actor* thisx, PlayState* play) { Actor_ApplyDamage(thisx); } if (thisx->colChkInfo.health == 0) { + Player_GainExperience(play, this->actor.exp); EnTite_SetupDeathCry(this); } else { // Flip tektite back up if it's on its back diff --git a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c index 6a1e967730f..7355f515374 100644 --- a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c +++ b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c @@ -601,6 +601,7 @@ void EnTp_UpdateDamage(EnTp* this, PlayState* play) { if (head->actor.params <= TAILPASARAN_HEAD) { EnTp_SetupDie(head); + Player_GainExperience(play, this->actor.exp); head->damageEffect = this->actor.colChkInfo.damageEffect; head->actor.params = TAILPASARAN_HEAD_DYING; } diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c index 3b95e4d25e0..5b5934a0853 100644 --- a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c @@ -386,6 +386,7 @@ void EnVm_Die(EnVm* this, PlayState* play) { bomb->timer = 0; } + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xA0); Actor_Kill(&this->actor); } @@ -395,7 +396,10 @@ void EnVm_CheckHealth(EnVm* this, PlayState* play) { EnBom* bomb; if (Actor_GetCollidedExplosive(play, &this->colliderCylinder.base) != NULL) { - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, HEALTH_ATTACK_MULTIPLIER); + this->actor.colChkInfo.damage += damage; + ActorDamageNumber_New(&this->actor, damage); + Actor_ApplyDamage(&this->actor); osSyncPrintf("hp down %d\n", this->actor.colChkInfo.health); } else { if (!(this->colliderQuad2.base.acFlags & AC_HIT) || this->unk_21C == 2) { diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c index 8a5ab56bec7..06f6a224f81 100644 --- a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -1947,6 +1947,7 @@ void EnZf_Die(EnZf* this, PlayState* play) { if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == -1)) { Flags_SetSwitch(play, this->clearFlag); func_800F5B58(); + Player_GainExperience(play, this->actor.exp); } else { D_80B4A1B4 = -1; } diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c index a58b09a4dc0..b6d9156ae43 100644 --- a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c @@ -2535,7 +2535,7 @@ void func_80B59828(EnZl3* this, PlayState* play) { if (func_80B59698(this, play) != 0) { func_80088AA0(180); func_80B53468(); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); if (Flags_GetSwitch(play, 0x20)) { Flags_UnsetSwitch(play, 0x20); @@ -2582,7 +2582,7 @@ void func_80B59AD0(EnZl3* this, PlayState* play) { func_80B53614(this, play); Flags_UnsetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO); func_80B56F10(this, play); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->action = 27; this->drawConfig = 1; diff --git a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c index 9212362d660..6ac11e9c85b 100644 --- a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c +++ b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c @@ -618,7 +618,8 @@ void EnfHG_Damage(EnfHG* this, PlayState* play) { this->timers[0] = 140; this->actionFunc = EnfHG_Retreat; Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); - if (bossGnd->actor.colChkInfo.health > 24) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(24, bossGnd->actor.level); + if (bossGnd->actor.colChkInfo.health > healthCheck) { this->bossGndSignal = FHG_RIDE; } else { bossGnd->flyMode = GND_FLY_NEUTRAL; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 86a6854cf65..5d9ffb4e62f 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -8400,13 +8400,14 @@ void func_80843AE8(PlayState* play, Player* this) { } if (CVarGetInteger("gFairyReviveEffect", 0)) { if (CVarGetInteger("gFairyRevivePercentRestore", 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger("gFairyReviveHealth", 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits + ((gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gFairyReviveHealth", 100) / 100); } else { - gSaveContext.healthAccumulator = CVarGetInteger("gFairyReviveHealth", 20) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger("gFairyReviveHealth", 20) * heartUnits; } } else { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } this->unk_850 = -1; } @@ -9616,6 +9617,7 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe this->meleeWeaponEffectIndex = TOTAL_EFFECT_COUNT; this->currentYaw = this->actor.world.rot.y; func_80834644(play, this); + Player_GainExperience(play, 0); SkelAnime_InitLink(play, &this->skelAnime, skelHeader, D_80853914[PLAYER_ANIMGROUP_0][this->modelAnimType], 9, this->jointTable, this->morphTable, PLAYER_LIMB_MAX); @@ -13045,37 +13047,40 @@ void func_8084EAC0(Player* this, PlayState* play) { if (this->unk_850 == 0) { if (this->itemAction == PLAYER_IA_BOTTLE_POE) { s32 rand = Rand_S16Offset(-1, 3); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; if (rand == 0) { rand = 3; } - if ((rand < 0) && (gSaveContext.health <= 0x10)) { + if ((rand < 0) && (gSaveContext.health <= heartUnits)) { rand = 3; } if (rand < 0) { - Health_ChangeBy(play, -0x10); + Health_ChangeBy(play, -heartUnits); } else { - gSaveContext.healthAccumulator = rand * 0x10; + gSaveContext.healthAccumulator = rand * heartUnits; } } else { s32 sp28 = D_808549FC[this->itemAction - PLAYER_IA_BOTTLE_POTION_RED]; if (CVarGetInteger("gRedPotionEffect", 0) && this->itemAction == PLAYER_IA_BOTTLE_POTION_RED) { if (CVarGetInteger("gRedPercentRestore", 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger("gRedPotionHealth", 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gRedPotionHealth", 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger("gRedPotionHealth", 20) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger("gRedPotionHealth", 20) * heartUnits; } } else if (CVarGetInteger("gBluePotionEffects", 0) && this->itemAction == PLAYER_IA_BOTTLE_POTION_BLUE) { if (CVarGetInteger("gBlueHealthPercentRestore", 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger("gBluePotionHealth", 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gBluePotionHealth", 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger("gBluePotionHealth", 20) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger("gBluePotionHealth", 20) * heartUnits; } if (CVarGetInteger("gBlueManaPercentRestore", 0)) { @@ -13083,10 +13088,8 @@ void func_8084EAC0(Player* this, PlayState* play) { Magic_Fill(play); } - func_80087708(play, - (gSaveContext.magicLevel * 48 * CVarGetInteger("gBluePotionMana", 100) / 100 + 15) / - 16 * 16, - 5); + func_80087708(play, (gSaveContext.magicLevel * gSaveContext.magicUnits * CVarGetInteger("gBluePotionMana", 100) / 100 + 15) / 16 * 16, 5); + } else { if (gSaveContext.magicState != 10) { Magic_Fill(play); @@ -13103,7 +13106,7 @@ void func_8084EAC0(Player* this, PlayState* play) { } func_80087708(play, - (gSaveContext.magicLevel * 48 * CVarGetInteger("gGreenPotionMana", 100) / 100 + 15) / + (gSaveContext.magicLevel * gSaveContext.magicUnits * CVarGetInteger("gGreenPotionMana", 100) / 100 + 15) / 16 * 16, 5); } else { @@ -13117,24 +13120,25 @@ void func_8084EAC0(Player* this, PlayState* play) { } else if (CVarGetInteger("gMilkEffect", 0) && (this->itemAction == PLAYER_IA_BOTTLE_MILK || this->itemAction == PLAYER_IA_BOTTLE_MILK_HALF)) { if (CVarGetInteger("gMilkPercentRestore", 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger("gMilkHealth", 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gMilkHealth", 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger("gMilkHealth", 5) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger("gMilkHealth", 5) * heartUnits; } if (CVarGetInteger("gSeparateHalfMilkEffect", 0) && this->itemAction == PLAYER_IA_BOTTLE_MILK_HALF) { if (CVarGetInteger("gHalfMilkPercentRestore", 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger("gHalfMilkHealth", 100) / 100 + 15) / 16 * - 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gHalfMilkHealth", 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger("gHalfMilkHealth", 5) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger("gHalfMilkHealth", 5) * heartUnits; } } } else { if (sp28 & 1) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } if (sp28 & 2) { @@ -13142,7 +13146,8 @@ void func_8084EAC0(Player* this, PlayState* play) { } if (sp28 & 4) { - gSaveContext.healthAccumulator = 0x50; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits * 5; } } } @@ -13258,13 +13263,15 @@ void func_8084EED8(Player* this, PlayState* play) { } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { if (CVarGetInteger("gFairyEffect", 0)) { if (CVarGetInteger("gFairyPercentRestore", 0)) { + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger("gFairyHealth", 100) / 100 + 15) / 16 * 16; + (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger("gFairyHealth", 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger("gFairyHealth", 8) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger("gFairyHealth", 8) * heartUnits; } } else { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } } } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c index 5b55f414493..f988d8abb8e 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -373,12 +373,14 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { if (gSaveContext.healthCapacity < 0x30) { gSaveContext.healthCapacity = 0x30; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { gSaveContext.healthCapacity += 0x10; if (gSaveContext.healthCapacity >= 0x140) { gSaveContext.healthCapacity = 0x140; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } break; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index be1320d7857..ffcc64d0f1f 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -577,6 +577,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { } Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); pauseCtx->unk_1E4 = 7; sEquipTimer = 10; } else if (CVarGetInteger("gAssignableTunicsAndBoots", 0) != 0) { @@ -746,5 +747,9 @@ void KaleidoScope_DrawEquipment(PlayState* play) { if (gUpgradeMasks[0]) {} + if (pauseCtx->pageIndex == PAUSE_EQUIP && (pauseCtx->unk_1E4 == 0 || sEquipTimer > 0) && pauseCtx->alpha == 255) { + Leveled_KaleidoEquip_Stats(play); + } + CLOSE_DISPS(play->state.gfxCtx); } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 4b8a80f9eee..85ccf4dc70c 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -4166,7 +4166,7 @@ void KaleidoScope_Update(PlayState* play) Grotto_ForceGrottoReturn(); } gSaveContext.nextTransitionType = 2; - gSaveContext.health = CVarGetInteger("gFullHealthSpawn", 0) ? gSaveContext.healthCapacity : 0x30; + gSaveContext.health = CVarGetInteger("gFullHealthSpawn", 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = 0; From 62ee583c13dfb4d673cc5cc4fc3ad28ecaac0b4d Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 8 Jul 2023 14:40:00 -0600 Subject: [PATCH 02/12] Fixed EXP resetting to 0 when loading save --- soh/soh/SaveManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 07d282eb349..9bf6ef62966 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1494,6 +1494,7 @@ void SaveManager::LoadBaseVersion3() { } void SaveManager::LoadBaseVersion4() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); From 5840902e747011dc52fe9fe4d60152d28f747f87 Mon Sep 17 00:00:00 2001 From: Cobalt Date: Sun, 30 Jul 2023 16:41:33 -0700 Subject: [PATCH 03/12] Fix Leveled Menu Bar Element Alignment --- soh/soh/SohMenuBar.cpp | 106 ++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index ea6d31d4561..cee86bcec82 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -1447,6 +1447,59 @@ void DrawRandomizerMenu() { } } +void DrawLeveledMenu() { + if (ImGui::BeginMenu("Leveled")) { + if (ImGui::BeginMenu("Floating Numbers")) { + EnhancementCheckbox("Enemy Damage", "gLeveledFloatingNumberEnemyDamage", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + ImGui::EndMenu(); + } + + EnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Enemy Level Affects Base Attack", "gLeveledEnemyAttackScalesWithLevel", + false, "", UIWidgets::CheckboxGraphics::Checkmark, true); + UIWidgets::Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " + "Power(Strength) stat. \nThis will increase difficulty a bit."); + EnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + EnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + + if (ImGui::BeginMenu("Heart Capacity in Units")) { + CVarGetInteger("gLeveledHeartUnits", 4); + UIWidgets::EnhancementRadioButton("4", "gLeveledHeartUnits", 1); + UIWidgets::EnhancementRadioButton("8", "gLeveledHeartUnits", 2); + UIWidgets::EnhancementRadioButton("12", "gLeveledHeartUnits", 3); + UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveledHeartUnits", 4); + UIWidgets::EnhancementRadioButton("20", "gLeveledHeartUnits", 5); + UIWidgets::EnhancementRadioButton("24", "gLeveledHeartUnits", 6); + UIWidgets::EnhancementRadioButton("28", "gLeveledHeartUnits", 7); + UIWidgets::EnhancementRadioButton("32", "gLeveledHeartUnits", 8); + UIWidgets::EnhancementRadioButton("36", "gLeveledHeartUnits", 9); + UIWidgets::EnhancementRadioButton("40", "gLeveledHeartUnits", 10); + UIWidgets::EnhancementRadioButton("44", "gLeveledHeartUnits", 11); + UIWidgets::EnhancementRadioButton("48", "gLeveledHeartUnits", 12); + ImGui::EndMenu(); + } + UIWidgets::Tooltip("Sets how much health units a heart is worth."); + + // if (ImGui::Button("Add 2000 EXP")){ + // gSaveContext.experience += 2000; + // } + + ImGui::EndMenu(); + } +} + void SohMenuBar::DrawElement() { if (ImGui::BeginMenuBar()) { DrawMenuBarIcon(); @@ -1477,58 +1530,11 @@ void SohMenuBar::DrawElement() { DrawRandomizerMenu(); - ImGui::PopStyleVar(1); - - if (ImGui::BeginMenu("Leveled")) { - if (ImGui::BeginMenu("Floating Numbers")) { - EnhancementCheckbox("Enemy Damage", "gLeveledFloatingNumberEnemyDamage", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - ImGui::EndMenu(); - } - - EnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Enemy Level Affects Base Attack", "gLeveledEnemyAttackScalesWithLevel", - false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - UIWidgets::Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " - "Power(Strength) stat. \nThis will increase difficulty a bit."); - EnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - - if (ImGui::BeginMenu("Heart Capacity in Units")) { - CVarGetInteger("gLeveledHeartUnits", 4); - UIWidgets::EnhancementRadioButton("4", "gLeveledHeartUnits", 1); - UIWidgets::EnhancementRadioButton("8", "gLeveledHeartUnits", 2); - UIWidgets::EnhancementRadioButton("12", "gLeveledHeartUnits", 3); - UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveledHeartUnits", 4); - UIWidgets::EnhancementRadioButton("20", "gLeveledHeartUnits", 5); - UIWidgets::EnhancementRadioButton("24", "gLeveledHeartUnits", 6); - UIWidgets::EnhancementRadioButton("28", "gLeveledHeartUnits", 7); - UIWidgets::EnhancementRadioButton("32", "gLeveledHeartUnits", 8); - UIWidgets::EnhancementRadioButton("36", "gLeveledHeartUnits", 9); - UIWidgets::EnhancementRadioButton("40", "gLeveledHeartUnits", 10); - UIWidgets::EnhancementRadioButton("44", "gLeveledHeartUnits", 11); - UIWidgets::EnhancementRadioButton("48", "gLeveledHeartUnits", 12); - ImGui::EndMenu(); - } - UIWidgets::Tooltip("Sets how much health units a heart is worth."); + ImGui::SetCursorPosY(0.0f); - // if (ImGui::Button("Add 2000 EXP")){ - // gSaveContext.experience += 2000; - // } + DrawLeveledMenu(); - ImGui::EndMenu(); - } + ImGui::PopStyleVar(1); ImGui::EndMenuBar(); } From fa530ee735e8a3ab154666f7272c40d631694aa7 Mon Sep 17 00:00:00 2001 From: Cobalt Date: Sun, 30 Jul 2023 19:38:16 -0700 Subject: [PATCH 04/12] Fix submenu padding --- soh/soh/SohMenuBar.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index cee86bcec82..ee772599a84 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -1452,26 +1452,26 @@ void DrawLeveledMenu() { if (ImGui::BeginMenu("Floating Numbers")) { EnhancementCheckbox("Enemy Damage", "gLeveledFloatingNumberEnemyDamage", false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", false, "", + PaddedEnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", false, "", + PaddedEnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); ImGui::EndMenu(); } - EnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", false, "", + PaddedEnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", false, "", + PaddedEnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Enemy Level Affects Base Attack", "gLeveledEnemyAttackScalesWithLevel", + PaddedEnhancementCheckbox("Enemy Level Affects Base Attack", "gLeveledEnemyAttackScalesWithLevel", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); UIWidgets::Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " "Power(Strength) stat. \nThis will increase difficulty a bit."); - EnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", false, "", + PaddedEnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", false, "", + PaddedEnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - EnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", false, "", + PaddedEnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", true, true, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); if (ImGui::BeginMenu("Heart Capacity in Units")) { @@ -1490,7 +1490,7 @@ void DrawLeveledMenu() { UIWidgets::EnhancementRadioButton("48", "gLeveledHeartUnits", 12); ImGui::EndMenu(); } - UIWidgets::Tooltip("Sets how much health units a heart is worth."); + UIWidgets::Tooltip("Sets how many health units a heart is worth."); // if (ImGui::Button("Add 2000 EXP")){ // gSaveContext.experience += 2000; From 3aee5dece03336054e6834e8dff4360d215f5ccd Mon Sep 17 00:00:00 2001 From: Cobalt Date: Sun, 30 Jul 2023 19:48:33 -0700 Subject: [PATCH 05/12] Clarify gLeveledHeartUnits description --- soh/soh/SohMenuBar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index ee772599a84..9663113e5f9 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -1474,7 +1474,7 @@ void DrawLeveledMenu() { PaddedEnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", true, true, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - if (ImGui::BeginMenu("Heart Capacity in Units")) { + if (ImGui::BeginMenu("Heart Container Value in Units")) { CVarGetInteger("gLeveledHeartUnits", 4); UIWidgets::EnhancementRadioButton("4", "gLeveledHeartUnits", 1); UIWidgets::EnhancementRadioButton("8", "gLeveledHeartUnits", 2); @@ -1490,7 +1490,7 @@ void DrawLeveledMenu() { UIWidgets::EnhancementRadioButton("48", "gLeveledHeartUnits", 12); ImGui::EndMenu(); } - UIWidgets::Tooltip("Sets how many health units a heart is worth."); + UIWidgets::Tooltip("Sets how many health units each completed heart container is worth.\nOne heart on the health meter is equal to 16 health units.\nA lower setting results in lower total health."); // if (ImGui::Button("Add 2000 EXP")){ // gSaveContext.experience += 2000; From fdeb401d350a6ac8cf22669899a80170e7e56cad Mon Sep 17 00:00:00 2001 From: Arrenton Date: Sat, 2 Sep 2023 17:16:15 -0600 Subject: [PATCH 06/12] Added showing player level to the save select screen --- soh/include/leveled_stat_math.h | 5 +- soh/soh/SaveManager.cpp | 6 ++ soh/soh/SaveManager.h | 1 + .../ovl_file_choose/z_file_choose.c | 63 +++++++++++++++++-- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h index 772856522e0..0f79ff49bd8 100644 --- a/soh/include/leveled_stat_math.h +++ b/soh/include/leveled_stat_math.h @@ -2,7 +2,6 @@ #define LEVELED_STAT_MATH_H #define HEALTH_ATTACK_MULTIPLIER 7 #include "z64.h" -#endif u16 GetActorStat_DisplayAttack(u16 attack, u8 power); u16 GetActorStat_Attack(u16 attack, u8 power); @@ -19,4 +18,6 @@ u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack); u16 Leveled_GoldSkulltulaExperience(u8 tokens); -void Leveled_SetPlayerModifiedStats(Player* player); \ No newline at end of file +void Leveled_SetPlayerModifiedStats(Player* player); + +#endif \ No newline at end of file diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 9bf6ef62966..7e17bac31b6 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -6,6 +6,7 @@ #include "functions.h" #include "macros.h" #include +#include "leveled_stat_math.h" #include "soh/Enhancements/boss-rush/BossRush.h" #include @@ -416,6 +417,11 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].questItems = gSaveContext.inventory.questItems; fileMetaInfo[fileNum].defense = gSaveContext.inventory.defenseHearts; fileMetaInfo[fileNum].health = gSaveContext.health; + fileMetaInfo[fileNum].level = 0; + + while (GetActorStat_NextLevelExp(fileMetaInfo[fileNum].level, gSaveContext.experience) <= 0 && fileMetaInfo[fileNum].level < 99) { + fileMetaInfo[fileNum].level += 1; + } for (int i = 0; i < ARRAY_COUNT(fileMetaInfo[fileNum].seedHash); i++) { fileMetaInfo[fileNum].seedHash[i] = gSaveContext.seedIcons[i]; diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 628b694f22f..87f49fd03ad 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -19,6 +19,7 @@ typedef struct { s16 buildVersionMajor; s16 buildVersionMinor; s16 buildVersionPatch; + u8 level; } SaveFileMetaInfo; #ifdef __cplusplus diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index d334b6dd22a..6348d16eaef 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -2,6 +2,7 @@ #include +#include "textures/nes_font_static/nes_font_static.h" #include "textures/title_static/title_static.h" #include "textures/parameter_static/parameter_static.h" #include @@ -1002,9 +1003,9 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { s16 phi_ra; s16 temp_t1; - this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x288 * sizeof(Vtx)); + this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x298 * sizeof(Vtx)); - for (phi_t2 = 0; phi_t2 < 0x288; phi_t2 += 4) { + for (phi_t2 = 0; phi_t2 < 0x298; phi_t2 += 4) { this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = 0x12C; this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = this->windowContentVtx[phi_t2].v.ob[0] + 0x10; @@ -1162,7 +1163,8 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] - WREG(43); } - phi_t0 = this->windowPosX - 14; + // Death count + phi_t0 = this->windowPosX - 26; temp_t1 -= 0x16; for (phi_a1 = 0; phi_a1 < 4; phi_a1++, phi_t2 += 4) { @@ -1172,7 +1174,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = this->windowContentVtx[phi_t2].v.ob[1] - D_80812828[phi_a1]; - phi_t0 += D_80812818[phi_a1]; + phi_t0 += D_80812818[phi_a1] - 1; } this->windowContentVtx[phi_t2 - 15].v.tc[0] = this->windowContentVtx[phi_t2 - 13].v.tc[0] = 0x400; @@ -1269,6 +1271,31 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2 + 6].v.ob[1] = this->windowContentVtx[phi_t2 + 7].v.ob[1] = this->windowContentVtx[phi_t2 + 4].v.ob[1] - 0x10; this->windowContentVtx[phi_t2 + 5].v.tc[0] = this->windowContentVtx[phi_t2 + 7].v.tc[0] = 0x1000; + + phi_t2 += 8; + // Level count + phi_t0 = this->windowPosX + 30; + temp_t1 = 24; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 += 3; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } + } static u16 D_8081284C[] = { 0x007C, 0x0124, 0x01CC }; @@ -1336,7 +1363,7 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0); gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 255, this->fileInfoAlpha[fileIndex]); - gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex]] + 0x24, 12, 0); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex]] + 36, 12, 0); FileChoose_SplitNumber(Save_GetSaveMetaInfo(fileIndex)->deaths, &deathCountSplit[0], &deathCountSplit[1], &deathCountSplit[2]); @@ -1347,6 +1374,32 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { vtxOffset); } + + // draw level + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[648], 32, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + + char lvText[] = { 21, 57 }; + + for (i = 0, vtxOffset = 0; i < 2; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + lvText[i] * FONT_CHAR_TEX_SIZE, vtxOffset); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[656], 12, 0); + + FileChoose_SplitNumber(Save_GetSaveMetaInfo(fileIndex)->level, &deathCountSplit[0], &deathCountSplit[1], + &deathCountSplit[2]); + + for (i = 1, vtxOffset = 0; i < 3; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + deathCountSplit[i] * FONT_CHAR_TEX_SIZE, + vtxOffset); + } + // end draw level + gDPPipeSync(POLY_OPA_DISP++); heartType = (Save_GetSaveMetaInfo(fileIndex)->defense == 0) ? 0 : 1; From e3b8021551d91c37551f31cd8406a1e1877f0593 Mon Sep 17 00:00:00 2001 From: Arrenton Date: Sat, 2 Sep 2023 17:54:58 -0600 Subject: [PATCH 07/12] Fixed build error. --- soh/include/leveled_stat_math.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h index 0f79ff49bd8..9bc8499ee89 100644 --- a/soh/include/leveled_stat_math.h +++ b/soh/include/leveled_stat_math.h @@ -3,6 +3,11 @@ #define HEALTH_ATTACK_MULTIPLIER 7 #include "z64.h" +#ifdef __cplusplus +extern "C" +{ +#endif + u16 GetActorStat_DisplayAttack(u16 attack, u8 power); u16 GetActorStat_Attack(u16 attack, u8 power); u8 GetActorStat_Power(u8 level); @@ -16,8 +21,12 @@ u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level); u16 GetPlayerStat_NextLevelExpAtLevel(u8 level); u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); -f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack); +f32 Leveled_DamageModify(Actor * actor, Actor * attackingActor, f32 attack); u16 Leveled_GoldSkulltulaExperience(u8 tokens); -void Leveled_SetPlayerModifiedStats(Player* player); +void Leveled_SetPlayerModifiedStats(Player * player); + +#ifdef __cplusplus +} +#endif #endif \ No newline at end of file From bacc009c0ae15edba2f2bd9b51dee934aa0dd839 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 7 Oct 2023 12:28:41 -0600 Subject: [PATCH 08/12] Fixed crash when dying at stalfos mini boss in forest temple, added option to disable NEXT display in the HUD when gaining EXP. --- soh/soh/SohMenuBar.cpp | 7 +++++++ soh/src/code/leveled_overlays.c | 11 ++++++++--- soh/src/code/z_player_lib.c | 4 ++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 9663113e5f9..431c053a5df 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -1449,6 +1449,13 @@ void DrawRandomizerMenu() { void DrawLeveledMenu() { if (ImGui::BeginMenu("Leveled")) { + if (ImGui::BeginMenu("HUD")) { + EnhancementCheckbox("EXP to NEXT Level", "gLeveledHUDExperienceNextLevel", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + UIWidgets::Tooltip("Show experience required to level up popup in the HUD when gaining EXP."); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Floating Numbers")) { EnhancementCheckbox("Enemy Damage", "gLeveledFloatingNumberEnemyDamage", false, "", UIWidgets::CheckboxGraphics::Checkmark, true); diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 8ea4ace8970..27c5afe5677 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -730,7 +730,7 @@ void Leveled_Interface_DrawNextLevel(PlayState* play) { } else { return; } - if (play->pauseCtx.state != 0) + if (play->pauseCtx.state != 0 || !CVarGetInteger("gLeveledHUDExperienceNextLevel", 1)) return; Player* player = GET_PLAYER(play); @@ -799,8 +799,13 @@ void Leveled_Interface_DrawNextLevel(PlayState* play) { for (u8 i = 0; i < digits; i++) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, 255 - play->pauseCtx.alpha); - for (j = 0; j < 4; j++) { - OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, posX - i * width + width * (digits - 1) + numbersPosX + (j % 2) * 2 - 1, posY + (j / 2) * 2 - 1, 8, 16, 1 << 10, 1 << 10); + for (j = 0; j < 3; j++) { + for (u8 k = 0; k < 3; k++) { + if (j == 1 && k == 1) + continue; + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, + posX - i * width + width * (digits - 1) + numbersPosX - 1 + k, posY - 1 + j, 8, 16, 1024, 1024); + } } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255 - play->pauseCtx.alpha); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 2887bff7dc7..302ed575e40 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -520,6 +520,10 @@ s32 Player_GetStrength(void) { void Player_GainExperience(PlayState* play, u16 experience) { Player* player = GET_PLAYER(play); + + if (player == NULL) + return; + bool levelUp = false; u8 prevPower = player->actor.power; u8 prevCourage = player->actor.courage; From c5f6a164433102c68648c73017284c447e88f22b Mon Sep 17 00:00:00 2001 From: Gotest Date: Sun, 8 Oct 2023 11:18:16 -0600 Subject: [PATCH 09/12] [ci] silently continue when trying to delete ccache.exe if it doesn't exist --- .github/workflows/generate-builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-builds.yml b/.github/workflows/generate-builds.yml index 41f279a13a9..d06cbe522c0 100644 --- a/.github/workflows/generate-builds.yml +++ b/.github/workflows/generate-builds.yml @@ -279,7 +279,7 @@ jobs: if: ${{ !vars.WINDOWS_RUNNER }} run: | choco install ninja - Remove-Item -Path "C:\ProgramData\Chocolatey\bin\ccache.exe" -Force + Remove-Item -Path "C:\ProgramData\Chocolatey\bin\ccache.exe" -Force -ErrorAction SilentlyContinue - uses: actions/checkout@v3 with: submodules: true From f39e69cf9a33c1b62223d8bb81fde319d8fde525 Mon Sep 17 00:00:00 2001 From: Arrenton Date: Sun, 8 Oct 2023 21:08:07 -0600 Subject: [PATCH 10/12] Fixed randomizer crash when you have a heart container as a starting item. --- soh/src/code/z_message_PAL.c | 3 +++ soh/src/code/z_parameter.c | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 3251194fbce..bc2eb8446da 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -3368,6 +3368,9 @@ void Message_Update(PlayState* play) { gSaveContext.inventory.questItems ^= 0x40000000; gSaveContext.healthCapacity += 0x10; gSaveContext.health += heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { if (sLastPlayedSong == OCARINA_SONG_SARIAS) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index d9d555e77e9..9aa664457d8 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2227,7 +2227,9 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.healthCapacity += 0x10; gSaveContext.health += heartUnits; gSaveContext.sohStats.heartContainers++; - Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" From d8d8a1a5ee218cc13133f23fb0d3f0dfeee15c1c Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 9 Nov 2023 22:36:21 -0700 Subject: [PATCH 11/12] Enemy Actors pull their level from the scene rather than be a fixed level. Allowing for more dynamic assignments of levels. EXP table and rate of gain adjusted for reduced need for grinding. Changed the '/' texture in the stats menu. --- soh/include/leveled_stat_math.h | 2 + soh/src/code/leveled_actor_level_table.c | 433 ++++++++++++++++------- soh/src/code/leveled_map_levels.c | 179 ++++++++++ soh/src/code/leveled_overlays.c | 36 +- soh/src/code/leveled_stat_math.c | 49 ++- 5 files changed, 528 insertions(+), 171 deletions(-) create mode 100644 soh/src/code/leveled_map_levels.c diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h index 9bc8499ee89..642fb6dbe17 100644 --- a/soh/include/leveled_stat_math.h +++ b/soh/include/leveled_stat_math.h @@ -20,10 +20,12 @@ u8 GetPlayerStat_MagicUnits(u8 level); u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level); u16 GetPlayerStat_NextLevelExpAtLevel(u8 level); u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); +u16 GetEnemyExperienceReward(u8 level, u16 expRate); f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); f32 Leveled_DamageModify(Actor * actor, Actor * attackingActor, f32 attack); u16 Leveled_GoldSkulltulaExperience(u8 tokens); void Leveled_SetPlayerModifiedStats(Player * player); +s8 Leveled_GetSceneLevel(s16 sceneId); #ifdef __cplusplus } diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c index f722cffe86f..57ea2db6a62 100644 --- a/soh/src/code/leveled_actor_level_table.c +++ b/soh/src/code/leveled_actor_level_table.c @@ -1,107 +1,149 @@ #include "global.h" +typedef struct { + s8 levelModifier; + u16 experienceRate; + u8 ignoreEntry; +} SceneLevelEntry; + ///////////////////////////////////////////////// ////////////////// BOSSES ///////////////////// ///////////////////////////////////////////////// -void Leveled_Gohma(PlayState* play, Actor*actor) { +void Leveled_Gohma(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 7; actor->exp = 120; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 3000; if (actor->category == ACTORCAT_ENEMY) { // Larva actor->level = 5; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 0; if (actor->params < 10) { actor->exp = 5; + levelEntry->experienceRate = 100; } } } -void Leveled_Dodongo(PlayState* play, Actor*actor) { +void Leveled_Dodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->category == ACTORCAT_BOSS) { // King Dodongo actor->level = 15; actor->exp = 500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 4000; } else { actor->level = 14; actor->exp = 18; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 160; } } -void Leveled_Barinade(PlayState* play, Actor*actor) { +void Leveled_Barinade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 21; + levelEntry->levelModifier = 5; if (actor->params == -1) { // Body actor->exp = 850; + levelEntry->experienceRate = 5000; } } -void Leveled_PhantomGanon(PlayState* play, Actor* actor) { +void Leveled_PhantomGanon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 28; + levelEntry->levelModifier = 5; if (actor->params == 1) actor->exp = 1400; + levelEntry->experienceRate = 6000; } -void Leveled_Volvagia(PlayState* play, Actor* actor) { +void Leveled_Volvagia(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 35; + levelEntry->levelModifier = 5; if (actor->id == ACTOR_BOSS_FD2) actor->exp = 2475; + levelEntry->experienceRate = 6300; } -void Leveled_Morpha(PlayState* play, Actor* actor) { +void Leveled_Morpha(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 40; actor->exp = 3500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 6600; } -void Leveled_BongoBongo(PlayState* play, Actor* actor) { +void Leveled_BongoBongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 45; actor->exp = 4666; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 7000; } -void Leveled_Twinrova(PlayState* play, Actor* actor) { +void Leveled_Twinrova(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 52; + levelEntry->levelModifier = 6; if (actor->params == 2) actor->exp = 6000; + levelEntry->experienceRate = 7000; } -void Leveled_Ganondorf(PlayState* play, Actor* actor) { - actor->level = 62; +void Leveled_Ganondorf(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 56; actor->exp = 9999; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 40000; } -void Leveled_Ganon(PlayState* play, Actor* actor) { - actor->level = 66; +void Leveled_Ganon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 60; + levelEntry->levelModifier = 8; } ///////////////////////////////////////////////// /////////////// MINI-BOSSES /////////////////// ///////////////////////////////////////////////// -void Leveled_BigOctorock(PlayState* play, Actor*actor) { +void Leveled_BigOctorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 20; actor->exp = 320; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 2100; } -void Leveled_FlareDancer(PlayState* play, Actor* actor) { +void Leveled_FlareDancer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 34; + levelEntry->levelModifier = 4; if (actor->id == ACTOR_EN_FW) { actor->exp = 1000; + levelEntry->experienceRate = 3000; } } -void Leveled_DarkLink(PlayState* play, Actor* actor) { +void Leveled_DarkLink(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = GET_PLAYER(play)->actor.level; actor->exp = (u16)((f32)GetPlayerStat_NextLevelExpAtLevel(GET_PLAYER(play)->actor.level) * 0.50f); + levelEntry->ignoreEntry = true; } -void Leveled_DeadHand(PlayState* play, Actor* actor) { +void Leveled_DeadHand(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 44; + levelEntry->levelModifier = 4; - if (actor->id == ACTOR_EN_DH) + if (actor->id == ACTOR_EN_DH) { actor->exp = 2465; + levelEntry->experienceRate = 3666; + } } -void Leveled_IronKnuckle(PlayState* play, Actor* actor) { +void Leveled_IronKnuckle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 50; actor->exp = 2733; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 5000; + if (actor->params == 0) { actor->level = 52; actor->exp = 3100; + levelEntry->levelModifier = 6; } if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle actor->level = 55; @@ -111,10 +153,12 @@ void Leveled_IronKnuckle(PlayState* play, Actor* actor) { ///////////////////////////////////////////////// ////////////// NORMAL ENEMIES ///////////////// ///////////////////////////////////////////////// -void Leveled_Dekubaba(PlayState* play, Actor*actor) { +void Leveled_Dekubaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == 1) { // Big baba actor->level = 24; actor->exp = 32; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 116; if (LINK_IS_CHILD) { actor->level = 5; actor->exp = 8; @@ -122,6 +166,8 @@ void Leveled_Dekubaba(PlayState* play, Actor*actor) { } else { actor->level = 2; actor->exp = 3; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 60; if (play->sceneNum == SCENE_BMORI1) { // Forest Temple actor->level = 23; actor->exp = 19; @@ -129,14 +175,18 @@ void Leveled_Dekubaba(PlayState* play, Actor*actor) { } } -void Leveled_StickDekuBaba(PlayState* play, Actor*actor) { +void Leveled_StickDekuBaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 1; actor->exp = 1; + levelEntry->levelModifier = -99; + levelEntry->experienceRate = 1; } -void Leveled_Skulltula(PlayState* play, Actor*actor) { +void Leveled_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 3; actor->exp = 3; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 80; if (play->sceneNum == SCENE_BMORI1) { // Forest Temple actor->level = 21; actor->exp = 19; @@ -155,16 +205,21 @@ void Leveled_Skulltula(PlayState* play, Actor*actor) { if (actor->params == 1) { // Big actor->level += 3; actor->exp += 2; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; } } -void Leveled_Wall_Skulltula(PlayState* play, Actor* actor) { +void Leveled_Wall_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (((play, actor->params & 0xE000) >> 0xD) != 0) { // Gold Skulltula actor->level = gSaveContext.inventory.gsTokens + 1; actor->exp = 0; + levelEntry->ignoreEntry = true; } else { actor->level = 3; actor->exp = 2; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 40; if (play->sceneNum == SCENE_BMORI1) { // Forest Temple actor->level = 24; actor->exp = 18; @@ -172,15 +227,19 @@ void Leveled_Wall_Skulltula(PlayState* play, Actor* actor) { } } -void Leveled_DekuScrub(PlayState* play, Actor*actor) { +void Leveled_DekuScrub(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 8; actor->exp = 7; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 75; } -void Leveled_Tektite(PlayState* play, Actor*actor) { +void Leveled_Tektite(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == -2) { // Blue actor->level = 12; actor->exp = 12; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; if (LINK_IS_ADULT) { actor->level = 25; actor->exp = 21; @@ -192,6 +251,8 @@ void Leveled_Tektite(PlayState* play, Actor*actor) { } else { actor->level = 9; actor->exp = 9; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; if (LINK_IS_ADULT) { actor->level = 27; actor->exp = 24; @@ -199,27 +260,35 @@ void Leveled_Tektite(PlayState* play, Actor*actor) { } } -void Leveled_Guay(PlayState* play, Actor*actor) { +void Leveled_Guay(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 8; actor->exp = 4; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; if (LINK_IS_ADULT) { actor->level = 21; actor->exp = 13; } } -void Leveled_Octorock(PlayState* play, Actor*actor) { +void Leveled_Octorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 13; actor->exp = 12; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 70; if (actor->params != 0) { actor->level = 0; actor->exp = 0; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 0; } } -void Leveled_Armos(PlayState* play, Actor* actor) { +void Leveled_Armos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 12; actor->exp = 17; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 135; if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple actor->level = 45; actor->exp = 60; @@ -229,18 +298,24 @@ void Leveled_Armos(PlayState* play, Actor* actor) { } } -void Leveled_BabyDodongo(PlayState* play, Actor*actor) { +void Leveled_BabyDodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 11; actor->exp = 7; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 75; } -void Leveled_Keese(PlayState* play, Actor*actor) { +void Leveled_Keese(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == 4) { // Ice Keese actor->level = 34; actor->exp = 33; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 60; } else { actor->level = 9; actor->exp = 5; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; if (play->sceneNum == SCENE_HIDAN) { // Fire Temple actor->level = 28; @@ -263,37 +338,49 @@ void Leveled_Keese(PlayState* play, Actor*actor) { actor->exp = 38; } if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 44; + actor->level = 47; + actor->exp = 80; } } } -void Leveled_Peahat(PlayState* play, Actor*actor) { +void Leveled_Peahat(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 18; actor->exp = 47; + levelEntry->levelModifier = 10; + levelEntry->experienceRate = 275; if (actor->params == 1) { // Larva actor->level = 13; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 0; } } -void Leveled_Poh(PlayState* play, Actor*actor) { +void Leveled_Poh(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 10; actor->exp = 14; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; if (actor->params == 2 || actor->params == 3) { // Composer actor->level = 15; actor->exp = 25; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 130; } } -void Leveled_Poh_Field(PlayState* play, Actor*actor) { +void Leveled_Poh_Field(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 24; actor->exp = 25; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; } -void Leveled_ReDead(PlayState* play, Actor*actor) { +void Leveled_ReDead(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params >= -1) { actor->level = 25; actor->exp = 45; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 280; if (play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple actor->level = 42; actor->exp = 171; @@ -304,56 +391,75 @@ void Leveled_ReDead(PlayState* play, Actor*actor) { } else { // Gibdo actor->level = 43; actor->exp = 183; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 305; } } -void Leveled_Shabom(PlayState* play, Actor*actor) { +void Leveled_Shabom(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 15; + levelEntry->levelModifier = -1; } -void Leveled_Bari(PlayState* play, Actor*actor) { +void Leveled_Bari(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 18; actor->exp = 25; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 85; } -void Leveled_Biri(PlayState* play, Actor*actor) { +void Leveled_Biri(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 16; actor->exp = 17; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 100; } -void Leveled_Stinger(PlayState* play, Actor*actor) { +void Leveled_Stinger(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 17; actor->exp = 19; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 125; if (play->sceneNum == SCENE_MIZUSIN) { // Water Temple actor->level = 37; actor->exp = 85; } } -void Leveled_JabuTentacle(PlayState* play, Actor*actor) { +void Leveled_JabuTentacle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 19; + levelEntry->levelModifier = 3; if (actor->params < 3) { actor->exp = 61; + levelEntry->experienceRate = 400; } } -void Leveled_Tailpasaran(PlayState* play, Actor*actor) { +void Leveled_Tailpasaran(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 19; actor->exp = 22; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 125; } -void Leveled_Stalchild(PlayState* play, Actor*actor) { +void Leveled_Stalchild(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 6 + (u16)(play, actor->params / 2); actor->exp = 3 + (u16)(play, actor->params / 2.5); + levelEntry->levelModifier = 1 + (u16)(play, actor->params / 2); + levelEntry->experienceRate = 45 + (u16)(play, actor->params * 5); } -void Leveled_Beamos(PlayState* play, Actor*actor) { +void Leveled_Beamos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == 0) { // Big actor->level = 48; actor->exp = 90; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 150; } else { actor->level = 11; actor->exp = 17; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 150; if (play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple actor->level = 43; @@ -365,16 +471,18 @@ void Leveled_Beamos(PlayState* play, Actor*actor) { actor->level = 50; actor->exp = 90; } else if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 120; + actor->level = 47; + actor->exp = 300; } } } -void Leveled_Wolfos(PlayState* play, Actor* actor) { +void Leveled_Wolfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == 0) { actor->level = 10; actor->exp = 24; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; if (LINK_IS_ADULT) { actor->level = 20; actor->exp = 29; @@ -392,51 +500,69 @@ void Leveled_Wolfos(PlayState* play, Actor* actor) { } else { // White actor->level = 37; actor->exp = 126; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 180; } if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 270; + actor->level = 47; + actor->exp = 420; } } -void Leveled_Lizalfos(PlayState* play, Actor*actor) { +void Leveled_Lizalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == -2) { // Dinolfos actor->level = 52; actor->exp = 250; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 320; } else if (actor->params == -1) { // Lizalfos actor->level = 47; actor->exp = 210; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 290; } else { // Miniboss Lizalfos actor->level = 13; actor->exp = 140; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 1150; actor->ignoreExpReward = true; } if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 320; + actor->level = 47; + actor->exp = 570; } } -void Leveled_Moblins(PlayState* play, Actor*actor) { +void Leveled_Moblins(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == -1) { // Spear actor->level = 24; actor->exp = 32; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; } else if (actor->params == 0) { // Club actor->level = 25; actor->exp = 47; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 240; } else { // Spear patrol actor->level = 24; actor->exp = 24; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; } } -void Leveled_Bubble(PlayState* play, Actor*actor) { +void Leveled_Bubble(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { if (actor->params == -1) { // Blue actor->level = 25; actor->exp = 29; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 105; } else if (actor->params == -2) { // Red actor->level = 32; actor->exp = 47; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 110; if ( play->sceneNum == SCENE_HAKADAN) { // Shadow Temple actor->level = 40; actor->exp = 75; @@ -444,12 +570,18 @@ void Leveled_Bubble(PlayState* play, Actor*actor) { } else if (actor->params == -3) { // White actor->level = 45; actor->exp = 76; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 107; } else if (actor->params == -4) { // Big Green actor->level = 41; actor->exp = 82; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; } else { // Green actor->level = 26; actor->exp = 31; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 93; if (play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple actor->level = 41; @@ -463,18 +595,21 @@ void Leveled_Bubble(PlayState* play, Actor*actor) { } } if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 100; + actor->level = 47; + actor->exp = 220; } } -void Leveled_Stalfos(PlayState* play, Actor*actor) { +void Leveled_Stalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 27; actor->exp = 64; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 345; if (play->sceneNum == SCENE_BMORI1) { // Forest Temple actor->ignoreExpReward = true; actor->exp = 250; + levelEntry->experienceRate = 800; }else if (play->sceneNum == SCENE_HAKADAN) { // Shadow Temple actor->level = 43; actor->exp = 214; @@ -487,14 +622,16 @@ void Leveled_Stalfos(PlayState* play, Actor*actor) { actor->level = 50; actor->exp = 260; } else if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 345; + actor->level = 47; + actor->exp = 700; } } -void Leveled_Floormaster(PlayState* play, Actor*actor) { +void Leveled_Floormaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 26; actor->exp = 41; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 170; actor->ignoreExpReward = true; if (play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple @@ -506,9 +643,11 @@ void Leveled_Floormaster(PlayState* play, Actor*actor) { } } -void Leveled_Wallmaster(PlayState* play, Actor*actor) { +void Leveled_Wallmaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 27; actor->exp = 44; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 175; if (play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_HAKADAN) { // Bottom of the Well and Shadow Temple actor->level = 41; @@ -522,14 +661,18 @@ void Leveled_Wallmaster(PlayState* play, Actor*actor) { } } -void Leveled_Poh_Sisters(PlayState* play, Actor* actor) { +void Leveled_Poh_Sisters(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 28; actor->exp = 127; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 450; } -void Leveled_LikeLike(PlayState* play, Actor* actor) { +void Leveled_LikeLike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 32; actor->exp = 99; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 245; if (play->sceneNum == SCENE_MIZUSIN) { // Water Temple actor->level = 38; actor->exp = 142; @@ -544,14 +687,16 @@ void Leveled_LikeLike(PlayState* play, Actor* actor) { actor->level = 50; actor->exp = 190; } else if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 260; + actor->level = 47; + actor->exp = 375; } } -void Leveled_TorchSlug(PlayState* play, Actor* actor) { +void Leveled_TorchSlug(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 33; actor->exp = 67; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 135; if (play->sceneNum == SCENE_JYASINZOU) { // Spirit Temple actor->level = 48; actor->exp = 163; @@ -559,55 +704,73 @@ void Leveled_TorchSlug(PlayState* play, Actor* actor) { actor->level = 50; actor->exp = 170; } else if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 240; + actor->level = 47; + actor->exp = 350; } } -void Leveled_Freezer(PlayState* play, Actor* actor) { +void Leveled_Freezer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 35; actor->exp = 101; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 205; if (play->sceneNum == SCENE_GANONTIKA || play->sceneNum == SCENE_GANON) { // Ganon's Castle actor->level = 50; actor->exp = 160; } } -void Leveled_ShellBlade(PlayState* play, Actor* actor) { +void Leveled_ShellBlade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 38; actor->exp = 129; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 200; if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 220; + actor->level = 47; + actor->exp = 390; } } -void Leveled_RollingSpike(PlayState* play, Actor* actor) { +void Leveled_RollingSpike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 37; actor->exp = 100; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; if (play->sceneNum == SCENE_MEN) { - actor->level = 55; - actor->exp = 200; + actor->level = 47; + actor->exp = 325; } } -void Leveled_GerudoFighter(PlayState* play, Actor* actor) { +void Leveled_GerudoFighter(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 45; actor->exp = 455; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 1250; } -void Leveled_Leever(PlayState* play, Actor* actor) { +void Leveled_Leever(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 45; actor->exp = 15; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 20; } -void Leveled_Anubis(PlayState* play, Actor* actor) { +void Leveled_Anubis(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { actor->level = 48; - if (actor->id == ACTOR_EN_ANUBICE) + levelEntry->levelModifier = 3; + if (actor->id == ACTOR_EN_ANUBICE) { actor->exp = 120; + levelEntry->experienceRate = 155; + } } void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride) { + SceneLevelEntry levelEntry; + levelEntry.levelModifier = 0; + levelEntry.experienceRate = 0; + levelEntry.ignoreEntry = 0; + u16 actorId = actor->id; if (actorIdOverride != 0) { actorId = actorIdOverride; @@ -616,176 +779,182 @@ void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverr switch (actorId) { case ACTOR_BOSS_DODONGO: case ACTOR_EN_DODONGO: - Leveled_Dodongo(play, actor); + Leveled_Dodongo(play, actor, &levelEntry); break; case ACTOR_BOSS_GOMA: case ACTOR_EN_GOMA: - Leveled_Gohma(play, actor); + Leveled_Gohma(play, actor, &levelEntry); break; case ACTOR_BOSS_VA: - Leveled_Barinade(play, actor); + Leveled_Barinade(play, actor, &levelEntry); break; case ACTOR_BOSS_GANONDROF: - Leveled_PhantomGanon(play, actor); + Leveled_PhantomGanon(play, actor, &levelEntry); break; case ACTOR_BOSS_FD: case ACTOR_BOSS_FD2: - Leveled_Volvagia(play, actor); + Leveled_Volvagia(play, actor, &levelEntry); break; case ACTOR_BOSS_MO: - Leveled_Morpha(play, actor); + Leveled_Morpha(play, actor, &levelEntry); break; case ACTOR_BOSS_SST: - Leveled_BongoBongo(play, actor); + Leveled_BongoBongo(play, actor, &levelEntry); break; case ACTOR_BOSS_TW: - Leveled_Twinrova(play, actor); + Leveled_Twinrova(play, actor, &levelEntry); break; case ACTOR_BOSS_GANON: - Leveled_Ganondorf(play, actor); + Leveled_Ganondorf(play, actor, &levelEntry); break; case ACTOR_BOSS_GANON2: - Leveled_Ganon(play, actor); + Leveled_Ganon(play, actor, &levelEntry); break; case ACTOR_EN_DEKUBABA: - Leveled_Dekubaba(play, actor); + Leveled_Dekubaba(play, actor, &levelEntry); break; case ACTOR_EN_KAREBABA: - Leveled_StickDekuBaba(play, actor); + Leveled_StickDekuBaba(play, actor, &levelEntry); break; case ACTOR_EN_ST: - Leveled_Skulltula(play, actor); + Leveled_Skulltula(play, actor, &levelEntry); break; case ACTOR_EN_DEKUNUTS: - Leveled_DekuScrub(play, actor); + Leveled_DekuScrub(play, actor, &levelEntry); break; case ACTOR_EN_TITE: - Leveled_Tektite(play, actor); + Leveled_Tektite(play, actor, &levelEntry); break; case ACTOR_EN_CROW: - Leveled_Guay(play, actor); + Leveled_Guay(play, actor, &levelEntry); break; case ACTOR_EN_OKUTA: - Leveled_Octorock(play, actor); + Leveled_Octorock(play, actor, &levelEntry); break; case ACTOR_EN_AM: - Leveled_Armos(play, actor); + Leveled_Armos(play, actor, &levelEntry); break; case ACTOR_EN_DODOJR: - Leveled_BabyDodongo(play, actor); + Leveled_BabyDodongo(play, actor, &levelEntry); break; case ACTOR_EN_FIREFLY: - Leveled_Keese(play, actor); + Leveled_Keese(play, actor, &levelEntry); break; case ACTOR_EN_PEEHAT: - Leveled_Peahat(play, actor); + Leveled_Peahat(play, actor, &levelEntry); break; case ACTOR_EN_POH: - Leveled_Poh(play, actor); + Leveled_Poh(play, actor, &levelEntry); case ACTOR_EN_PO_FIELD: - Leveled_Poh_Field(play, actor); + Leveled_Poh_Field(play, actor, &levelEntry); break; case ACTOR_EN_RD: - Leveled_ReDead(play, actor); + Leveled_ReDead(play, actor, &levelEntry); break; case ACTOR_EN_SKB: - Leveled_Stalchild(play, actor); + Leveled_Stalchild(play, actor, &levelEntry); break; case ACTOR_EN_SW: - Leveled_Wall_Skulltula(play, actor); + Leveled_Wall_Skulltula(play, actor, &levelEntry); break; case ACTOR_EN_VM: - Leveled_Beamos(play, actor); + Leveled_Beamos(play, actor, &levelEntry); break; case ACTOR_EN_WF: - Leveled_Wolfos(play, actor); + Leveled_Wolfos(play, actor, &levelEntry); break; case ACTOR_EN_ZF: - Leveled_Lizalfos(play, actor); + Leveled_Lizalfos(play, actor, &levelEntry); break; case ACTOR_EN_BUBBLE: - Leveled_Shabom(play, actor); + Leveled_Shabom(play, actor, &levelEntry); break; case ACTOR_EN_BILI: - Leveled_Biri(play, actor); + Leveled_Biri(play, actor, &levelEntry); break; case ACTOR_EN_VALI: - Leveled_Bari(play, actor); + Leveled_Bari(play, actor, &levelEntry); break; case ACTOR_EN_TP: - Leveled_Tailpasaran(play, actor); + Leveled_Tailpasaran(play, actor, &levelEntry); break; case ACTOR_EN_BA: case ACTOR_EN_BX: - Leveled_JabuTentacle(play, actor); + Leveled_JabuTentacle(play, actor, &levelEntry); break; case ACTOR_EN_EIYER: case ACTOR_EN_WEIYER: - Leveled_Stinger(play, actor); + Leveled_Stinger(play, actor, &levelEntry); break; case ACTOR_EN_BIGOKUTA: - Leveled_BigOctorock(play, actor); + Leveled_BigOctorock(play, actor, &levelEntry); break; case ACTOR_EN_MB: - Leveled_Moblins(play, actor); + Leveled_Moblins(play, actor, &levelEntry); break; case ACTOR_EN_BB: - Leveled_Bubble(play, actor); + Leveled_Bubble(play, actor, &levelEntry); break; case ACTOR_EN_TEST: - Leveled_Stalfos(play, actor); + Leveled_Stalfos(play, actor, &levelEntry); break; case ACTOR_EN_FLOORMAS: - Leveled_Floormaster(play, actor); + Leveled_Floormaster(play, actor, &levelEntry); break; case ACTOR_EN_WALLMAS: - Leveled_Wallmaster(play, actor); + Leveled_Wallmaster(play, actor, &levelEntry); break; case ACTOR_EN_PO_SISTERS: - Leveled_Poh_Sisters(play, actor); + Leveled_Poh_Sisters(play, actor, &levelEntry); break; case ACTOR_EN_RR: - Leveled_LikeLike(play, actor); + Leveled_LikeLike(play, actor, &levelEntry); break; case ACTOR_EN_BW: - Leveled_TorchSlug(play, actor); + Leveled_TorchSlug(play, actor, &levelEntry); break; case ACTOR_EN_FZ: - Leveled_Freezer(play, actor); + Leveled_Freezer(play, actor, &levelEntry); break; case ACTOR_EN_SB: - Leveled_ShellBlade(play, actor); + Leveled_ShellBlade(play, actor, &levelEntry); break; case ACTOR_EN_NY: - Leveled_RollingSpike(play, actor); + Leveled_RollingSpike(play, actor, &levelEntry); break; case ACTOR_EN_TORCH2: - Leveled_DarkLink(play, actor); + Leveled_DarkLink(play, actor, &levelEntry); break; case ACTOR_EN_DH: case ACTOR_EN_DHA: - Leveled_DeadHand(play, actor); + Leveled_DeadHand(play, actor, &levelEntry); break; case ACTOR_EN_FD: case ACTOR_EN_FW: case ACTOR_EN_FD_FIRE: - Leveled_FlareDancer(play, actor); + Leveled_FlareDancer(play, actor, &levelEntry); break; case ACTOR_EN_IK: - Leveled_IronKnuckle(play, actor); + Leveled_IronKnuckle(play, actor, &levelEntry); break; case ACTOR_EN_GELDB: - Leveled_GerudoFighter(play, actor); + Leveled_GerudoFighter(play, actor, &levelEntry); break; case ACTOR_EN_REEBA: - Leveled_Leever(play, actor); + Leveled_Leever(play, actor, &levelEntry); break; case ACTOR_EN_ANUBICE: case ACTOR_EN_ANUBICE_FIRE: - Leveled_Anubis(play, actor); + Leveled_Anubis(play, actor, &levelEntry); break; default: actor->level = GET_PLAYER(play)->actor.level; break; } + + s8 sceneLevel = Leveled_GetSceneLevel(play->sceneNum); + if (!levelEntry.ignoreEntry && sceneLevel >= 0) { + actor->level = max(sceneLevel + levelEntry.levelModifier, 1); + actor->exp = GetEnemyExperienceReward(actor->level, levelEntry.experienceRate); + } } \ No newline at end of file diff --git a/soh/src/code/leveled_map_levels.c b/soh/src/code/leveled_map_levels.c new file mode 100644 index 00000000000..53ec87c9727 --- /dev/null +++ b/soh/src/code/leveled_map_levels.c @@ -0,0 +1,179 @@ +#include "global.h" + +s8 Leveled_GetSceneLevel(s16 sceneId) { + switch (sceneId) { + case SCENE_YDAN: + case SCENE_YDAN_BOSS: + return 2; // Deku Tree + + case SCENE_DDAN: + case SCENE_DDAN_BOSS: + return 10; // Dodongo's + + case SCENE_BDAN: + case SCENE_BDAN_BOSS: + return 16; // Jabu Jabu + + case SCENE_BMORI1: + case SCENE_MORIBOSSROOM: + return 24; // Forest Temple + + case SCENE_HIDAN: + case SCENE_FIRE_BS: + return 30; // Fire Temple + + case SCENE_MIZUSIN: + case SCENE_MIZUSIN_BS: + return 35; // Water Temple + + case SCENE_JYASINZOU: + case SCENE_JYASINBOSS: + return 45; // Spirit Temple + + case SCENE_HAKADAN: + case SCENE_HAKADAN_BS: + return 40; // Shadow Temple + + case SCENE_HAKADANCH: + return 38; // Bottom of the Well + + case SCENE_ICE_DOUKUTO: + return 32; // Ice Cavern + + case SCENE_GANON: + case SCENE_GANON_BOSS: + case SCENE_GANON_FINAL: + case SCENE_GANON_DEMO: + return 50; // Ganon's Tower + + case SCENE_MEN: + return 43; // Gerudo Training Grounds + + case SCENE_GERUDOWAY: + return 42; // Thieve's Hideout + + case SCENE_GANONTIKA: + return 52; // Inside Ganon's Castle + + case SCENE_GANON_SONOGO: + // Tower Collapse + case SCENE_GANONTIKA_SONOGO: + return 53; // Castle Collapse + + case SCENE_MARKET_RUINS: + return 23; + + case SCENE_HAKAANA: + return 10; // Redead Grave + + case SCENE_HAKAANA_OUKE: + return 8; // Royal Family's Tomb + + case SCENE_HAKASITARELAY: + return 22; // Dampe's Grave + + case SCENE_KINSUTA: + return 99; // House of Skulltula + + case SCENE_SPOT00: + if (LINK_IS_CHILD) + return 5; + else // Hyrule Field + return 20; + + case SCENE_SPOT02: + if (LINK_IS_CHILD) + return 7; + else // Graveyard + return 20; + + case SCENE_SPOT03: + if (LINK_IS_CHILD) + return 8; + else // Zora's River + return 24; + + case SCENE_SPOT04: + if (LINK_IS_CHILD) + return 3; + else // Kokiri Forest + return 21; + + case SCENE_SPOT05: + if (LINK_IS_CHILD) + return 9; + else // Sacred Forest Meadow + return 23; + + case SCENE_SPOT06: + if (LINK_IS_CHILD) + return 7; + else // Lake Hylia + return 22; + + case SCENE_SPOT08: + if (LINK_IS_CHILD) + return 13; + else // Zora's Fountain + return 30; + + case SCENE_SPOT09: + if (LINK_IS_CHILD) + return 10; + else // Gerudo Valley + return 28; + + case SCENE_SPOT10: + if (LINK_IS_CHILD) + return 7; + else // Lost Woods + return 22; + + case SCENE_SPOT11: + return 42; // Desert Colossus + + case SCENE_SPOT12: + return 40; // Gerudo's Fortress + + case SCENE_SPOT16: + if (LINK_IS_CHILD) + return 8; // Death Mountain Trail + else + return 24; + + case SCENE_SPOT17: + return 11; // Death Mountain Crater + + case SCENE_SPOT20: + return 6;// Lon Lon Ranch + + case SCENE_GANON_TOU: + return 25; // Outside Ganon's Castle + + + // Debug only scenes + case SCENE_TEST01: + // Test Map + case SCENE_BESITU: + // Test Room + case SCENE_DEPTH_TEST: + // Depth Test + case SCENE_SYOTES: + // Stalfos Mini-Boss + case SCENE_SYOTES2: + // Stalfos Boss + case SCENE_SUTARU: + // Dark Link + case SCENE_HAIRAL_NIWA2: + // Castle Maze (Broken) + case SCENE_SASATEST: + // SRD Room + case SCENE_TESTROOM: + // Chest Room + return 45; + + default: + return -1; + } + return -1; +} \ No newline at end of file diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 27c5afe5677..3ae6aba70cd 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -641,10 +641,11 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { } // Initialize textures - Leveled_DrawTexIA8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); - Leveled_DrawTexIA8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); - Leveled_DrawTexIA8(play, (u8*)gMsgChar2BPlusSignTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); - Leveled_DrawTexIA8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 16, -114, -222, 16, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 16, -114, -222, 16, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar2BPlusSignTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar2FSolidusTex, 8, 16, -114, -222, 8, 16, 255, 255, 255); Leveled_DrawTex32(play, (u8*)gKokiriSwordIconTex, 32, 32, -192, -268, 32, 32); Leveled_DrawTex32(play, (u8*)gSilverGauntletsIconTex, 32, 32, -192, -268, 32, 32); @@ -657,22 +658,21 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { // Values and Icons // Level - for (u8 i = 0; i < 10; i++) { - Leveled_DrawTexIA8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 8, 92, statY, 8, 16, 255, 255, 255); - Leveled_DrawTexIA8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 8, 95, statY, 8, 16, 255, 255, 255); - } + Leveled_DrawTexI8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 8, 92, statY, 8, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 8, 95, statY, 8, 16, 255, 255, 255); + Leveled_BigValueNumberDraw(play, 100, statY - 6, player->actor.level, 255, 255, 255, 255); statY += 10; // Health Leveled_DrawTexIA8(play, (u8*)gHeartFullTex, 8, 8, 92, statY, 16, 16, 255, 70, 0); - Leveled_DrawTexI8(play, (u8*)digitTextures[1], 8, 11, 116, statY - 2, 8, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar2FSolidusTex, 8, 9, 119, statY - 1, 8, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, 100, statY, gSaveContext.health, 255, 255, 255); Leveled_ValueNumberDraw(play, 123, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; // Magic if (gSaveContext.magicCapacity > 0) { Leveled_DrawTex32(play, (u8*)gBigMagicJarIconTex, 8, 8, 92, statY, 24, 24); - Leveled_DrawTexI8(play, (u8*)digitTextures[1], 8, 11, 116, statY - 2, 8, 16, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar2FSolidusTex, 8, 9, 119, statY - 1, 8, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, 100, statY, gSaveContext.magic, 255, 255, 255); Leveled_ValueNumberDraw(play, 123, statY, gSaveContext.magicCapacity, 120, 255, 0); statY += 8; @@ -685,14 +685,10 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { Leveled_DrawTex32(play, (u8*)gSilverGauntletsIconTex, 8, 7, 92, statY, 8, 7); Leveled_ValueNumberDraw(play, 100, statY, player->actor.power, 255, 255, 255); if (player->actor.powerModifier > 0){ - for (u8 i = 0; i < 10; i++) { - Leveled_DrawTexIA8(play, (u8*)gMsgChar2BPlusSignTex, 8, 8, 114, statY, 8, 16, 120, 255, 0); - } + Leveled_DrawTexI8(play, (u8*)gMsgChar2BPlusSignTex, 16, 12, 111, statY - 3, 8, 16, 120, 255, 0); Leveled_ValueNumberDraw(play, 118, statY, player->actor.powerModifier, 120, 255, 0); } else if (player->actor.powerModifier < 0){ - for (u8 i = 0; i < 10; i++) { - Leveled_DrawTexIA8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 8, 114, statY, 8, 16, 255, 0, 0); - } + Leveled_DrawTexI8(play, (u8*)gMsgChar2DHyphenMinusTex, 16, 16, 111, statY - 5, 8, 16, 255, 0, 0); Leveled_ValueNumberDraw(play, 118, statY, -player->actor.powerModifier, 255, 0, 0); } statY += 8; @@ -700,14 +696,10 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { Leveled_DrawTex32(play, (u8*)gHylianShieldIconTex, 8, 7, 92, statY, 8, 7); Leveled_ValueNumberDraw(play, 100, statY, player->actor.courage, 255, 255, 255); if (player->actor.courageModifier > 0){ - for (u8 i = 0; i < 10; i++) { - Leveled_DrawTexIA8(play, (u8*)gMsgChar2BPlusSignTex, 8, 8, 114, statY, 8, 16, 120, 255, 0); - } + Leveled_DrawTexI8(play, (u8*)gMsgChar2BPlusSignTex, 16, 12, 111, statY - 3, 8, 16, 120, 255, 0); Leveled_ValueNumberDraw(play, 118, statY, player->actor.courageModifier, 120, 255, 0); } else if (player->actor.courageModifier < 0){ - for (u8 i = 0; i < 10; i++) { - Leveled_DrawTexIA8(play, (u8*)gMsgChar2DHyphenMinusTex, 8, 8, 114, statY, 8, 16, 255, 0, 0); - } + Leveled_DrawTexI8(play, (u8*)gMsgChar2DHyphenMinusTex, 16, 16, 113, statY - 5, 8, 16, 255, 0, 0); Leveled_ValueNumberDraw(play, 118, statY, -player->actor.courageModifier, 255, 0, 0); } statY += 67; diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c index 4c2d59acb8c..138d93653ac 100644 --- a/soh/src/code/leveled_stat_math.c +++ b/soh/src/code/leveled_stat_math.c @@ -1,23 +1,24 @@ #include "global.h" -static u32 sExpTable[] = { 0, 30, 65, 110, 167, 240, 334, 452, 598, 777, 993, - 1251, 1555, 1910, 2320, 2791, 3329, 3937, 4621, 5388, 6242, 7189, - 8235, 9386, 10647, 12026, 13528, 15160, 16929, 18840, 20902, 23120, 25501, - 28054, 30784, 33700, 36809, 40119, 43637, 47371, 51329, 55520, 59951, 64630, - 69567, 74770, 80248, 86008, 92061, 98416, 105080, 112065, 119379, 127032, 135033, - 143392, 152119, 161225, 170719, 180611, 190912, 201632, 212783, 224374, 236417, 248922, - 261901, 275365, 289325, 303792, 318779, 334297, 350358, 366973, 384155, 401916, 420268, - 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, - 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 }; +static u32 sExpTable[] = { 0, 30, 64, 105, 155, 217, 292, 385, 497, 632, 792, + 981, 1202, 1458, 1753, 2089, 2471, 2903, 3388, 3930, 4534, 5203, + 5942, 6755, 7648, 8625, 9691, 10851, 12110, 13474, 14949, 16540, 18253, + 20094, 22070, 24188, 26453, 28874, 31456, 34209, 37139, 40254, 43562, 47073, + 50794, 54734, 58904, 63312, 67968, 72883, 78067, 83531, 89286, 95345, 101719, + 108421, 115463, 122859, 130623, 138770, 147314, 156270, 165655, 175485, 185778, 196552, + 207824, 219614, 231943, 244831, 258300, 272372, 287071, 302422, 318450, 335182, 352645, + 370870, 389885, 409724, 430419, 452005, 474520, 498001, 522489, 548027, 574659, 602434, + 631400, 661611, 693122, 725993, 760287, 796070, 833414, 872395, 913094, 955597, 999999 }; /* Old table -{ 0, 40, 89, 154, 241, 355, 504, 694, 930, 1219, 1568, 1982, 2468, - 3033, 3681, 4420, 5257, 6196, 7245, 8409, 9695, 11110, 12659, 14349, 16185, 18175, - 20324, 22640, 25127, 27792, 30642, 33683, 36921, 40362, 44012, 47879, 51967, 56284, 60836, - 65628, 70668, 75961, 81513, 87332, 93422, 99792, 106445, 113390, 120632, 128177, 136032, 144203, - 152697, 161518, 170675, 180173, 190017, 200216, 210774, 221698, 232995, 244670, 256730, 269181, 282030, - 295282, 308944, 323022, 337522, 352451, 367815, 383621, 399873, 416580, 433746, 451379, 469484, 488067, - 507136, 526696, 546754, 567315, 588386, 609974, 632084, 654723, 677897, 701612, 725875, 750692, 776069, - 802012, 828528, 855623, 883303, 911574, 940443, 969916, 999999 };*/ +{ 0, 30, 65, 110, 167, 240, 334, 452, 598, 777, 993, + 1251, 1555, 1910, 2320, 2791, 3329, 3937, 4621, 5388, 6242, 7189, + 8235, 9386, 10647, 12026, 13528, 15160, 16929, 18840, 20902, 23120, 25501, + 28054, 30784, 33700, 36809, 40119, 43637, 47371, 51329, 55520, 59951, 64630, + 69567, 74770, 80248, 86008, 92061, 98416, 105080, 112065, 119379, 127032, 135033, + 143392, 152119, 161225, 170719, 180611, 190912, 201632, 212783, 224374, 236417, 248922, + 261901, 275365, 289325, 303792, 318779, 334297, 350358, 366973, 384155, 401916, 420268, + 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, + 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 };*/ u16 GetActorStat_DisplayAttack(u16 attack, u8 power) { return GetActorStat_Attack(attack, power) / (1 + (float)power / 30.0f); @@ -99,6 +100,20 @@ u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp) { return nextLv; } +u16 GetEnemyExperienceReward(u8 level, u16 expRate) { + if (expRate == 0) + return 0; + + return min( + max( + round( + (3 + min(floor(level / 6) * 4, 4) + (level - 1) * ((0.1 + (level / 95.0)) + pow(max(level - 8, 0), 1.25) / 80.0)) * expRate / 100.0 + ), 1 + ), + 9999 + ); +} + f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) { f32 damage = GetActorStat_Attack(attack, power); if (power >= courage) { From 0054dcdbe07939a179e1566ed8f8532cdb64aa86 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 9 Nov 2023 23:14:00 -0700 Subject: [PATCH 12/12] Fix non-windows build errors --- soh/src/code/leveled_actor_level_table.c | 2 +- soh/src/code/leveled_stat_math.c | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c index 57ea2db6a62..ebdcf52f2be 100644 --- a/soh/src/code/leveled_actor_level_table.c +++ b/soh/src/code/leveled_actor_level_table.c @@ -954,7 +954,7 @@ void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverr s8 sceneLevel = Leveled_GetSceneLevel(play->sceneNum); if (!levelEntry.ignoreEntry && sceneLevel >= 0) { - actor->level = max(sceneLevel + levelEntry.levelModifier, 1); + actor->level = CLAMP_MIN(sceneLevel + levelEntry.levelModifier, 1); actor->exp = GetEnemyExperienceReward(actor->level, levelEntry.experienceRate); } } \ No newline at end of file diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c index 138d93653ac..f70bb3273d2 100644 --- a/soh/src/code/leveled_stat_math.c +++ b/soh/src/code/leveled_stat_math.c @@ -104,14 +104,7 @@ u16 GetEnemyExperienceReward(u8 level, u16 expRate) { if (expRate == 0) return 0; - return min( - max( - round( - (3 + min(floor(level / 6) * 4, 4) + (level - 1) * ((0.1 + (level / 95.0)) + pow(max(level - 8, 0), 1.25) / 80.0)) * expRate / 100.0 - ), 1 - ), - 9999 - ); + return CLAMP(round((3 + CLAMP_MAX(floor(level / 6) * 4, 4) + (level - 1) * ((0.1 + (level / 95.0)) + pow(CLAMP_MIN(level - 8, 0), 1.25) / 80.0)) * expRate / 100.0), 1, 9999); } f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) {