From c4ba2221ffec49471fdd9d9d32f8e498f90ec443 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 23 Aug 2024 02:06:03 -0600 Subject: [PATCH 01/18] Inserted Leveled code. Needs further testing. --- 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 | 34 + soh/include/z64actor.h | 21 +- 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 | 8 +- soh/soh/Enhancements/mods.cpp | 4 +- soh/soh/OTRGlobals.cpp | 44 + soh/soh/OTRGlobals.h | 1 + soh/soh/SaveManager.cpp | 19 +- soh/soh/SaveManager.h | 1 + soh/soh/SohMenuBar.cpp | 67 ++ soh/src/code/leveled_actor_level_table.c | 970 ++++++++++++++++++ soh/src/code/leveled_map_levels.c | 178 ++++ soh/src/code/leveled_overlays.c | 765 ++++++++++++++ soh/src/code/leveled_stat_math.c | 261 +++++ soh/src/code/z_actor.c | 46 +- soh/src/code/z_collision_check.c | 19 +- soh/src/code/z_en_item00.c | 2 +- soh/src/code/z_lifemeter.c | 27 +- soh/src/code/z_message_PAL.c | 18 +- soh/src/code/z_parameter.c | 106 +- soh/src/code/z_player_lib.c | 47 + 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 | 13 +- .../overlays/actors/ovl_Boss_Fd/z_boss_fd.c | 18 +- .../overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c | 54 +- .../actors/ovl_Boss_Ganon/z_boss_ganon.c | 29 +- .../actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 22 +- .../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 | 19 +- .../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 | 14 +- .../ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c | 2 +- .../actors/ovl_En_Bubble/z_en_bubble.c | 2 +- soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c | 2 + .../actors/ovl_En_Clear_Tag/z_en_clear_tag.c | 11 +- .../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 | 7 +- .../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 | 9 +- 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 | 4 + .../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 | 69 +- .../ovl_file_choose/z_file_choose.c | 58 +- .../misc/ovl_kaleido_scope/z_kaleido_debug.c | 2 + .../ovl_kaleido_scope/z_kaleido_equipment.c | 5 + 82 files changed, 3055 insertions(+), 238 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_map_levels.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 77d57899754..c3176ff23f5 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -392,6 +392,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); @@ -545,7 +546,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); @@ -1062,6 +1063,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); @@ -1120,6 +1125,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..45bfc4886a9 --- /dev/null +++ b/soh/include/leveled_stat_math.h @@ -0,0 +1,34 @@ +#ifndef LEVELED_STAT_MATH_H +#define LEVELED_STAT_MATH_H +#define HEALTH_ATTACK_MULTIPLIER 15 +#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); +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); +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 +} +#endif + +#endif \ No newline at end of file diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 82c8611d101..f91bc661ea8 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 @@ -179,8 +179,19 @@ typedef struct Actor { /* 0x138 */ ActorResetFunc reset; /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom // #region SOH [General] - /* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init + /* */ u16 maximumHealth; // Max health value for use with health bars, set on actor init // #endregion + 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 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 d03b64da5ab..64f5f2ef287 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -644,7 +644,7 @@ typedef struct Player { /* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful /* 0x089C */ s16 unk_89C; /* 0x089E */ u16 floorSfxOffset; - /* 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 c7bee045fc1..285e1d6949e 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -181,7 +181,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; @@ -325,6 +325,11 @@ typedef struct { /* */ u16 adultTradeItems; /* */ u8 triforcePiecesCollected; // #endregion + 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 } SaveContext; // size = 0x1428 typedef enum { diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 4badd827973..fa39d5e701a 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -1230,7 +1230,7 @@ void Draw_Placements(){ DrawPositionsRadioBoxes(CVAR_COSMETIC("HUD.HeartsCount")); DrawPositionSlider(CVAR_COSMETIC("HUD.HeartsCount"),-22,ImGui::GetWindowViewport()->Size.y,-125,ImGui::GetWindowViewport()->Size.x); DrawScaleSlider(CVAR_COSMETIC("HUD.HeartsCount"),0.7f); - UIWidgets::EnhancementSliderInt("Heart line length : %d", "##HeartLineLength", CVAR_COSMETIC("HUD.Hearts.LineLength"), 0, 20, "", 10); + UIWidgets::EnhancementSliderInt("Heart line length : %d", "##HeartLineLength", CVAR_COSMETIC("HUD.Hearts.LineLength"), 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 3e4d271a827..4b621ba8a00 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -1001,6 +1001,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); }); @@ -1026,7 +1030,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 daf08894251..ebaf62a9a61 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -341,12 +341,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"); @@ -388,7 +388,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 } @@ -396,7 +396,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 5e61f704ded..2730fbd45f9 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -22,7 +22,7 @@ void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { // Full single magic = 48 // Full double magic = 96 - int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * 48; + int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; @@ -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 9a3988e39a5..15963bac661 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -78,8 +78,8 @@ void RegisterInfiniteHealth() { GameInteractor::Instance->RegisterGameHook([]() { if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger(CVAR_CHEAT("InfiniteHealth"), 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 456e07eaa0c..c9face81c3b 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2363,6 +2363,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 d0001534a55..bb76b143d55 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -161,6 +161,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 b6fd0cb07c2..4bb69172488 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -7,6 +7,7 @@ #include "functions.h" #include "macros.h" #include +#include "leveled_stat_math.h" #include "soh/Enhancements/boss-rush/BossRush.h" #include #include "SohGui.hpp" @@ -447,6 +448,12 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].gregFound = Flags_GetRandomizerInf(RAND_INF_GREG_FOUND); 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]; @@ -488,9 +495,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; @@ -644,6 +655,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; @@ -1172,6 +1184,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); @@ -1317,6 +1330,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); @@ -1533,6 +1547,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); @@ -1754,6 +1769,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); @@ -1935,6 +1951,7 @@ void SaveManager::LoadBaseVersion4() { } void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSave) { + SaveManager::Instance->SaveData("experience", saveContext->experience); SaveManager::Instance->SaveData("entranceIndex", saveContext->entranceIndex); SaveManager::Instance->SaveData("linkAge", saveContext->linkAge); SaveManager::Instance->SaveData("cutsceneIndex", saveContext->cutsceneIndex); diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 817fc6fbfb2..970d7bb0542 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -29,6 +29,7 @@ typedef struct { s16 gsTokens; u8 isDoubleDefenseAcquired; u8 gregFound; + u8 level; } SaveFileMetaInfo; #ifdef __cplusplus diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 7286fe0b964..b5922d9d123 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -2029,6 +2029,69 @@ 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); + PaddedEnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + ImGui::EndMenu(); + } + + PaddedEnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + 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."); + PaddedEnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", true, true, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + + if (ImGui::BeginMenu("Heart Container Value in Units")) { + CVarGetInteger("gLeveledHeartUnits", 4); + UIWidgets::EnhancementRadioButton("4 (x0.25)", "gLeveledHeartUnits", 1); + UIWidgets::EnhancementRadioButton("8 (x0.5)", "gLeveledHeartUnits", 2); + UIWidgets::EnhancementRadioButton("12 (x0.75)", "gLeveledHeartUnits", 3); + UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveledHeartUnits", 4); + UIWidgets::EnhancementRadioButton("20 (x1.25)", "gLeveledHeartUnits", 5); + UIWidgets::EnhancementRadioButton("24 (x1.5)", "gLeveledHeartUnits", 6); + UIWidgets::EnhancementRadioButton("28 (x1.75)", "gLeveledHeartUnits", 7); + UIWidgets::EnhancementRadioButton("32 (x2)", "gLeveledHeartUnits", 8); + UIWidgets::EnhancementRadioButton("48 (x3)", "gLeveledHeartUnits", 12); + UIWidgets::EnhancementRadioButton("64 (x4)", "gLeveledHeartUnits", 16); + UIWidgets::EnhancementRadioButton("80 (x5)", "gLeveledHeartUnits", 20); + UIWidgets::EnhancementRadioButton("96 (x6)", "gLeveledHeartUnits", 24); + UIWidgets::EnhancementRadioButton("112 (x7)", "gLeveledHeartUnits", 28); + UIWidgets::EnhancementRadioButton("128 (x8)", "gLeveledHeartUnits", 32); + ImGui::EndMenu(); + } + 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; + // } + + ImGui::EndMenu(); + } +} + void SohMenuBar::InitElement() { UpdateWindowBackendObjects(); } @@ -2069,6 +2132,10 @@ void SohMenuBar::DrawElement() { DrawRandomizerMenu(); + ImGui::SetCursorPosY(0.0f); + + DrawLeveledMenu(); + ImGui::PopStyleVar(1); 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..4179ad1da2e --- /dev/null +++ b/soh/src/code/leveled_actor_level_table.c @@ -0,0 +1,970 @@ +#include "global.h" + +typedef struct { + s8 levelModifier; + u16 experienceRate; + u8 ignoreEntry; +} SceneLevelEntry; + +///////////////////////////////////////////////// +////////////////// BOSSES ///////////////////// +///////////////////////////////////////////////// +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, 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, 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, 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, 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, SceneLevelEntry* levelEntry) { + actor->level = 40; + actor->exp = 3500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 6600; +} + +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, 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, SceneLevelEntry* levelEntry) { + actor->level = 56; + actor->exp = 9999; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 40000; +} + +void Leveled_Ganon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 60; + levelEntry->levelModifier = 8; +} + +///////////////////////////////////////////////// +/////////////// MINI-BOSSES /////////////////// +///////////////////////////////////////////////// +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, 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, 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, SceneLevelEntry* levelEntry) { + actor->level = 44; + levelEntry->levelModifier = 4; + + if (actor->id == ACTOR_EN_DH) { + actor->exp = 2465; + levelEntry->experienceRate = 3666; + } +} + +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_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 55; + actor->exp = 2850; + } +} +///////////////////////////////////////////////// +////////////// NORMAL ENEMIES ///////////////// +///////////////////////////////////////////////// +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; + } + } else { + actor->level = 2; + actor->exp = 3; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 60; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 23; + actor->exp = 19; + } + } +} + +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, SceneLevelEntry* levelEntry) { + actor->level = 3; + actor->exp = 3; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 80; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 21; + actor->exp = 19; + } + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 30; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 44; + actor->exp = 34; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 50; + } + 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, 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_FOREST_TEMPLE) { + actor->level = 24; + actor->exp = 18; + } + } +} + +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, 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; + } + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 36; + actor->exp = 44; + } + } else { + actor->level = 9; + actor->exp = 9; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + if (LINK_IS_ADULT) { + actor->level = 27; + actor->exp = 24; + } + } +} + +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, 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, SceneLevelEntry* levelEntry) { + actor->level = 12; + actor->exp = 17; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 60; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { // Ganon's Castle + actor->level = 50; + actor->exp = 70; + } +} + +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, 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_FIRE_TEMPLE) { + actor->level = 28; + actor->exp = 22; + } else if (play->sceneNum == SCENE_ICE_CAVERN) { + actor->level = 31; + actor->exp = 26; + } else if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 34; + actor->exp = 29; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 42; + actor->exp = 36; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 33; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 38; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 80; + } + } +} + +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, 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, SceneLevelEntry* levelEntry) { + actor->level = 24; + actor->exp = 25; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; +} + +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_BOTTOM_OF_THE_WELL || play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 171; + } else if (play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 230; + } + } else { // Gibdo + actor->level = 43; + actor->exp = 183; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 305; + } +} + +void Leveled_Shabom(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 15; + levelEntry->levelModifier = -1; +} + +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, SceneLevelEntry* levelEntry) { + actor->level = 16; + actor->exp = 17; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 100; +} + +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_WATER_TEMPLE) { + actor->level = 37; + actor->exp = 85; + } +} + +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, SceneLevelEntry* levelEntry) { + actor->level = 19; + actor->exp = 22; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 125; +} + +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, 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_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 80; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 87; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 90; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 300; + } + } +} + +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; + } + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 26; + actor->exp = 43; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 158; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 165; + } + } else { // White + actor->level = 37; + actor->exp = 126; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 180; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 420; + } +} + +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_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 570; + } +} + +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, 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_SHADOW_TEMPLE) { + actor->level = 40; + actor->exp = 75; + } + } 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_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 70; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 77; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 85; + } + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 220; + } +} + +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_FOREST_TEMPLE) { + actor->ignoreExpReward = true; + actor->exp = 250; + levelEntry->experienceRate = 800; + }else if (play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 214; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 236; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER || + play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 260; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 700; + } +} + +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_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 140; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 160; + } +} + +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_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 129; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 138; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 150; + } +} + +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, SceneLevelEntry* levelEntry) { + actor->level = 32; + actor->exp = 99; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 245; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 38; + actor->exp = 142; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 161; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 182; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 190; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 375; + } +} + +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_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 163; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 170; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 350; + } +} +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_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 160; + } +} + +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_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 390; + } +} + +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_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 325; + } +} + +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, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 15; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 20; +} + +void Leveled_Anubis(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 48; + levelEntry->levelModifier = 3; + if (actor->id == ACTOR_EN_ANUBICE) { + actor->exp = 120; + levelEntry->experienceRate = 155; + } +} + +void Leveled_Arwing(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 50; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 215; +} + + +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; + } + + switch (actorId) { + case ACTOR_BOSS_DODONGO: + case ACTOR_EN_DODONGO: + Leveled_Dodongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GOMA: + case ACTOR_EN_GOMA: + Leveled_Gohma(play, actor, &levelEntry); + break; + case ACTOR_BOSS_VA: + Leveled_Barinade(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANONDROF: + Leveled_PhantomGanon(play, actor, &levelEntry); + break; + case ACTOR_BOSS_FD: + case ACTOR_BOSS_FD2: + Leveled_Volvagia(play, actor, &levelEntry); + break; + case ACTOR_BOSS_MO: + Leveled_Morpha(play, actor, &levelEntry); + break; + case ACTOR_BOSS_SST: + Leveled_BongoBongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_TW: + Leveled_Twinrova(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON: + Leveled_Ganondorf(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON2: + Leveled_Ganon(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUBABA: + Leveled_Dekubaba(play, actor, &levelEntry); + break; + case ACTOR_EN_KAREBABA: + Leveled_StickDekuBaba(play, actor, &levelEntry); + break; + case ACTOR_EN_ST: + Leveled_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUNUTS: + Leveled_DekuScrub(play, actor, &levelEntry); + break; + case ACTOR_EN_TITE: + Leveled_Tektite(play, actor, &levelEntry); + break; + case ACTOR_EN_CROW: + Leveled_Guay(play, actor, &levelEntry); + break; + case ACTOR_EN_OKUTA: + Leveled_Octorock(play, actor, &levelEntry); + break; + case ACTOR_EN_AM: + Leveled_Armos(play, actor, &levelEntry); + break; + case ACTOR_EN_DODOJR: + Leveled_BabyDodongo(play, actor, &levelEntry); + break; + case ACTOR_EN_FIREFLY: + Leveled_Keese(play, actor, &levelEntry); + break; + case ACTOR_EN_PEEHAT: + Leveled_Peahat(play, actor, &levelEntry); + break; + case ACTOR_EN_POH: + Leveled_Poh(play, actor, &levelEntry); + case ACTOR_EN_PO_FIELD: + Leveled_Poh_Field(play, actor, &levelEntry); + break; + case ACTOR_EN_RD: + Leveled_ReDead(play, actor, &levelEntry); + break; + case ACTOR_EN_SKB: + Leveled_Stalchild(play, actor, &levelEntry); + break; + case ACTOR_EN_SW: + Leveled_Wall_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_VM: + Leveled_Beamos(play, actor, &levelEntry); + break; + case ACTOR_EN_WF: + Leveled_Wolfos(play, actor, &levelEntry); + break; + case ACTOR_EN_ZF: + Leveled_Lizalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_BUBBLE: + Leveled_Shabom(play, actor, &levelEntry); + break; + case ACTOR_EN_BILI: + Leveled_Biri(play, actor, &levelEntry); + break; + case ACTOR_EN_VALI: + Leveled_Bari(play, actor, &levelEntry); + break; + case ACTOR_EN_TP: + Leveled_Tailpasaran(play, actor, &levelEntry); + break; + case ACTOR_EN_BA: + case ACTOR_EN_BX: + Leveled_JabuTentacle(play, actor, &levelEntry); + break; + case ACTOR_EN_EIYER: + case ACTOR_EN_WEIYER: + Leveled_Stinger(play, actor, &levelEntry); + break; + case ACTOR_EN_BIGOKUTA: + Leveled_BigOctorock(play, actor, &levelEntry); + break; + case ACTOR_EN_MB: + Leveled_Moblins(play, actor, &levelEntry); + break; + case ACTOR_EN_BB: + Leveled_Bubble(play, actor, &levelEntry); + break; + case ACTOR_EN_TEST: + Leveled_Stalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_FLOORMAS: + Leveled_Floormaster(play, actor, &levelEntry); + break; + case ACTOR_EN_WALLMAS: + Leveled_Wallmaster(play, actor, &levelEntry); + break; + case ACTOR_EN_PO_SISTERS: + Leveled_Poh_Sisters(play, actor, &levelEntry); + break; + case ACTOR_EN_RR: + Leveled_LikeLike(play, actor, &levelEntry); + break; + case ACTOR_EN_BW: + Leveled_TorchSlug(play, actor, &levelEntry); + break; + case ACTOR_EN_FZ: + Leveled_Freezer(play, actor, &levelEntry); + break; + case ACTOR_EN_SB: + Leveled_ShellBlade(play, actor, &levelEntry); + break; + case ACTOR_EN_NY: + Leveled_RollingSpike(play, actor, &levelEntry); + break; + case ACTOR_EN_TORCH2: + Leveled_DarkLink(play, actor, &levelEntry); + break; + case ACTOR_EN_DH: + case ACTOR_EN_DHA: + Leveled_DeadHand(play, actor, &levelEntry); + break; + case ACTOR_EN_FD: + case ACTOR_EN_FW: + case ACTOR_EN_FD_FIRE: + Leveled_FlareDancer(play, actor, &levelEntry); + break; + case ACTOR_EN_IK: + Leveled_IronKnuckle(play, actor, &levelEntry); + break; + case ACTOR_EN_GELDB: + Leveled_GerudoFighter(play, actor, &levelEntry); + break; + case ACTOR_EN_REEBA: + Leveled_Leever(play, actor, &levelEntry); + break; + case ACTOR_EN_ANUBICE: + case ACTOR_EN_ANUBICE_FIRE: + Leveled_Anubis(play, actor, &levelEntry); + break; + case ACTOR_EN_CLEAR_TAG: + Leveled_Arwing(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 = 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_map_levels.c b/soh/src/code/leveled_map_levels.c new file mode 100644 index 00000000000..de5c1d4ca20 --- /dev/null +++ b/soh/src/code/leveled_map_levels.c @@ -0,0 +1,178 @@ +#include "global.h" + +s8 Leveled_GetSceneLevel(s16 sceneId) { + switch (sceneId) { + case SCENE_DEKU_TREE: + case SCENE_DEKU_TREE_BOSS: + return 2; // Deku Tree + + case SCENE_DODONGOS_CAVERN: + case SCENE_DODONGOS_CAVERN_BOSS: + return 10; // Dodongo's + + case SCENE_JABU_JABU: + case SCENE_JABU_JABU_BOSS: + return 16; // Jabu Jabu + + case SCENE_FOREST_TEMPLE: + case SCENE_FOREST_TEMPLE_BOSS: + return 24; // Forest Temple + + case SCENE_FIRE_TEMPLE: + case SCENE_FIRE_TEMPLE_BOSS: + return 30; // Fire Temple + + case SCENE_WATER_TEMPLE: + case SCENE_WATER_TEMPLE_BOSS: + return 35; // Water Temple + + case SCENE_SPIRIT_TEMPLE: + case SCENE_SPIRIT_TEMPLE_BOSS: + return 45; // Spirit Temple + + case SCENE_SHADOW_TEMPLE: + case SCENE_SHADOW_TEMPLE_BOSS: + return 40; // Shadow Temple + + case SCENE_BOTTOM_OF_THE_WELL: + return 38; // Bottom of the Well + + case SCENE_ICE_CAVERN: + return 32; // Ice Cavern + + case SCENE_GANONS_TOWER: + case SCENE_GANONDORF_BOSS: + case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: + case SCENE_GANON_BOSS: + return 50; + + case SCENE_GERUDO_TRAINING_GROUND: + return 43; + + case SCENE_THIEVES_HIDEOUT: + return 42; + + case SCENE_INSIDE_GANONS_CASTLE: + return 52; + + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: + return 53; + + case SCENE_MARKET_RUINS: + return 23; + + case SCENE_REDEAD_GRAVE: + return 10; + + case SCENE_ROYAL_FAMILYS_TOMB: + return 8; + + case SCENE_WINDMILL_AND_DAMPES_GRAVE: + return 22; + + case SCENE_HOUSE_OF_SKULLTULA: + return 99; + + case SCENE_HYRULE_FIELD: + if (LINK_IS_CHILD) + return 5; + else + return 20; + + case SCENE_GRAVEYARD: + if (LINK_IS_CHILD) + return 7; + else + return 20; + + case SCENE_ZORAS_RIVER: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_KOKIRI_FOREST: + if (LINK_IS_CHILD) + return 2; + else + return 21; + + case SCENE_SACRED_FOREST_MEADOW: + if (LINK_IS_CHILD) + return 9; + else + return 23; + + case SCENE_LAKE_HYLIA: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_ZORAS_FOUNTAIN: + if (LINK_IS_CHILD) + return 13; + else + return 30; + + case SCENE_GERUDO_VALLEY: + if (LINK_IS_CHILD) + return 10; + else + return 28; + + case SCENE_LOST_WOODS: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_DESERT_COLOSSUS: + return 42; + + case SCENE_GERUDOS_FORTRESS: + return 40; + + case SCENE_DEATH_MOUNTAIN_TRAIL: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_DEATH_MOUNTAIN_CRATER: + return 11; + + case SCENE_LON_LON_RANCH: + return 6; + + case SCENE_OUTSIDE_GANONS_CASTLE: + return 25; + + + // 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 new file mode 100644 index 00000000000..9993d0ef54c --- /dev/null +++ b/soh/src/code/leveled_overlays.c @@ -0,0 +1,765 @@ +#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, (s32)((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, textureWidth, textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + 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, textureWidth, textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + 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, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + 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, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + 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) { + + for (u8 j = 0; j < 3; j++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 5, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar56LatinCapitalLetterVTex, 8, 16, (s16)spBC.x + 10, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 15, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x + 20, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar55LatinCapitalLetterUTex, 8, 16, (s16)spBC.x + 30, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar50LatinCapitalLetterPTex, 8, 16, (s16)spBC.x + 35, (s16)spBC.y, 8, 16, 2048, 2048); + } + } + + if (i == 3) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconSilverGauntletsTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 4) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconShieldHylianTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + 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, dgHeartFullTex, 16, 16, (s16)spBC.x, (s16)spBC.y, 16, 16, 2048, 2048); + } + + if (i == 6) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgQuestIconMagicJarBigTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + + 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 statX = 92; + u16 statY = 76; + u8 attack = 1; + Color_RGBA8 textColor = { 255, 255, 255, 255 }; + + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 1) + attack = HEALTH_ATTACK_MULTIPLIER; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 2) + attack = HEALTH_ATTACK_MULTIPLIER << 1; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 3) { + attack = HEALTH_ATTACK_MULTIPLIER << 2; + if (gBitFlags[3] & gSaveContext.inventory.equipment) + attack = HEALTH_ATTACK_MULTIPLIER; + } + + // Values and Icons + // Level + Leveled_DrawTexI8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); + + Leveled_BigValueNumberDraw(play, statX + 10, statY - 6, player->actor.level, 255, 255, 255, 255); + statY += 10; + // Health + Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 29, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 33, statY, gSaveContext.healthCapacity2, 120, 255, 0); + statY += 8; + // Magic + if (gSaveContext.magicCapacity > 0) { + Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 29, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 33, statY, gSaveContext.magicCapacity, 120, 255, 0); + statY += 8; + } + // Attack + Leveled_DrawTex32(play, dgItemIconSwordKokiriTex, 32, 32, statX, statY - 1, 22, 20); + Leveled_ValueNumberDraw(play, statX + 10, statY, GetActorStat_Attack(attack, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255)), 255, 255, 255); + statY += 8; + // Power + if (player->actor.powerModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.powerModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } + Leveled_DrawTex32(play, dgItemIconSilverGauntletsTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statY += 8; + // Courage + if (player->actor.courageModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.courageModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } + Leveled_DrawTex32(play, dgItemIconShieldHylianTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.courage + player->actor.courageModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statX = 90; + statY += 60; + if (gSaveContext.magicCapacity > 0) { + statY -= 8; + } + // EXP + Leveled_DrawTex32(play, dgItemIconGoronsBraceletTex, 32, 32, statX + 4, statY, 13, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.experience, 255, 255, 255); + statY += 8; + // Next LV + Leveled_DrawTex4b(play, dgNextDoActionENGTex, 48, 16, statX - 4, statY, 68, 15); + Leveled_ValueNumberDraw(play, statX + 22, 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 || !CVarGetInteger("gLeveledHUDExperienceNextLevel", 1)) + 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, dgNextDoActionENGTex, 48, 16, -192, -268, 48, 16, 255 - play->pauseCtx.alpha); // Load texture + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 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 < 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); + 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..ff54bfb0725 --- /dev/null +++ b/soh/src/code/leveled_stat_math.c @@ -0,0 +1,261 @@ +#include "global.h" + +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, 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); +} + +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; +} + +u16 GetEnemyExperienceReward(u8 level, u16 expRate) { + if (expRate == 0) + return 0; + + 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) { + 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_TYPE_SWORD)){ + case PLAYER_SWORD_MASTER: + courageModifier += 1; + break; + + case PLAYER_SWORD_BIGGORON: + if (gBitFlags[3] & gSaveContext.inventory.equipment){ + powerModifier -= 7; + courageModifier -= 12; + } else { + powerModifier += 2; + courageModifier -= 8; + } + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_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_TYPE_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_TYPE_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 289982318e0..1c9284d5e0d 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1215,12 +1215,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; @@ -1242,6 +1265,14 @@ void Actor_Init(Actor* actor, PlayState* play) { if (actor->category == ACTORCAT_ENEMY) { actor->maximumHealth = actor->colChkInfo.health; } + + 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)); } } @@ -2179,7 +2210,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; @@ -3585,6 +3621,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); } @@ -4693,12 +4732,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 5376ae41cef..ff40cdc191d 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -3043,13 +3043,26 @@ void CollisionCheck_ApplyDamage(PlayState* play, CollisionCheckContext* colChkCt damage = tbl->table[i] & 0xF; collider->actor->colChkInfo.damageEffect = tbl->table[i] >> 4 & 0xF; } - if (!(collider->acFlags & AC_HARD)) { - collider->actor->colChkInfo.damage += damage; - } if (CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0)) { collider->actor->colChkInfo.damage *= GET_PLAYER(play)->ivanDamageMultiplier; } + + if (!(collider->acFlags & AC_HARD)) { + Actor* attacker = collider->ac; + + if (collider->actor->category != ACTORCAT_PLAYER) { + damage *= HEALTH_ATTACK_MULTIPLIER; + } else { + damage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); + } + + 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 77281771605..cf357d89d21 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1553,7 +1553,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 3cb9d471591..9b6b6dc7824 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--; } @@ -626,7 +631,7 @@ void HealthMeter_Draw(PlayState* play) { } offsetX += 10.0f; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (lineLength != 0 && (i+1)%lineLength == 0) { offsetX = PosX_anchor; offsetY += 10.0f; @@ -663,15 +668,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 0495df1dd10..f2a9a615096 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -1666,6 +1666,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; @@ -3329,7 +3334,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 || @@ -3366,13 +3371,20 @@ 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; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.health -= heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 63892de7027..96fed4bf2d6 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2356,19 +2356,24 @@ 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; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.health -= heartUnits; + } + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } gSaveContext.sohStats.heartContainers++; 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) { @@ -2504,7 +2509,7 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { slot = SLOT(item); if (item == RG_MAGIC_SINGLE) { gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Magic_Fill(play); return Return_Item_Entry(giEntry, RG_NONE); } else if (item == RG_MAGIC_DOUBLE) { @@ -2512,7 +2517,7 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Magic_Fill(play); return Return_Item_Entry(giEntry, RG_NONE); @@ -2529,7 +2534,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); } @@ -3182,13 +3187,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) { @@ -3323,7 +3332,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) * MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; gSaveContext.magicState = MAGIC_STATE_FILL; } } @@ -3462,7 +3471,7 @@ void Interface_UpdateMagicBar(PlayState* play) { switch (gSaveContext.magicState) { case MAGIC_STATE_STEP_CAPACITY: - temp = gSaveContext.magicLevel * MAGIC_NORMAL_METER; + temp = gSaveContext.magicLevel * gSaveContext.magicUnits; if (gSaveContext.magicCapacity != temp) { if (gSaveContext.magicCapacity < temp) { gSaveContext.magicCapacity += 8; @@ -3490,6 +3499,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; @@ -3712,7 +3724,7 @@ void Interface_DrawMagicBar(PlayState* play) { s16 rMagicBarX; s16 PosX_MidEnd; s16 rMagicFillX; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) != 0) { magicBarY = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0)+Y_Margins; if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == 1) {//Anchor Left @@ -3739,8 +3751,9 @@ void Interface_DrawMagicBar(PlayState* play) { rMagicFillX = -9999; } else if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == 5) {//Anchor To life meter magicBarY = R_MAGIC_BAR_SMALL_Y-2 + - magicDrop*(lineLength == 0 ? 0 : (gSaveContext.healthCapacity-1)/(0x10*lineLength)) + - CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + getHealthMeterYOffset(); + magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity2 - 1) / + ((CVarGetInteger("gLeveledHeartUnits", 4) << 2) * lineLength)) + + CVarGetInteger("gMagicBarPosY", 0) + getHealthMeterYOffset(); s16 xPushover = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 0) + getHealthMeterXOffset() + R_MAGIC_BAR_X-1; PosX_Start = xPushover; rMagicBarX = xPushover; @@ -3748,9 +3761,11 @@ void Interface_DrawMagicBar(PlayState* play) { rMagicFillX = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 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; } @@ -5213,7 +5228,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); } @@ -5443,6 +5458,50 @@ void Interface_Draw(PlayState* play) { gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } + // 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)); + + // Render enemy health bar after Z-target to leverage set variables if (CVarGetInteger(CVAR_ENHANCEMENT("EnemyHealthBar"), 0)) { Interface_DrawEnemyHealthBar(&play->actorCtx.targetCtx, play); @@ -5541,6 +5600,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); @@ -6591,17 +6652,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 >> 2; + gSaveContext.health += heartUnits >> 2; - if ((gSaveContext.health & 0xF) < 4) { + if ((gSaveContext.health % (heartUnits)) < heartUnits >> 2) { 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 b5128740c79..9760d134ac6 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -721,6 +721,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); } } @@ -802,6 +803,52 @@ 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; + 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 af92afe9120..21153c79b17 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -141,8 +141,8 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 0x30) { - gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + if (gSaveContext.health < 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2) { + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 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 e8b2be4df50..cb4148cfeb9 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(IS_RANDO) { - 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 = MAGIC_NORMAL_METER; + 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 = MAGIC_DOUBLE_METER; + 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 8427a969494..bd4bfab0ea9 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 @@ -323,7 +323,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; @@ -331,6 +332,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { this->unk_228 = 9200.0f; 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 // SOH [General] @@ -725,7 +728,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) { @@ -1470,7 +1475,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; @@ -1504,12 +1509,14 @@ void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { item1 = this->collider.elements[0].info.acHitInfo; 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 0d33533ae1d..dc015e848b8 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 <<= 1; + 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->bodyIsBurning == false) { for (i2 = 0; i2 < ARRAY_COUNT(player->bodyFlameTimers); i2++) { player->bodyFlameTimers[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 fb14c5ebe47..d1558a6f150 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; @@ -315,20 +315,20 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { void BossFd2_SetupIdle(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; - s8 health; + u16 health; s16 idleTime; osSyncPrintf("UP INIT 1\n"); 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 65420de320b..40225258a7d 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 (!(IS_BOSS_RUSH && 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 & PLAYER_STATE1_HANGING_OFF_LEDGE)) { 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,8 +2794,21 @@ 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++) { @@ -2805,7 +2818,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanon_SetupDeathCutscene(this, play); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER); 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 4de5ab06843..ab5242c8de6 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 @@ -1460,7 +1460,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; } @@ -1930,10 +1930,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))) { @@ -1960,7 +1960,7 @@ void func_80902524(BossGanon2* this, PlayState* play) { Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); this->actor.colChkInfo.health -= 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) { @@ -1992,11 +1992,19 @@ 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 111380f0b11..50601075e84 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 4d616599417..c713ffd490f 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 74684df71fa..bad0bd02be3 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 d7be78fabf2..fdf87a82fb1 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) { Player_PlaySfx(&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 9d0a68c3822..483449680d1 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,11 @@ 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; } @@ -5166,7 +5169,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) { @@ -5267,7 +5270,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, &gTwinrovaChargedAttackHitAnim, -15.0f); this->timers[0] = 150; @@ -5281,11 +5284,13 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&gTwinrovaDamageAnim); 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 4891b03305f..d8a7e35c430 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 04b09aca8b4..c7d43dcbf71 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 @@ -408,6 +408,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); } @@ -453,7 +454,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); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); 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 39c0e54e952..2a7b2cc2309 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 @@ -530,7 +530,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); + } } } @@ -1184,7 +1186,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 { @@ -1201,13 +1203,13 @@ 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))) { + } else if ((this->actor.params == ENBB_WHITE) && ((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 ab0b82d6567..5700988858c 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->bodyFlameTimers[i] = Rand_S16Offset(0, 200); } player->bodyIsBurning = 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 2f1c4abe4bd..20c44703e30 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 @@ -175,6 +175,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 a50dabe0107..d7b327c9dc8 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 @@ -563,6 +563,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) { @@ -578,7 +582,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } EnBili_SetupDischargeLightning(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } } else if (damageEffect == BIRI_DMGEFF_FIRE) { EnBili_SetupBurnt(this); @@ -588,7 +595,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } else if (damageEffect == BIRI_DMGEFF_SLINGSHOT) { EnBili_SetupRecoil(this); } else { - 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 5c18c52fa79..b5ed32934ee 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 89642a9fd9b..2fe4075d932 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 @@ -116,7 +116,7 @@ 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 501c7ec9e6c..07640a59d13 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 @@ -674,6 +674,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); } } @@ -717,6 +718,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_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index f0f6b5bb523..58f3b3430db 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -371,10 +371,17 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) { this->acceleration.z = Rand_CenteredFloat(15.0f); Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); - this->actor.colChkInfo.health--; - if ((s8)this->actor.colChkInfo.health <= 0) { + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 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 (this->actor.colChkInfo.health <= 0) { this->state = CLEAR_TAG_STATE_CRASHING; this->actor.velocity.y = 0.0f; + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); goto state_crashing; } 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 2b4344f68ce..d454f4fb37d 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 @@ -1046,6 +1046,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 79ed4433e85..322efc8f648 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 @@ -406,7 +406,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 d63982e146b..6ccd447c4b0 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 @@ -491,9 +491,10 @@ void EnDh_CollisionCheck(EnDh* this, PlayState* play) { EnDh_SetupDeath(this); 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 359477614b7..d1e34209dca 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 @@ -665,6 +665,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 3d0566a7fef..88f040b8129 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(CVAR_ENHANCEMENT("FairyPercentRestore"), 0)) { - Health_ChangeBy(play, (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 100) / 100 + 15) / 16 * 16); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + Health_ChangeBy(play, heartUnits + ((gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 100) / 100)); } else { - Health_ChangeBy(play, CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 8) * 16); + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + Health_ChangeBy(play, CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 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 277ee851a93..9c09e9db58f 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 @@ -221,10 +221,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)) { @@ -779,7 +780,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 7797cba9ce5..6bc6662de0f 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 @@ -630,20 +630,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) { @@ -654,17 +652,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 1a906e5b7e9..acf9d1ac880 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 @@ -738,7 +738,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_SetupSmWait(this); + 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); + } } this->actor.scale.z = this->actor.scale.x; this->actor.scale.y = this->actor.scale.x; @@ -822,7 +826,8 @@ void EnFloormas_GrabLink(EnFloormas* this, PlayState* play) { } else { Player_PlaySfx(&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 61f009e4781..2e2d7879be5 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 60afdd2939f..2e4ef9a9db2 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 @@ -364,6 +364,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); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } } @@ -372,6 +373,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); @@ -718,7 +720,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 0382735a3cd..0afcb3b3d3d 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 @@ -639,7 +639,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 efaaed84c43..e8e9a25a8f4 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 @@ -616,7 +616,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); @@ -655,7 +655,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 87ed6aeae61..95dc4ba1600 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 @@ -680,7 +680,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; @@ -715,19 +715,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); } @@ -743,7 +744,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); @@ -765,7 +766,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 b0aff37509b..773400d7105 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 @@ -624,6 +624,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); } else if (this->state == ENMB_STATE_CLUB_KNEELING) { /* dead code: the setup for this action sets state to something else */ EnMb_SetupClubDamagedWhileKneeling(this); @@ -633,6 +634,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); } else { EnMb_SetupSpearDamaged(this); } @@ -1443,6 +1445,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else if (this->state != ENMB_STATE_CLUB_KNEELING) { EnMb_SetupClubDamaged(this); @@ -1450,6 +1453,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } 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 5526ad49c13..62c622e010e 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 @@ -872,6 +872,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 e69cfda78b7..12e323c6a22 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 @@ -497,7 +497,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: @@ -531,7 +532,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; Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); @@ -724,6 +726,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); @@ -795,6 +798,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 f999f34866b..a3ace2417a2 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 @@ -333,6 +333,7 @@ void EnRr_SetupReleasePlayer(EnRr* this, PlayState* play) { EnRr_SetupDamage(this); } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } @@ -461,6 +462,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { } else { this->dropType = dropType; EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } return; case RR_DMG_FIRE: // Fire Arrow and Din's Fire @@ -754,6 +756,7 @@ void EnRr_Stunned(EnRr* this, PlayState* play) { this->actionFunc = EnRr_Approach; } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } } 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 98e8f411633..2859599e1e7 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 @@ -185,6 +185,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); GameInteractor_ExecuteOnFlagSet(FLAG_GS_TOKEN, this->actor.params); Actor_Kill(&this->actor); if (gSaveContext.pendingIceTrapCount > 0 && player->heldItemId == 11) { 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 e985fe862ec..f096ca952c0 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 @@ -417,6 +417,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); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } 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 59d4be6f36c..90ffac582a2 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 @@ -398,7 +398,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 1f4be6670b5..7de21087303 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 @@ -316,6 +316,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); @@ -1509,7 +1511,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 ba583c155ce..6a7a9a96cdf 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 @@ -725,6 +725,7 @@ void EnTite_Stunned(EnTite* this, PlayState* play) { this->actor.world.rot.y = this->actor.shape.rot.y; if (this->actor.colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else if (this->flipState == TEKTITE_FLIPPED) { EnTite_SetupFlipUpright(this); } else if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) && @@ -866,6 +867,7 @@ void EnTite_CheckDamage(Actor* thisx, PlayState* play) { } if (thisx->colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else { // Flip tektite back up if it's on its back Audio_PlayActorSound2(thisx, NA_SE_EN_TEKU_DAMAGE); 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 30ff3c37f13..edff48f97aa 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 @@ -600,6 +600,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 ba0a7561341..e466bbafac4 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 @@ -387,6 +387,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); } @@ -396,7 +397,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 03fb95bd3c3..32b14b0f18e 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 @@ -1944,6 +1944,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 d07f2de50c1..aedec3d2268 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 1ef4fa593e2..9d94ad50d9b 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4036,12 +4036,12 @@ s32 func_80837B18_modified(PlayState* play, Player* this, s32 damage, u8 modifie return 1; } - s32 modifiedDamage = damage; + /* s32 modifiedDamage = damage; if (modified) { modifiedDamage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); - } + }*/ - return Health_ChangeBy(play, modifiedDamage); + return Health_ChangeBy(play, damage); } s32 func_80837B18(PlayState* play, Player* this, s32 damage) { @@ -8909,13 +8909,14 @@ void func_80843AE8(PlayState* play, Player* this) { } if (CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveEffect"), 0)) { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyRevivePercentRestore"), 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveHealth"), 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits + ((gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveHealth"), 100) / 100); } else { - gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveHealth"), 20) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveHealth"), 20) * heartUnits; } } else { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } this->av2.actionVar2 = -1; } @@ -10125,6 +10126,7 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe this->meleeWeaponEffectIndex = TOTAL_EFFECT_COUNT; this->yaw = this->actor.world.rot.y; func_80834644(play, this); + Player_GainExperience(play, 0); SkelAnime_InitLink(play, &this->skelAnime, skelHeader, GET_PLAYER_ANIM(PLAYER_ANIMGROUP_wait, this->modelAnimType), 9, this->jointTable, this->morphTable, PLAYER_LIMB_MAX); @@ -13816,37 +13818,40 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { if (this->av2.actionVar2 == 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(CVAR_ENHANCEMENT("RedPotionEffect"), 0) && this->itemAction == PLAYER_IA_BOTTLE_POTION_RED) { if (CVarGetInteger(CVAR_ENHANCEMENT("RedPercentRestore"), 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("RedPotionHealth"), 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("RedPotionHealth"), 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("RedPotionHealth"), 20) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("RedPotionHealth"), 20) * heartUnits; } } else if (CVarGetInteger(CVAR_ENHANCEMENT("BluePotionEffects"), 0) && this->itemAction == PLAYER_IA_BOTTLE_POTION_BLUE) { if (CVarGetInteger(CVAR_ENHANCEMENT("BlueHealthPercentRestore"), 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("BluePotionHealth"), 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("BluePotionHealth"), 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("BluePotionHealth"), 20) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("BluePotionHealth"), 20) * heartUnits; } if (CVarGetInteger(CVAR_ENHANCEMENT("BlueManaPercentRestore"), 0)) { @@ -13855,7 +13860,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } Magic_RequestChange(play, - (gSaveContext.magicLevel * 48 * CVarGetInteger(CVAR_ENHANCEMENT("BluePotionMana"), 100) / 100 + 15) / + (gSaveContext.magicLevel * gSaveContext.magicUnits * CVarGetInteger(CVAR_ENHANCEMENT("BluePotionMana"), 100) / 100 + 15) / 16 * 16, MAGIC_ADD); } else { @@ -13874,7 +13879,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } Magic_RequestChange(play, - (gSaveContext.magicLevel * 48 * CVarGetInteger(CVAR_ENHANCEMENT("GreenPotionMana"), 100) / 100 + 15) / + (gSaveContext.magicLevel * gSaveContext.magicUnits * CVarGetInteger(CVAR_ENHANCEMENT("GreenPotionMana"), 100) / 100 + 15) / 16 * 16, MAGIC_ADD); } else { @@ -13888,24 +13893,25 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } else if (CVarGetInteger(CVAR_ENHANCEMENT("MilkEffect"), 0) && (this->itemAction == PLAYER_IA_BOTTLE_MILK_FULL || this->itemAction == PLAYER_IA_BOTTLE_MILK_HALF)) { if (CVarGetInteger(CVAR_ENHANCEMENT("MilkPercentRestore"), 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("MilkHealth"), 100) / 100 + 15) / 16 * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("MilkHealth"), 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("MilkHealth"), 5) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("MilkHealth"), 5) * heartUnits; } if (CVarGetInteger(CVAR_ENHANCEMENT("SeparateHalfMilkEffect"), 0) && this->itemAction == PLAYER_IA_BOTTLE_MILK_HALF) { if (CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkPercentRestore"), 0)) { - gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkHealth"), 100) / 100 + 15) / 16 * - 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkHealth"), 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkHealth"), 5) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkHealth"), 5) * heartUnits; } } } else { if (sp28 & 1) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } if (sp28 & 2) { @@ -13913,7 +13919,8 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } if (sp28 & 4) { - gSaveContext.healthAccumulator = 0x50; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits * 5; } } } @@ -14032,13 +14039,15 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyEffect"), 0)) { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyPercentRestore"), 0)) { + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; gSaveContext.healthAccumulator = - (gSaveContext.healthCapacity * CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 100) / 100 + 15) / 16 * 16; + (gSaveContext.healthCapacity2) * CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 100) / 100; } else { - gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 8) * 16; + s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 8) * heartUnits; } } else { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } } } 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 bb22c90ae00..5cda5c705d7 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 @@ -3,6 +3,7 @@ #include #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 @@ -1665,9 +1666,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; @@ -1825,7 +1826,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] - WREG(43); } - phi_t0 = this->windowPosX - 14; + phi_t0 = this->windowPosX - 26; temp_t1 -= 0x16; for (phi_a1 = 0; phi_a1 < 4; phi_a1++, phi_t2 += 4) { @@ -1835,7 +1836,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; @@ -1932,6 +1933,30 @@ 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 }; @@ -2012,6 +2037,31 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { } } + // 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; 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 d2050bc80c6..3c988be23c1 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 116ad0c3cfb..0dc0544d930 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 @@ -638,6 +638,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(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) { @@ -856,5 +857,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); } From 3ab043499c0fa8449aa428a77ed286db73761e79 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 24 Aug 2024 02:13:27 -0600 Subject: [PATCH 02/18] Fixed enemies not getting level data. Save Editor correctly updates max HP when changed. Added space for HP values over 1000. --- soh/include/leveled_stat_math.h | 2 +- soh/soh/Enhancements/debugger/debugSaveEditor.cpp | 6 ++++++ .../game-interactor/GameInteractionEffect.cpp | 2 +- soh/soh/SohGui.cpp | 6 +++--- soh/src/code/leveled_overlays.c | 9 ++++++--- soh/src/code/z_actor.c | 13 +++++++++++-- soh/src/overlays/actors/ovl_player_actor/z_player.c | 2 +- .../misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c | 2 +- 8 files changed, 30 insertions(+), 12 deletions(-) diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h index 45bfc4886a9..28251c41ec6 100644 --- a/soh/include/leveled_stat_math.h +++ b/soh/include/leveled_stat_math.h @@ -1,6 +1,6 @@ #ifndef LEVELED_STAT_MATH_H #define LEVELED_STAT_MATH_H -#define HEALTH_ATTACK_MULTIPLIER 15 +#define HEALTH_ATTACK_MULTIPLIER 17 #include "z64.h" #ifdef __cplusplus diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index ebaf62a9a61..384c9b8369a 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -18,6 +18,7 @@ extern "C" { #include "functions.h" #include "macros.h" #include "soh/Enhancements/randomizer/adult_trade_shuffle.h" +#include "leveled_stat_math.h" extern PlayState* gPlayState; #include "textures/icon_item_static/icon_item_static.h" @@ -339,6 +340,11 @@ void DrawInfoTab() { ImGui::InputScalar("Max Health", ImGuiDataType_S16, &healthIntermediary); if (ImGui::IsItemDeactivated()) { gSaveContext.healthCapacity = healthIntermediary; + + Player* player = GET_PLAYER(gPlayState); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, player->actor.level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; } UIWidgets::InsertHelpHoverText("Maximum health. 16 units per full heart"); if (gSaveContext.health > gSaveContext.healthCapacity2) { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index a0469d0c398..d75dc6cc1cd 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -194,7 +194,7 @@ namespace GameInteractionEffect { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ( - (parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) + (parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity2) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0)) ) { return GameInteractionEffectQueryResult::NotPossible; diff --git a/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp index 71a3660217f..6ebbe09aaca 100644 --- a/soh/soh/SohGui.cpp +++ b/soh/soh/SohGui.cpp @@ -200,9 +200,9 @@ namespace SohGui { gui->AddGuiWindow(mRandomizerSettingsWindow); mAdvancedResolutionSettingsWindow = std::make_shared(CVAR_WINDOW("AdvancedResolutionEditor"), "Advanced Resolution Settings"); gui->AddGuiWindow(mAdvancedResolutionSettingsWindow); - mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); - gui->AddGuiWindow(mModalWindow); - mModalWindow->Show(); + //mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); + //gui->AddGuiWindow(mModalWindow); + //mModalWindow->Show(); } void Destroy() { diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 9993d0ef54c..f0680e94bde 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -617,9 +617,12 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { statY += 10; // Health Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); - Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 29, statY - 1, 8, 9, 255, 255, 255); - Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); - Leveled_ValueNumberDraw(play, statX + 33, statY, gSaveContext.healthCapacity2, 120, 255, 0); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + (gSaveContext.healthCapacity2 >= 1000 ? 35 : 29), + statY - 1, 8, 9, 255, 255, 255); + u8 healthValX = 10; + Leveled_ValueNumberDraw(play, statX + healthValX, statY, gSaveContext.health, 255, 255, 255); + healthValX = gSaveContext.healthCapacity2 >= 1000 ? 39 : 33; + Leveled_ValueNumberDraw(play, statX + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; // Magic if (gSaveContext.magicCapacity > 0) { diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 1c9284d5e0d..c2d291ae1a3 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1219,8 +1219,9 @@ 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.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, actor->level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; gSaveContext.magicUnits = GetPlayerStat_MagicUnits(actor->level); Leveled_SetPlayerModifiedStats(player); } else { @@ -2624,6 +2625,14 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { if (actor->category == ACTORCAT_ENEMY) { actor->maximumHealth = actor->colChkInfo.health; } + + 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)); } actor = actor->next; } else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { 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 9d94ad50d9b..4e53544ae09 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -10126,7 +10126,6 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe this->meleeWeaponEffectIndex = TOTAL_EFFECT_COUNT; this->yaw = this->actor.world.rot.y; func_80834644(play, this); - Player_GainExperience(play, 0); SkelAnime_InitLink(play, &this->skelAnime, skelHeader, GET_PLAYER_ANIM(PLAYER_ANIMGROUP_wait, this->modelAnimType), 9, this->jointTable, this->morphTable, PLAYER_LIMB_MAX); @@ -10146,6 +10145,7 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe Collider_SetQuad(play, &this->meleeWeaponQuads[1], &this->actor, &D_80854650); Collider_InitQuad(play, &this->shieldQuad); Collider_SetQuad(play, &this->shieldQuad, &this->actor, &D_808546A0); + Player_GainExperience(play, 0); this->ivanDamageMultiplier = 1; } 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 a8b7536f0fb..12081df81cf 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 @@ -4508,7 +4508,7 @@ void KaleidoScope_Update(PlayState* play) // Reset frame counter to prevent autosave on respawn play->gameplayFrames = 0; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK; - gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = MAGIC_STATE_IDLE; From 8e470ed21e0d948b3db379ff43f2beb22c937a30 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 24 Aug 2024 02:33:20 -0600 Subject: [PATCH 03/18] Fix some damage values when using the damage multiplier setting. --- soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c | 3 ++- soh/src/overlays/actors/ovl_player_actor/z_player.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) 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 95dc4ba1600..c0c7e276756 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 @@ -781,7 +781,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -64); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 64); + play->damagePlayer(play, -damage); this->unk_2FE = 0; } } 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 4e53544ae09..ac5fba59400 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4036,12 +4036,12 @@ s32 func_80837B18_modified(PlayState* play, Player* this, s32 damage, u8 modifie return 1; } - /* s32 modifiedDamage = damage; + s32 modifiedDamage = damage; if (modified) { modifiedDamage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); - }*/ + } - return Health_ChangeBy(play, damage); + return Health_ChangeBy(play, modifiedDamage); } s32 func_80837B18(PlayState* play, Player* this, s32 damage) { @@ -4080,7 +4080,7 @@ void func_80837C0C(PlayState* play, Player* this, s32 arg2, f32 arg3, f32 arg4, Player_PlaySfx(this, NA_SE_PL_DAMAGE); - if (!func_80837B18(play, this, 0 - this->actor.colChkInfo.damage)) { + if (!func_80837B18_modified(play, this, 0 - this->actor.colChkInfo.damage, false)) { this->stateFlags2 &= ~PLAYER_STATE2_GRABBED_BY_ENEMY; if (!(this->actor.bgCheckFlags & 1) && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)) { func_80837B9C(this, play); From 0991bc4c33bab8bbbe5fc126563f79f72c1e372e Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 24 Aug 2024 16:27:33 -0600 Subject: [PATCH 04/18] Correct timers for breath or extreme heat when heart units are changed. --- soh/src/code/z_actor.c | 23 ++++++++++------------- soh/src/code/z_parameter.c | 6 ++++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index c2d291ae1a3..9f9e29955e0 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1260,16 +1260,14 @@ void Actor_Init(Actor* actor, PlayState* play) { actor->init(actor, play); actor->init = NULL; - GameInteractor_ExecuteOnActorInit(actor); - - // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { - actor->maximumHealth = actor->colChkInfo.health; - } - if (actor->category != ACTORCAT_PLAYER) { Actor_GetLevelAndExperience(play, actor, 0); actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + + GameInteractor_ExecuteOnActorInit(actor); + + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } @@ -2619,16 +2617,15 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { actor->init(actor, play); actor->init = NULL; - GameInteractor_ExecuteOnActorInit(actor); - - // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { - actor->maximumHealth = actor->colChkInfo.health; - } if (actor->category != ACTORCAT_PLAYER) { Actor_GetLevelAndExperience(play, actor, 0); actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + + GameInteractor_ExecuteOnActorInit(actor); + + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 96fed4bf2d6..20e4df42c43 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -6002,7 +6002,8 @@ void Interface_Draw(PlayState* play) { case 1: D_8015FFE2 = 20; D_8015FFE0 = 20; - gSaveContext.timer1Value = gSaveContext.health >> 1; + u8 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.timer1Value = (s32)((f32)gSaveContext.health / heartUnits * 8); gSaveContext.timer1State = 2; break; case 2: @@ -6805,7 +6806,8 @@ void Interface_Update(PlayState* play) { } if (gSaveContext.timer1State == 0) { - if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && ((gSaveContext.health >> 1) != 0)) { + u8 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && (((s32)((f32)gSaveContext.health / heartUnits * 8)) != 0)) { gSaveContext.timer1State = 1; gSaveContext.timerX[0] = 140; gSaveContext.timerY[0] = 80; From ea58cbf71e542820e80a325f688a0b102a6d6418 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 24 Aug 2024 22:59:37 -0600 Subject: [PATCH 05/18] Enemy EXP scales with HP when setting is enabled. --- soh/soh/Enhancements/mods.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 15963bac661..0e356564b92 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -1241,9 +1241,11 @@ void RegisterRandomizedEnemySizes() { // Scale the health based on a smaller factor than randomScale float healthScalingFactor = 0.8f; // Adjust this factor as needed float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor); + float scaledExp = actor->exp * (randomScale * healthScalingFactor); // Ensure the scaled health doesn't go below zero actor->colChkInfo.health = fmax(scaledHealth, 1.0f); + actor->exp = fmax(scaledExp, 1.0f); } else { return; } From d1dbb2a0b4241d1f2b8d07761396fa0113c2d6c3 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 24 Aug 2024 23:18:47 -0600 Subject: [PATCH 06/18] More damage multiplier fixes, Fixed Magic icon in stats. --- soh/src/code/leveled_overlays.c | 10 +++++----- soh/src/overlays/actors/ovl_player_actor/z_player.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index f0680e94bde..17133ed5ff0 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -610,8 +610,8 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { // Values and Icons // Level - Leveled_DrawTexI8(play, (u8*)gMsgChar4CLatinCapitalLetterLTex, 8, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); - Leveled_DrawTexI8(play, (u8*)gMsgChar76LatinSmallLetterVTex, 8, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar76LatinSmallLetterVTex, 8, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); Leveled_BigValueNumberDraw(play, statX + 10, statY - 6, player->actor.level, 255, 255, 255, 255); statY += 10; @@ -621,15 +621,15 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { statY - 1, 8, 9, 255, 255, 255); u8 healthValX = 10; Leveled_ValueNumberDraw(play, statX + healthValX, statY, gSaveContext.health, 255, 255, 255); - healthValX = gSaveContext.healthCapacity2 >= 1000 ? 39 : 33; + healthValX = gSaveContext.healthCapacity2 >= 1000 ? 40 : 34; Leveled_ValueNumberDraw(play, statX + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; // Magic if (gSaveContext.magicCapacity > 0) { - Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 24, 24, statX + 2, statY, 14, 14); Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 29, statY - 1, 8, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); - Leveled_ValueNumberDraw(play, statX + 33, statY, gSaveContext.magicCapacity, 120, 255, 0); + Leveled_ValueNumberDraw(play, statX + 34, statY, gSaveContext.magicCapacity, 120, 255, 0); statY += 8; } // Attack 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 ac5fba59400..c3094caf26e 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4409,7 +4409,7 @@ s32 func_808382DC(Player* this, PlayState* play) { ((sp48 >= 0) && ((this->currentTunic != PLAYER_TUNIC_GORON && CVarGetInteger(CVAR_CHEAT("SuperTunic"), 0) == 0) || (this->floorTypeTimer >= D_808544F4[sp48])))) { this->floorTypeTimer = 0; - this->actor.colChkInfo.damage = 4; + this->actor.colChkInfo.damage = 4 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, 0, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } else { return 0; @@ -8656,7 +8656,7 @@ s32 func_80842DF4(PlayState* play, Player* this) { func_80842B7C(play, this); if (this->actor.colChkInfo.atHitEffect == 1) { - this->actor.colChkInfo.damage = 8; + this->actor.colChkInfo.damage = 8 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, 4, 0.0f, 0.0f, this->actor.shape.rot.y, 20); return 1; } @@ -9276,7 +9276,7 @@ void Player_Action_80844A44(Player* this, PlayState* play) { Math_StepToF(&this->linearVelocity, 0.0f, 0.05f); if (this->actor.bgCheckFlags & 1) { - this->actor.colChkInfo.damage = 0x10; + this->actor.colChkInfo.damage = 0x10 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, 1, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } } From 671193c1ebdaaaa2436aee5059c7369a5df23b80 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 25 Aug 2024 00:37:12 -0600 Subject: [PATCH 07/18] Yet more fixes for damage multiplier. --- soh/soh/SaveManager.cpp | 3 ++- soh/src/overlays/actors/ovl_player_actor/z_player.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 4bb69172488..7fffc93cf3a 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -658,7 +658,8 @@ void SaveManager::InitFileDebug() { gSaveContext.experience = 80000; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0xE0; - gSaveContext.health = 0xE0; + gSaveContext.healthCapacity2 = 0x270F; + gSaveContext.health = 0x270F; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 150; 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 c3094caf26e..a6a07376757 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4310,7 +4310,7 @@ s32 func_808382DC(Player* this, PlayState* play) { this->bodyShockTimer = 40; } - this->actor.colChkInfo.damage += this->unk_8A0; + this->actor.colChkInfo.damage += this->unk_8A0 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, sp5C[this->unk_8A1 - 1], this->unk_8A4, this->unk_8A8, this->unk_8A2, 20); } else { sp64 = (this->shieldQuad.base.acFlags & AC_BOUNCED) != 0; From ed9c077c49caabd8c3b4a657268650d29e459bd6 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 2 Sep 2024 14:19:29 -0600 Subject: [PATCH 08/18] Fix Gohma Larva and Floormaster (Oops) --- .../overlays/actors/ovl_En_Floormas/z_en_floormas.c | 1 + soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) 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 acf9d1ac880..a22a9fd41f0 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 @@ -738,6 +738,7 @@ 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_SetupSmWait(this); EnFloormas* parent = (EnFloormas*)this->actor.parent; EnFloormas* child = (EnFloormas*)this->actor.child; if ((parent->actionFunc == EnFloormas_SmWait) && (child->actionFunc == EnFloormas_SmWait)) { 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 e8e9a25a8f4..d29a808eb24 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 @@ -326,7 +326,7 @@ void EnGoma_SetupHurt(EnGoma* this, PlayState* play) { Animation_GetLastFrame(&gObjectGolDamagedAnim), ANIMMODE_ONCE, -2.0f); this->actionFunc = EnGoma_Hurt; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { this->actionTimer = 5; Enemy_StartFinishingBlow(play, &this->actor); } else { @@ -350,7 +350,7 @@ void EnGoma_Hurt(EnGoma* this, PlayState* play) { } if (this->actionTimer == 0) { - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { EnGoma_SetupDie(this); } else { EnGoma_SetupFlee(this); @@ -624,7 +624,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->actor.velocity.y = 0.0f; } - if ((this->colCyl2.base.acFlags & AC_HIT) && (s8)this->actor.colChkInfo.health > 0) { + if ((this->colCyl2.base.acFlags & AC_HIT) && this->actor.colChkInfo.health > 0) { acHitInfo = this->colCyl2.info.acHitInfo; this->colCyl2.base.acFlags &= ~AC_HIT; @@ -656,7 +656,11 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { } swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * HEALTH_ATTACK_MULTIPLIER); - this->actor.colChkInfo.health -= swordDamage; + if (swordDamage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= swordDamage; + } else { + this->actor.colChkInfo.health = 0; + } ActorDamageNumber_New(&this->actor, swordDamage); EnGoma_SetupHurt(this, play); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); From d9bbce89257a22f874fcac53d6ad674cca0fabb2 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 6 Sep 2024 02:31:39 -0600 Subject: [PATCH 09/18] Reorganized the options for the mod. Added EXP, enemy level, and HP sliders. Added options to disable level up on hud, and an option to disable level up sound. --- .../GameInteractor_RawAction.cpp | 6 +- soh/soh/OTRGlobals.cpp | 4 +- soh/soh/SaveManager.cpp | 2 +- soh/soh/SohMenuBar.cpp | 129 ++++++++++++------ soh/src/code/leveled_actor_level_table.c | 2 +- soh/src/code/leveled_map_levels.c | 5 +- soh/src/code/leveled_overlays.c | 6 +- soh/src/code/leveled_stat_math.c | 14 +- soh/src/code/z_lifemeter.c | 6 +- soh/src/code/z_message_PAL.c | 4 +- soh/src/code/z_parameter.c | 18 +-- soh/src/code/z_player_lib.c | 12 +- soh/src/code/z_sram.c | 4 +- soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c | 6 +- .../actors/ovl_player_actor/z_player.c | 28 ++-- .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 2 +- 16 files changed, 149 insertions(+), 99 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 2730fbd45f9..a2c8dbe1eba 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 * CVarGetInteger("gLeveledHeartUnits", 4) << 2); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveledHeartUnits", 4) << 2); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } } void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { - gSaveContext.health = hearts * CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.health = hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } void GameInteractor::RawAction::SetLinkInvisibility(bool active) { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index a50c1b79e20..89ac7e09cba 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2375,13 +2375,13 @@ extern "C" int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBuf postfix = ""; } else { postfix = ""; - if (CVarGetInteger("gLeveledNaviLevel", 1)) { + if (CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1)) { postfix += " \x05" "F" "Lv" + std::to_string(actor->level); } - if (CVarGetInteger("gLeveledNaviMaxHP", 1) && actor->maximumHealth > 0) { + if (CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1) && actor->maximumHealth > 0) { postfix += " \x05" "A" "MaxHP " + diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 7fffc93cf3a..d39ae8eda67 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -501,7 +501,7 @@ void SaveManager::InitFileNormal() { gSaveContext.showNeededExpTimer = 0; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0x30; - gSaveContext.health = 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.health = 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 0; diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index b5922d9d123..36be765e82a 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -2031,58 +2031,99 @@ 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("UI")) { + if (ImGui::BeginMenu("HUD")) { + EnhancementCheckbox("EXP to NEXT Level", "gLeveled.HUD.ExperienceNextLevel", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + UIWidgets::Tooltip("Show experience required to level up popup in the HUD when gaining EXP."); + EnhancementCheckbox("Level Up", "gLeveled.HUD.LevelUp", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + UIWidgets::Tooltip("Show Level Up popup when leveling up."); + EnhancementCheckbox("Level Up Sound", "gLeveled.HUD.LevelUpSound", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + ImGui::EndMenu(); + } - if (ImGui::BeginMenu("Floating Numbers")) { - EnhancementCheckbox("Enemy Damage", "gLeveledFloatingNumberEnemyDamage", false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - PaddedEnhancementCheckbox("Player Damage", "gLeveledFloatingNumberPlayerDamage", true, false, false, "", + if (ImGui::BeginMenu("Floating Numbers")) { + EnhancementCheckbox("Enemy Damage", "gLeveled.HUD.FloatingNumbers.EnemyDamage", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("Player Damage", "gLeveled.HUD.FloatingNumbers.PlayerDamage", true, false, + false, "", UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("EXP Gain", "gLeveled.HUD.FloatingNumbers.ExpGain", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + ImGui::EndMenu(); + } + PaddedEnhancementCheckbox("Navi tells enemy level", "gLeveled.Navi.TellEnemyLevel", true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - PaddedEnhancementCheckbox("EXP Gain", "gLeveledFloatingNumberExpGain", true, false, false, "", + PaddedEnhancementCheckbox("Navi tells enemy max HP", "gLeveled.Navi.TellEnemyMaxHP", true, true, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); ImGui::EndMenu(); } - PaddedEnhancementCheckbox("Level Gives Bonus Hearts", "gLeveledHeartsWithLevelUp", true, false, false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - PaddedEnhancementCheckbox("Level Affects Magic Capacity", "gLeveledMagicWithLevelUp", true, false, false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - 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."); - PaddedEnhancementCheckbox("Equipment Affects Stats", "gLeveledEquipmentStats", true, false, false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - PaddedEnhancementCheckbox("Navi tells enemy level", "gLeveledNaviLevel", true, false, false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - PaddedEnhancementCheckbox("Navi tells enemy max HP", "gLeveledNaviMaxHP", true, true, false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); - - if (ImGui::BeginMenu("Heart Container Value in Units")) { - CVarGetInteger("gLeveledHeartUnits", 4); - UIWidgets::EnhancementRadioButton("4 (x0.25)", "gLeveledHeartUnits", 1); - UIWidgets::EnhancementRadioButton("8 (x0.5)", "gLeveledHeartUnits", 2); - UIWidgets::EnhancementRadioButton("12 (x0.75)", "gLeveledHeartUnits", 3); - UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveledHeartUnits", 4); - UIWidgets::EnhancementRadioButton("20 (x1.25)", "gLeveledHeartUnits", 5); - UIWidgets::EnhancementRadioButton("24 (x1.5)", "gLeveledHeartUnits", 6); - UIWidgets::EnhancementRadioButton("28 (x1.75)", "gLeveledHeartUnits", 7); - UIWidgets::EnhancementRadioButton("32 (x2)", "gLeveledHeartUnits", 8); - UIWidgets::EnhancementRadioButton("48 (x3)", "gLeveledHeartUnits", 12); - UIWidgets::EnhancementRadioButton("64 (x4)", "gLeveledHeartUnits", 16); - UIWidgets::EnhancementRadioButton("80 (x5)", "gLeveledHeartUnits", 20); - UIWidgets::EnhancementRadioButton("96 (x6)", "gLeveledHeartUnits", 24); - UIWidgets::EnhancementRadioButton("112 (x7)", "gLeveledHeartUnits", 28); - UIWidgets::EnhancementRadioButton("128 (x8)", "gLeveledHeartUnits", 32); + + if (ImGui::BeginMenu("Entity Modifications")) { + if (ImGui::BeginMenu("Player Modifications")) { + PaddedEnhancementCheckbox("Level Gives Bonus Hearts", "gLeveled.Player.Enhancements.HeartsWithLevelUp", + true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("Level Affects Magic Capacity", + "gLeveled.Player.Enhancements.MagicWithLevelUp", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + PaddedEnhancementCheckbox("Equipment Affects Stats", "gLeveled.Player.Enhancements.EquipmentStats", + true, false, false, "", UIWidgets::CheckboxGraphics::Checkmark, true); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Enemy Modifications")) { + PaddedEnhancementCheckbox("Enemy Level Affects Base Attack", + "gLeveled.Enemy.Enhancements.AttackScalesWithLevel", 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."); + + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + + + if (ImGui::BeginMenu("Difficulty Options")) { + if (ImGui::BeginMenu("Heart Container Value in Units")) { + UIWidgets::EnhancementRadioButton("4 (x0.25)", "gLeveled.Difficulty.HeartUnits", 1); + UIWidgets::EnhancementRadioButton("8 (x0.5)", "gLeveled.Difficulty.HeartUnits", 2); + UIWidgets::EnhancementRadioButton("12 (x0.75)", "gLeveled.Difficulty.HeartUnits", 3); + UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveled.Difficulty.HeartUnits", 4); + UIWidgets::EnhancementRadioButton("20 (x1.25)", "gLeveled.Difficulty.HeartUnits", 5); + UIWidgets::EnhancementRadioButton("24 (x1.5)", "gLeveled.Difficulty.HeartUnits", 6); + UIWidgets::EnhancementRadioButton("28 (x1.75)", "gLeveled.Difficulty.HeartUnits", 7); + UIWidgets::EnhancementRadioButton("32 (x2)", "gLeveled.Difficulty.HeartUnits", 8); + UIWidgets::EnhancementRadioButton("48 (x3)", "gLeveled.Difficulty.HeartUnits", 12); + UIWidgets::EnhancementRadioButton("64 (x4)", "gLeveled.Difficulty.HeartUnits", 16); + UIWidgets::EnhancementRadioButton("80 (x5)", "gLeveled.Difficulty.HeartUnits", 20); + UIWidgets::EnhancementRadioButton("96 (x6)", "gLeveled.Difficulty.HeartUnits", 24); + UIWidgets::EnhancementRadioButton("112 (x7)", "gLeveled.Difficulty.HeartUnits", 28); + UIWidgets::EnhancementRadioButton("128 (x8)", "gLeveled.Difficulty.HeartUnits", 32); + ImGui::EndMenu(); + } + 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::BeginMenu("EXP Options")) { + UIWidgets::PaddedEnhancementSliderFloat("EXP Rate: %.1f %%", "##Leveled_EXPRate", "gLeveled.Difficulty.EXP.Rate", 0.0f, 10.0f, "", 1.0f, true, true, false, false); + UIWidgets::PaddedEnhancementSliderFloat("Skulltula Token EXP Rate: %.1f %%", "##Leveled_TokenEXPRate", "gLeveled.Difficulty.EXP.TokenRate", 0.0f, 10.0f, "", 1.0f, true, true, false, false); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Enemy Options")) { + UIWidgets::PaddedEnhancementSliderFloat("Enemy HP: %.1f %%", "##Leveled_EnemyHP", "gLeveled.Difficulty.Enemy.HPPercent", 0.0f, 10.0f, "", 1.0f, true, true, false, false); + UIWidgets::PaddedEnhancementSliderFloat("Level Scale: %.1f %%", "##Leveled_EnemyLevel", "gLeveled.Difficulty.Enemy.LevelScale", 0.0f, 2.0f, "", 1.0f, true, true, false, false); + UIWidgets::Tooltip("Adjust enemy level by percentage. Caps at level 99."); + PaddedEnhancementCheckbox("Collecting all 100 tokens makes all enemies in Ganon's Castle level 99.", + "gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", true, false, false, "", + UIWidgets::CheckboxGraphics::Checkmark, false); + ImGui::EndMenu(); + } ImGui::EndMenu(); } - 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; diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c index 4179ad1da2e..e059bab5371 100644 --- a/soh/src/code/leveled_actor_level_table.c +++ b/soh/src/code/leveled_actor_level_table.c @@ -964,7 +964,7 @@ void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverr s8 sceneLevel = Leveled_GetSceneLevel(play->sceneNum); if (!levelEntry.ignoreEntry && sceneLevel >= 0) { - actor->level = CLAMP_MIN(sceneLevel + levelEntry.levelModifier, 1); + actor->level = (u8)CLAMP((f32)(sceneLevel + levelEntry.levelModifier) * (f32)CVarGetFloat("gLeveled.Difficulty.Enemy.LevelScale", 1.0f), 1, 99); 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 index de5c1d4ca20..ea1616b8043 100644 --- a/soh/src/code/leveled_map_levels.c +++ b/soh/src/code/leveled_map_levels.c @@ -44,7 +44,10 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { case SCENE_GANONDORF_BOSS: case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: case SCENE_GANON_BOSS: - return 50; + if (CVarGetInteger("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", 0) == 1 && gSaveContext.inventory.gsTokens == 100) + return 120; + else + return 50; case SCENE_GERUDO_TRAINING_GROUND: return 43; diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 17133ed5ff0..3310643d71b 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -104,7 +104,7 @@ void Leveled_OverlayDrawTex4b(PlayState* play, void* texture, s16 textureWidth, } void ActorDamageNumber_New(Actor* actor, u16 damage) { - if (damage == 0 || (actor->category == ACTORCAT_PLAYER && !CVarGetInteger("gLeveledFloatingNumberPlayerDamage", 1)) || (actor->category != ACTORCAT_PLAYER && !CVarGetInteger("gLeveledFloatingNumberEnemyDamage", 1))) + if (damage == 0 || (actor->category == ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.PlayerDamage", 1)) || (actor->category != ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.EnemyDamage", 1))) return; Vec2f position = { 0, 0 }; @@ -117,7 +117,7 @@ void ActorDamageNumber_New(Actor* actor, u16 damage) { } void ActorExperienceNumber_New(Actor* actor, u16 experience) { - if (experience == 0 || !CVarGetInteger("gLeveledFloatingNumberExpGain", 1)) + if (experience == 0 || !CVarGetInteger("gLeveled.HUD.FloatingNumbers.ExpGain", 1)) return; @@ -682,7 +682,7 @@ void Leveled_Interface_DrawNextLevel(PlayState* play) { } else { return; } - if (play->pauseCtx.state != 0 || !CVarGetInteger("gLeveledHUDExperienceNextLevel", 1)) + if (play->pauseCtx.state != 0 || !CVarGetInteger("gLeveled.HUD.ExperienceNextLevel", 1)) return; Player* player = GET_PLAYER(play); diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c index ff54bfb0725..e7e43346b9b 100644 --- a/soh/src/code/leveled_stat_math.c +++ b/soh/src/code/leveled_stat_math.c @@ -49,11 +49,11 @@ u8 GetActorStat_PlayerCourage(u8 level) { } u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level){ - return GetActorStat_Attack(baseHealth * HEALTH_ATTACK_MULTIPLIER, GetActorStat_PlayerPower(level)); + return (u16)(CLAMP((f32)GetActorStat_Attack(baseHealth * HEALTH_ATTACK_MULTIPLIER, GetActorStat_PlayerPower(level)) * CVarGetFloat("gLeveled.Difficulty.Enemy.HPPercent", 1.0f), 1, 0xffff)); } u8 GetPlayerStat_BonusHearts(u8 level){ - if (CVarGetInteger("gLeveledHeartsWithLevelUp", 1) == 0){ + if (CVarGetInteger("gLeveled.Player.Enhancements.HeartsWithLevelUp", 1) == 0){ return 0; } @@ -65,7 +65,7 @@ u8 GetPlayerStat_BonusHearts(u8 level){ } u8 GetPlayerStat_MagicUnits(u8 level){ - if (CVarGetInteger("gLeveledMagicWithLevelUp", 1) == 0){ + if (CVarGetInteger("gLeveled.Player.Enhancements.MagicWithLevelUp", 1) == 0){ return 48; } @@ -77,7 +77,7 @@ u8 GetPlayerStat_MagicUnits(u8 level){ } u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level){ - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; u16 baseHearts = baseHealth / 16; return (baseHearts + GetPlayerStat_BonusHearts(level)) * heartUnits; } @@ -124,7 +124,7 @@ f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) { f32 Leveled_DamageFormulaOnPlayer(f32 attack, u8 power, u8 courage) { f32 damage = attack; - if (CVarGetInteger("gLeveledEnemyAttackScalesWithLevel", 1) == 1){ + if (CVarGetInteger("gLeveled.Enemy.Enhancements.AttackScalesWithLevel", 1) == 1){ damage = GetActorStat_EnemyAttack(attack, power); if (power >= courage) { @@ -179,7 +179,7 @@ u16 Leveled_GoldSkulltulaExperience(u8 tokens) { u8 i; for (i = 0; i < tokens; i++) { - experience += 5 + 5 * (u16)((f32)i / 10.0); + experience += 5 + 5 * (u16)(CVarGetFloat("gLeveled.Difficulty.EXP.TokenRate", 1.0f) * (f32) i / (f32)10.0); } return experience; } @@ -188,7 +188,7 @@ void Leveled_SetPlayerModifiedStats(Player* player) { s8 powerModifier = 0; s8 courageModifier = 0; - if (CVarGetInteger("gLeveledEquipmentStats", 1) == 1){ + if (CVarGetInteger("gLeveled.Player.Enhancements.EquipmentStats", 1) == 1){ switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD)){ case PLAYER_SWORD_MASTER: courageModifier += 1; diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 9b6b6dc7824..ed752b6b60b 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -385,10 +385,10 @@ void HealthMeter_Draw(PlayState* play) { f32 temp2; f32 temp3; f32 temp4; - u8 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (heartUnits < 4) { heartUnits = 4; - CVarSetInteger("gLeveledHeartUnits", 1); + CVarSetInteger("gLeveled.Difficulty.HeartUnits", 1); } InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; @@ -668,7 +668,7 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { u32 HealthMeter_IsCritical(void) { s32 var; - s32 heartValue = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartValue = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; var = (s32)(heartValue + (f32)(((gSaveContext.healthCapacity2 / heartValue) - 3) * heartValue) * 0.103f); diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index f2a9a615096..bb9b162cba9 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -1666,7 +1666,7 @@ 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)) && + } else if ((CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1) || CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1)) && (textId > 0x0600 && textId < 0x06FF) && play->actorCtx.targetCtx.targetedActor != NULL) { Message_FindMessage(play, textId); msgCtx->msgLength = font->msgLength = GetLeveledNaviEnemyInfo( @@ -3371,7 +3371,7 @@ void Message_Update(PlayState* play) { msgCtx->textboxEndType = TEXTBOX_ENDTYPE_DEFAULT; } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.inventory.questItems ^= 0x40000000; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 20e4df42c43..8a26e3be541 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2356,7 +2356,7 @@ 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; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; gSaveContext.health += heartUnits; @@ -2372,7 +2372,7 @@ u8 Item_Give(PlayState* play, u8 item) { } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Health_ChangeBy(play, heartUnits); } return Return_Item(item, MOD_NONE, item); @@ -3197,7 +3197,7 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { gSaveContext.health = gSaveContext.healthCapacity2; } - heartCount = gSaveContext.health / CVarGetInteger("gLeveledHeartUnits", 4) << 2; + heartCount = gSaveContext.health / CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; healthLevel = heartCount; if (heartCount != 0) { @@ -3752,7 +3752,7 @@ void Interface_DrawMagicBar(PlayState* play) { } else if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == 5) {//Anchor To life meter magicBarY = R_MAGIC_BAR_SMALL_Y-2 + magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity2 - 1) / - ((CVarGetInteger("gLeveledHeartUnits", 4) << 2) * lineLength)) + + ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength)) + CVarGetInteger("gMagicBarPosY", 0) + getHealthMeterYOffset(); s16 xPushover = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 0) + getHealthMeterXOffset() + R_MAGIC_BAR_X-1; PosX_Start = xPushover; @@ -3761,11 +3761,11 @@ void Interface_DrawMagicBar(PlayState* play) { rMagicFillX = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 0) + getHealthMeterXOffset() + R_MAGIC_FILL_X-1; } } else { - if ((gSaveContext.healthCapacity2 - 1) / (CVarGetInteger("gLeveledHeartUnits", 4) << 2) >= lineLength && + if ((gSaveContext.healthCapacity2 - 1) / (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) >= lineLength && lineLength != 0) { magicBarY = magicBarY_original_l + magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity2 - 1) / - ((CVarGetInteger("gLeveledHeartUnits", 4) << 2) * lineLength) - 1)); + ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength) - 1)); } else { magicBarY = magicBarY_original_s; } @@ -6002,7 +6002,7 @@ void Interface_Draw(PlayState* play) { case 1: D_8015FFE2 = 20; D_8015FFE0 = 20; - u8 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.timer1Value = (s32)((f32)gSaveContext.health / heartUnits * 8); gSaveContext.timer1State = 2; break; @@ -6653,7 +6653,7 @@ void Interface_Update(PlayState* play) { Map_Update(play); if (gSaveContext.healthAccumulator != 0) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator -= heartUnits >> 2; gSaveContext.health += heartUnits >> 2; @@ -6806,7 +6806,7 @@ void Interface_Update(PlayState* play) { } if (gSaveContext.timer1State == 0) { - u8 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && (((s32)((f32)gSaveContext.health / heartUnits * 8)) != 0)) { gSaveContext.timer1State = 1; gSaveContext.timerX[0] = 140; diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 9760d134ac6..715e63085e3 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -809,6 +809,9 @@ void Player_GainExperience(PlayState* play, u16 experience) { if (player == NULL) return; + + experience = (u16)(CLAMP(round((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.Rate", (f32)1.0)), 0, 0xffff)); + bool levelUp = false; u8 prevPower = player->actor.power; u8 prevCourage = player->actor.courage; @@ -843,9 +846,12 @@ void Player_GainExperience(PlayState* play, u16 experience) { 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); + if (CVarGetInteger("gLeveled.HUD.LevelUp", 1) == 1) { + ActorLevelUp_New(&player->actor, player->actor.power - prevPower, player->actor.courage - prevCourage, gSaveContext.healthCapacity2 - prevHealthCapacity, gSaveContext.magicUnits - prevMagicUnits); + } + if (CVarGetInteger("gLeveled.HUD.LevelUpSound", 1) == 1) { + Audio_PlayFanfare(NA_BGM_ITEM_GET); + } } } diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 21153c79b17..f8de70953cc 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -141,8 +141,8 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2) { - gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; + if (gSaveContext.health < 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) { + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } if (gSaveContext.scarecrowLongSongSet) { 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 88f040b8129..2e292b924ec 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,18 +635,18 @@ void func_80A0329C(EnElf* this, PlayState* play) { { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyPercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Health_ChangeBy(play, heartUnits + ((gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 100) / 100)); } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Health_ChangeBy(play, CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 8) * heartUnits); } } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Health_ChangeBy(play, 8 * heartUnits); } if (this->fairyFlags & FAIRY_FLAG_BIG) { 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 a6a07376757..9fe03a483b7 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -8909,10 +8909,10 @@ void func_80843AE8(PlayState* play, Player* this) { } if (CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveEffect"), 0)) { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyRevivePercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = heartUnits + ((gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveHealth"), 100) / 100); } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("FairyReviveHealth"), 20) * heartUnits; } } else { @@ -13818,7 +13818,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { if (this->av2.actionVar2 == 0) { if (this->itemAction == PLAYER_IA_BOTTLE_POE) { s32 rand = Rand_S16Offset(-1, 3); - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (rand == 0) { rand = 3; @@ -13838,19 +13838,19 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { if (CVarGetInteger(CVAR_ENHANCEMENT("RedPotionEffect"), 0) && this->itemAction == PLAYER_IA_BOTTLE_POTION_RED) { if (CVarGetInteger(CVAR_ENHANCEMENT("RedPercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("RedPotionHealth"), 100) / 100; } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("RedPotionHealth"), 20) * heartUnits; } } else if (CVarGetInteger(CVAR_ENHANCEMENT("BluePotionEffects"), 0) && this->itemAction == PLAYER_IA_BOTTLE_POTION_BLUE) { if (CVarGetInteger(CVAR_ENHANCEMENT("BlueHealthPercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("BluePotionHealth"), 100) / 100; } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("BluePotionHealth"), 20) * heartUnits; } @@ -13893,19 +13893,19 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } else if (CVarGetInteger(CVAR_ENHANCEMENT("MilkEffect"), 0) && (this->itemAction == PLAYER_IA_BOTTLE_MILK_FULL || this->itemAction == PLAYER_IA_BOTTLE_MILK_HALF)) { if (CVarGetInteger(CVAR_ENHANCEMENT("MilkPercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("MilkHealth"), 100) / 100; } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("MilkHealth"), 5) * heartUnits; } if (CVarGetInteger(CVAR_ENHANCEMENT("SeparateHalfMilkEffect"), 0) && this->itemAction == PLAYER_IA_BOTTLE_MILK_HALF) { if (CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkPercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2 - heartUnits) * CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkHealth"), 100) / 100; } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("HalfMilkHealth"), 5) * heartUnits; } } @@ -13919,7 +13919,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } if (sp28 & 4) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = heartUnits * 5; } } @@ -14039,11 +14039,11 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyEffect"), 0)) { if (CVarGetInteger(CVAR_ENHANCEMENT("FairyPercentRestore"), 0)) { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = (gSaveContext.healthCapacity2) * CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 100) / 100; } else { - s32 heartUnits = CVarGetInteger("gLeveledHeartUnits", 4) << 2; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.healthAccumulator = CVarGetInteger(CVAR_ENHANCEMENT("FairyHealth"), 8) * heartUnits; } } else { 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 12081df81cf..2953c924753 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 @@ -4508,7 +4508,7 @@ void KaleidoScope_Update(PlayState* play) // Reset frame counter to prevent autosave on respawn play->gameplayFrames = 0; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK; - gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveledHeartUnits", 4) << 2; + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = MAGIC_STATE_IDLE; From a61d2ce44017856b71190d17371142317b1d6758 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 7 Sep 2024 12:17:30 -0600 Subject: [PATCH 10/18] Fix token experience multiplier --- soh/src/code/leveled_stat_math.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c index e7e43346b9b..0d388ad6e0b 100644 --- a/soh/src/code/leveled_stat_math.c +++ b/soh/src/code/leveled_stat_math.c @@ -179,9 +179,9 @@ u16 Leveled_GoldSkulltulaExperience(u8 tokens) { u8 i; for (i = 0; i < tokens; i++) { - experience += 5 + 5 * (u16)(CVarGetFloat("gLeveled.Difficulty.EXP.TokenRate", 1.0f) * (f32) i / (f32)10.0); + experience += 5 + 5 * i / (f32)10.0; } - return experience; + return (u16)((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.TokenRate", 1.0f)); } void Leveled_SetPlayerModifiedStats(Player* player) { From 37b431a41099aa80c45f546d9ee1ca2fcd63cddc Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 9 Sep 2024 12:48:19 -0600 Subject: [PATCH 11/18] Fix magic icon in level up popup. --- soh/src/code/leveled_overlays.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 3310643d71b..0006a3de569 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -434,7 +434,7 @@ void Actor_LevelUpDraw(PlayState* play, Actor* actor) { } if (i == 6) { - OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgQuestIconMagicJarBigTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgQuestIconMagicJarBigTex, 24, 24, (s16)spBC.x, (s16)spBC.y, 24, 24, 3072, 3072); } CLOSE_DISPS(play->state.gfxCtx); From 99f3a21be95e31cedf1b95a2782c3c464ab4adb9 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 9 Sep 2024 13:49:33 -0600 Subject: [PATCH 12/18] Add a global damage multiplier option the player takes. --- soh/soh/SohMenuBar.cpp | 45 +++++++++++++++++++++++--------------- soh/src/code/z_parameter.c | 3 +++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 36be765e82a..6414ab9dfcd 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -2087,26 +2087,35 @@ void DrawLeveledMenu() { if (ImGui::BeginMenu("Difficulty Options")) { - if (ImGui::BeginMenu("Heart Container Value in Units")) { - UIWidgets::EnhancementRadioButton("4 (x0.25)", "gLeveled.Difficulty.HeartUnits", 1); - UIWidgets::EnhancementRadioButton("8 (x0.5)", "gLeveled.Difficulty.HeartUnits", 2); - UIWidgets::EnhancementRadioButton("12 (x0.75)", "gLeveled.Difficulty.HeartUnits", 3); - UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveled.Difficulty.HeartUnits", 4); - UIWidgets::EnhancementRadioButton("20 (x1.25)", "gLeveled.Difficulty.HeartUnits", 5); - UIWidgets::EnhancementRadioButton("24 (x1.5)", "gLeveled.Difficulty.HeartUnits", 6); - UIWidgets::EnhancementRadioButton("28 (x1.75)", "gLeveled.Difficulty.HeartUnits", 7); - UIWidgets::EnhancementRadioButton("32 (x2)", "gLeveled.Difficulty.HeartUnits", 8); - UIWidgets::EnhancementRadioButton("48 (x3)", "gLeveled.Difficulty.HeartUnits", 12); - UIWidgets::EnhancementRadioButton("64 (x4)", "gLeveled.Difficulty.HeartUnits", 16); - UIWidgets::EnhancementRadioButton("80 (x5)", "gLeveled.Difficulty.HeartUnits", 20); - UIWidgets::EnhancementRadioButton("96 (x6)", "gLeveled.Difficulty.HeartUnits", 24); - UIWidgets::EnhancementRadioButton("112 (x7)", "gLeveled.Difficulty.HeartUnits", 28); - UIWidgets::EnhancementRadioButton("128 (x8)", "gLeveled.Difficulty.HeartUnits", 32); + if (ImGui::BeginMenu("Player Options")) { + if (ImGui::BeginMenu("Heart Container Value in Units")) { + UIWidgets::EnhancementRadioButton("4 (x0.25)", "gLeveled.Difficulty.HeartUnits", 1); + UIWidgets::EnhancementRadioButton("8 (x0.5)", "gLeveled.Difficulty.HeartUnits", 2); + UIWidgets::EnhancementRadioButton("12 (x0.75)", "gLeveled.Difficulty.HeartUnits", 3); + UIWidgets::EnhancementRadioButton("16 (Vanilla)", "gLeveled.Difficulty.HeartUnits", 4); + UIWidgets::EnhancementRadioButton("20 (x1.25)", "gLeveled.Difficulty.HeartUnits", 5); + UIWidgets::EnhancementRadioButton("24 (x1.5)", "gLeveled.Difficulty.HeartUnits", 6); + UIWidgets::EnhancementRadioButton("28 (x1.75)", "gLeveled.Difficulty.HeartUnits", 7); + UIWidgets::EnhancementRadioButton("32 (x2)", "gLeveled.Difficulty.HeartUnits", 8); + UIWidgets::EnhancementRadioButton("48 (x3)", "gLeveled.Difficulty.HeartUnits", 12); + UIWidgets::EnhancementRadioButton("64 (x4)", "gLeveled.Difficulty.HeartUnits", 16); + UIWidgets::EnhancementRadioButton("80 (x5)", "gLeveled.Difficulty.HeartUnits", 20); + UIWidgets::EnhancementRadioButton("96 (x6)", "gLeveled.Difficulty.HeartUnits", 24); + UIWidgets::EnhancementRadioButton("112 (x7)", "gLeveled.Difficulty.HeartUnits", 28); + UIWidgets::EnhancementRadioButton("128 (x8)", "gLeveled.Difficulty.HeartUnits", 32); + ImGui::EndMenu(); + } + 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."); + + + ImGui::Text("Damage Multiplier: %.2fx", (float)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f); + ImGui::SameLine(); + UIWidgets::EnhancementSliderInt("", "##LeveledPlayerDamageMultiplier", "gLeveled.Difficulty.Player.DamageMultiplier", 1, 32, "", 4, true, false); + UIWidgets::Tooltip("Sets a multiplier for the damage the player takes. Includes ALL sources, even damage while being frozen or burned.\nDamage cannot be reduced below 1."); ImGui::EndMenu(); } - 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::BeginMenu("EXP Options")) { UIWidgets::PaddedEnhancementSliderFloat("EXP Rate: %.1f %%", "##Leveled_EXPRate", "gLeveled.Difficulty.EXP.Rate", 0.0f, 10.0f, "", 1.0f, true, true, false, false); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 8a26e3be541..5c1e2bea783 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -3188,6 +3188,9 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } if (healthChange < 0) { + healthChange = (f32)healthChange * (f32)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f; + if (healthChange >= 0) + healthChange = -1; ActorDamageNumber_New(GET_PLAYER(play), -healthChange); } From dcf1a29fd4a63867d1ddac74f7b8e5b3c43086c5 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 9 Sep 2024 14:09:21 -0600 Subject: [PATCH 13/18] Add an option to change link's attack stat, and enemy HP multiplier. Does not change balance, just the numbers. --- soh/include/leveled_stat_math.h | 2 +- soh/soh/SohMenuBar.cpp | 2 ++ soh/src/code/leveled_overlays.c | 8 ++++---- soh/src/code/leveled_stat_math.c | 6 +++++- soh/src/code/z_collision_check.c | 2 +- soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 4 ++-- soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c | 2 +- soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c | 4 ++-- soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c | 2 +- soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 2 +- .../overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c | 4 ++-- soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c | 2 +- soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c | 2 +- soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c | 2 +- soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c | 2 +- soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c | 2 +- soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c | 2 +- soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c | 2 +- 18 files changed, 29 insertions(+), 23 deletions(-) diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h index 28251c41ec6..40dfa0c0326 100644 --- a/soh/include/leveled_stat_math.h +++ b/soh/include/leveled_stat_math.h @@ -1,6 +1,5 @@ #ifndef LEVELED_STAT_MATH_H #define LEVELED_STAT_MATH_H -#define HEALTH_ATTACK_MULTIPLIER 17 #include "z64.h" #ifdef __cplusplus @@ -25,6 +24,7 @@ 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); +u8 Leveled_GetHealthAttackMultiplier(); s8 Leveled_GetSceneLevel(s16 sceneId); #ifdef __cplusplus diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 6414ab9dfcd..4cac8c7c9e5 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -2082,6 +2082,8 @@ void DrawLeveledMenu() { ImGui::EndMenu(); } + UIWidgets::EnhancementSliderInt("Enemy HP and Link's Attack Multiplier: %dx", "##LeveledHPandAttackDamageMultiplier", "gLeveled.Enhancements.AttackAndHPMultiplier", 1, 32, "", 9, true, false); + UIWidgets::Tooltip("Changes Link's Attack and enemy HP multiplier. This doesn't change balance, but rather the size of numbers."); ImGui::EndMenu(); } diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 0006a3de569..99ed5780615 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -599,13 +599,13 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { Color_RGBA8 textColor = { 255, 255, 255, 255 }; if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 1) - attack = HEALTH_ATTACK_MULTIPLIER; + attack = Leveled_GetHealthAttackMultiplier(); if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 2) - attack = HEALTH_ATTACK_MULTIPLIER << 1; + attack = Leveled_GetHealthAttackMultiplier() << 1; if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 3) { - attack = HEALTH_ATTACK_MULTIPLIER << 2; + attack = Leveled_GetHealthAttackMultiplier() << 2; if (gBitFlags[3] & gSaveContext.inventory.equipment) - attack = HEALTH_ATTACK_MULTIPLIER; + attack = Leveled_GetHealthAttackMultiplier(); } // Values and Icons diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c index 0d388ad6e0b..fb620c44086 100644 --- a/soh/src/code/leveled_stat_math.c +++ b/soh/src/code/leveled_stat_math.c @@ -20,6 +20,10 @@ static u32 sExpTable[] = { 0, 30, 64, 105, 155, 217, 292, 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 };*/ +u8 Leveled_GetHealthAttackMultiplier() { + return CLAMP(CVarGetInteger("gLeveled.Enhancements.AttackAndHPMultiplier", 9), 1, 32); +} + u16 GetActorStat_DisplayAttack(u16 attack, u8 power) { return GetActorStat_Attack(attack, power) / (1 + (float)power / 30.0f); } @@ -49,7 +53,7 @@ u8 GetActorStat_PlayerCourage(u8 level) { } u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level){ - return (u16)(CLAMP((f32)GetActorStat_Attack(baseHealth * HEALTH_ATTACK_MULTIPLIER, GetActorStat_PlayerPower(level)) * CVarGetFloat("gLeveled.Difficulty.Enemy.HPPercent", 1.0f), 1, 0xffff)); + return (u16)(CLAMP((f32)GetActorStat_Attack(baseHealth * Leveled_GetHealthAttackMultiplier(), GetActorStat_PlayerPower(level)) * CVarGetFloat("gLeveled.Difficulty.Enemy.HPPercent", 1.0f), 1, 0xffff)); } u8 GetPlayerStat_BonusHearts(u8 level){ diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c index ff40cdc191d..4fd5e48b42d 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -3052,7 +3052,7 @@ void CollisionCheck_ApplyDamage(PlayState* play, CollisionCheckContext* colChkCt Actor* attacker = collider->ac; if (collider->actor->category != ACTORCAT_PLAYER) { - damage *= HEALTH_ATTACK_MULTIPLIER; + damage *= Leveled_GetHealthAttackMultiplier(); } else { damage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); } 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 bd4bfab0ea9..e5d8bc30105 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 @@ -728,7 +728,7 @@ 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); - u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); this->health -= damage; ActorDamageNumber_New(&this->actor, damage); @@ -1509,7 +1509,7 @@ void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { item1 = this->collider.elements[0].info.acHitInfo; 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); + swordDamage = Leveled_DamageModify(&this->actor, this->collider.base.actor, swordDamage * Leveled_GetHealthAttackMultiplier()); if (damage != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); 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 dc015e848b8..eed28b66c96 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,7 +1288,7 @@ void BossFd_CollisionCheck(BossFd* this, PlayState* play) { if (headCollider->info.bumperFlags & BUMP_HIT) { headCollider->info.bumperFlags &= ~BUMP_HIT; hurtbox = headCollider->info.acHitInfo; - u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); if (hurtbox->toucher.dmgFlags & 0x1000) damage <<= 1; ActorDamageNumber_New(&this->actor, damage); 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 d1558a6f150..66788efa652 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 @@ -845,7 +845,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { hurtbox = this->collider.elements[0].info.acHitInfo; if (!bossFd->faceExposed) { if (hurtbox->toucher.dmgFlags & 0x40000040) { - u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, damage); if (this->actor.colChkInfo.health > damage) { @@ -887,7 +887,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { if (hurtbox->toucher.dmgFlags & 0x80) { damage = 0; } - damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); if ((bossFd->actor.colChkInfo.health > 1) || canKill) { if (bossFd->actor.colChkInfo.health >= damage) { ActorDamageNumber_New(&this->actor, damage); 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 40225258a7d..07f2a6a665b 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 @@ -2794,7 +2794,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { hitWithSword = true; } - damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, damage); if (hitWithSword) { 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 ab5242c8de6..b65cc5d61df 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 @@ -1994,7 +1994,7 @@ void func_80902524(BossGanon2* this, PlayState* play) { } u8 baseDamage = phi_v1_2; - phi_v1_2 = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, phi_v1_2 * HEALTH_ATTACK_MULTIPLIER); + phi_v1_2 = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, phi_v1_2 * Leveled_GetHealthAttackMultiplier()); if (phi_v1_2 <= this->actor.colChkInfo.health) { this->actor.colChkInfo.health -= phi_v1_2; } else { 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 50601075e84..1a989e7e316 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 @@ -1245,7 +1245,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { dmg = CollisionCheck_GetSwordDamage(dmgFlags, play); (dmg == 0) ? (dmg = 2) : (canKill = true); - dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, dmg * HEALTH_ATTACK_MULTIPLIER); + dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, dmg * Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, dmg); if ((this->actor.colChkInfo.health > 2) || canKill) { if (dmg < this->actor.colChkInfo.health) { @@ -1277,7 +1277,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { this->work[GND_INVINC_TIMER] = 10; - u16 dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * HEALTH_ATTACK_MULTIPLIER); + u16 dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, dmg); if (dmg < this->actor.colChkInfo.health) { this->actor.colChkInfo.health -= dmg; 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 c713ffd490f..3d314fbdf7a 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 @@ -1833,7 +1833,7 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) { } else if (this->actionFunc == BossGoma_FloorStunned && (damage = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags, play)) != 0) { damage = Leveled_DamageModify(&this->actor, this->collider.elements[0].info.acHit->actor, - damage * HEALTH_ATTACK_MULTIPLIER); + damage * Leveled_GetHealthAttackMultiplier()); if (damage <= this->actor.colChkInfo.health) { this->actor.colChkInfo.health -= damage; } else { 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 bad0bd02be3..356336dde56 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 @@ -1782,7 +1782,7 @@ 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); + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, damage); this->actor.speedXZ = 15.0f; 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 483449680d1..a31fe0e5673 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 @@ -3095,7 +3095,7 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { swordDamage = true; } - damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * HEALTH_ATTACK_MULTIPLIER); + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, damage); if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) { 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 c7d43dcbf71..73925e0cc6e 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 @@ -454,7 +454,7 @@ 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; - u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, HEALTH_ATTACK_MULTIPLIER); + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); ActorDamageNumber_New(&this->actor, damage); if (damage > this->actor.colChkInfo.health) { diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index 58f3b3430db..1d52d6fb122 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -371,7 +371,7 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) { this->acceleration.z = Rand_CenteredFloat(15.0f); Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); - u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, HEALTH_ATTACK_MULTIPLIER); + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); if (damage <= this->actor.colChkInfo.health) { this->actor.colChkInfo.health -= damage; } else { 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 d29a808eb24..6c4cc787208 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 @@ -655,7 +655,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { swordDamage = 1; } - swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * HEALTH_ATTACK_MULTIPLIER); + swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * Leveled_GetHealthAttackMultiplier()); if (swordDamage <= this->actor.colChkInfo.health) { this->actor.colChkInfo.health -= swordDamage; } else { 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 e466bbafac4..f6036adbf4b 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 @@ -397,7 +397,7 @@ void EnVm_CheckHealth(EnVm* this, PlayState* play) { EnBom* bomb; if (Actor_GetCollidedExplosive(play, &this->colliderCylinder.base) != NULL) { - u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, HEALTH_ATTACK_MULTIPLIER); + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); this->actor.colChkInfo.damage += damage; ActorDamageNumber_New(&this->actor, damage); Actor_ApplyDamage(&this->actor); From adbb1c5ed28b9db2ef63a0c07d9e1c3d60d00439 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 24 Sep 2024 15:27:35 -0600 Subject: [PATCH 14/18] Strength upgrades now increase Power. Bracelets, Silver Gaunt, Gold Gaunt increase Power by 1/2/3 respectively. --- soh/src/code/leveled_overlays.c | 15 +++++++-------- soh/src/code/leveled_stat_math.c | 2 ++ soh/src/overlays/gamestates/ovl_title/z_title.c | 5 +++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 99ed5780615..2141dfcdc68 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -617,19 +617,18 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { statY += 10; // Health Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); - Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + (gSaveContext.healthCapacity2 >= 1000 ? 35 : 29), - statY - 1, 8, 9, 255, 255, 255); - u8 healthValX = 10; - Leveled_ValueNumberDraw(play, statX + healthValX, statY, gSaveContext.health, 255, 255, 255); - healthValX = gSaveContext.healthCapacity2 >= 1000 ? 40 : 34; - Leveled_ValueNumberDraw(play, statX + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); + u8 healthValX = gSaveContext.healthCapacity2 >= 1000 ? 12 : gSaveContext.healthCapacity2 >= 100 ? 6 : 0; + Leveled_ValueNumberDraw(play, statX + 10 + healthValX, statY, gSaveContext.health, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; // Magic if (gSaveContext.magicCapacity > 0) { + healthValX = gSaveContext.magicCapacity >= 100 ? 6 : 0; Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 24, 24, statX + 2, statY, 14, 14); - Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 29, statY - 1, 8, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); - Leveled_ValueNumberDraw(play, statX + 34, statY, gSaveContext.magicCapacity, 120, 255, 0); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.magicCapacity, 120, 255, 0); statY += 8; } // Attack diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c index fb620c44086..e1ae153d9bc 100644 --- a/soh/src/code/leveled_stat_math.c +++ b/soh/src/code/leveled_stat_math.c @@ -258,6 +258,8 @@ void Leveled_SetPlayerModifiedStats(Player* player) { default: break; } + + powerModifier += Player_GetStrength(); } player->actor.powerModifier = powerModifier; diff --git a/soh/src/overlays/gamestates/ovl_title/z_title.c b/soh/src/overlays/gamestates/ovl_title/z_title.c index 8893d9d5f5c..fa5cd90ad41 100644 --- a/soh/src/overlays/gamestates/ovl_title/z_title.c +++ b/soh/src/overlays/gamestates/ovl_title/z_title.c @@ -34,6 +34,11 @@ void Title_PrintBuildInfo(Gfx** gfxp) { //if tag is empty (not a release build) bool showGitInfo = gGitCommitTag[0] == 0; + GfxPrint_SetPos(&printer, 1, 21); + GfxPrint_Printf(&printer, "Leveled Mod Version: 1.1.2.1"); + GfxPrint_SetPos(&printer, 1, 22); + GfxPrint_Printf(&printer, "A Mod By Gotest"); + if (showGitInfo) { GfxPrint_SetPos(&printer, 1, 24); GfxPrint_Printf(&printer, "Git Branch: %s", gGitBranch); From f2f854b1fe90ffea7b67575712bf72a0e833e81d Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 24 Sep 2024 15:38:50 -0600 Subject: [PATCH 15/18] text correction --- soh/src/overlays/gamestates/ovl_title/z_title.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/overlays/gamestates/ovl_title/z_title.c b/soh/src/overlays/gamestates/ovl_title/z_title.c index fa5cd90ad41..cd39960db45 100644 --- a/soh/src/overlays/gamestates/ovl_title/z_title.c +++ b/soh/src/overlays/gamestates/ovl_title/z_title.c @@ -37,7 +37,7 @@ void Title_PrintBuildInfo(Gfx** gfxp) { GfxPrint_SetPos(&printer, 1, 21); GfxPrint_Printf(&printer, "Leveled Mod Version: 1.1.2.1"); GfxPrint_SetPos(&printer, 1, 22); - GfxPrint_Printf(&printer, "A Mod By Gotest"); + GfxPrint_Printf(&printer, "A Mod By Gotest, AKA Arrenton"); if (showGitInfo) { GfxPrint_SetPos(&printer, 1, 24); From 3653b2404271d580cce410fc4294a0adc98abfeb Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 24 Sep 2024 16:54:06 -0600 Subject: [PATCH 16/18] Fix health number position. --- soh/src/code/leveled_overlays.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 2141dfcdc68..2a3baec0925 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -618,7 +618,7 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { // Health Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); u8 healthValX = gSaveContext.healthCapacity2 >= 1000 ? 12 : gSaveContext.healthCapacity2 >= 100 ? 6 : 0; - Leveled_ValueNumberDraw(play, statX + 10 + healthValX, statY, gSaveContext.health, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; From 67f60e4d90c1338da60b0773d7579162730dcfbb Mon Sep 17 00:00:00 2001 From: Gotest Date: Tue, 31 Dec 2024 13:48:03 -0700 Subject: [PATCH 17/18] Update to regenerate executable --- soh/src/overlays/gamestates/ovl_title/z_title.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/overlays/gamestates/ovl_title/z_title.c b/soh/src/overlays/gamestates/ovl_title/z_title.c index cd39960db45..5c3b96817e5 100644 --- a/soh/src/overlays/gamestates/ovl_title/z_title.c +++ b/soh/src/overlays/gamestates/ovl_title/z_title.c @@ -35,7 +35,7 @@ void Title_PrintBuildInfo(Gfx** gfxp) { bool showGitInfo = gGitCommitTag[0] == 0; GfxPrint_SetPos(&printer, 1, 21); - GfxPrint_Printf(&printer, "Leveled Mod Version: 1.1.2.1"); + GfxPrint_Printf(&printer, "Leveled Mod Version: 1.1.2.2"); GfxPrint_SetPos(&printer, 1, 22); GfxPrint_Printf(&printer, "A Mod By Gotest, AKA Arrenton"); From d1931b9bae634d524f41c235e4ed44e5cdc7584b Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 21 Jan 2025 23:34:01 -0700 Subject: [PATCH 18/18] Added health check for Guay when they take damage. HP status text now turns red orange when low HP. --- soh/src/code/leveled_overlays.c | 10 +++++++++- soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 2a3baec0925..47b6e81af4a 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -618,7 +618,11 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { // Health Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); u8 healthValX = gSaveContext.healthCapacity2 >= 1000 ? 12 : gSaveContext.healthCapacity2 >= 100 ? 6 : 0; - Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); + if (HealthMeter_IsCritical()) { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 127, 0); + } else { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); + } Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; @@ -657,6 +661,10 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { textColor.r = 255; textColor.g = 0; textColor.b = 0; + } else { + textColor.r = 255; + textColor.g = 255; + textColor.b = 255; } Leveled_DrawTex32(play, dgItemIconShieldHylianTex, 32, 32, statX + 2, statY, 16, 16); Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.courage + player->actor.courageModifier, 0, 255), textColor.r, textColor.g, textColor.b); diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c index bb7236e045f..b0e2f8612dd 100644 --- a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c @@ -412,7 +412,7 @@ void EnCrow_Respawn(EnCrow* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, target, target * 0.1f)) { this->actor.flags |= ACTOR_FLAG_TARGETABLE; this->actor.flags &= ~ACTOR_FLAG_UPDATE_WHILE_CULLED; - this->actor.colChkInfo.health = 1; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(1, this->actor.level); EnCrow_SetupFlyIdle(this); } this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; @@ -427,10 +427,17 @@ void EnCrow_UpdateDamage(EnCrow* this, PlayState* play) { if (this->actor.colChkInfo.damageEffect == 1) { // Deku Nuts EnCrow_SetupTurnAway(this); } else { - Actor_ApplyDamage(&this->actor); - this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; - Enemy_StartFinishingBlow(play, &this->actor); - EnCrow_SetupDamaged(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + Enemy_StartFinishingBlow(play, &this->actor); + EnCrow_SetupDamaged(this, play); + } else { + EnCrow_SetupFlyIdle(this); + this->timer = (s16)Rand_ZeroFloat(25) + 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_DAMAGE); + this->actor.flags |= ACTOR_FLAG_UPDATE_WHILE_CULLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); + } } } }