From 1ab44d853c257d3dec1647ddd572d8049758a0e5 Mon Sep 17 00:00:00 2001 From: Frankie-hz <105882754+Frankie-hz@users.noreply.github.com> Date: Mon, 11 May 2026 19:06:24 -0400 Subject: [PATCH] [cpp] Fixes underflow in avatar perpetuation --- sql/pet_list.sql | 2 +- src/map/status_effect_container.cpp | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/sql/pet_list.sql b/sql/pet_list.sql index f6df52bdf5b..5511cdd9d55 100644 --- a/sql/pet_list.sql +++ b/sql/pet_list.sql @@ -106,7 +106,7 @@ INSERT INTO `pet_list` VALUES (72,'StormwakerFrame',5127,1,99,0,0,3); -- INSERT INTO `pet_list` VALUES (73, 'AdventuringFellow', 0, 1, 99, 0, 0); -- 74 is Chocobo in the enum.. INSERT INTO `pet_list` VALUES (75,'Luopan',6040,1,99,0,0,0); -INSERT INTO `pet_list` VALUES (76,'Siren',7047,1,99,0,0,2); +INSERT INTO `pet_list` VALUES (76,'Siren',7047,1,99,0,3,2); INSERT INTO `pet_list` VALUES (77,'SweetCaroline',7500,99,119,7200,0,3); INSERT INTO `pet_list` VALUES (78,'AmiableRoche',7501,99,110,7200,0,2); -- nat max 110, merit max 110 INSERT INTO `pet_list` VALUES (79,'HeadbreakerKen',7502,99,115,7200,0,2); -- nat max 115, merit max 115 diff --git a/src/map/status_effect_container.cpp b/src/map/status_effect_container.cpp index 54e76549d4e..e42a85bf3d6 100644 --- a/src/map/status_effect_container.cpp +++ b/src/map/status_effect_container.cpp @@ -2163,16 +2163,29 @@ void CStatusEffectContainer::TickRegen(timer::time_point tick) { CPetEntity* PPet = (CPetEntity*)m_POwner->PPet; ELEMENT petElement = static_cast(PPet->m_Element); - uint8 petElementIdx = static_cast(petElement) - 1; + bool elementValid = petElement >= ELEMENT_FIRE && petElement <= ELEMENT_DARK; // Check if the element is not 0 (None) or out of bounds + uint8 petElementIdx = 0; ELEMENT dayElement = battleutils::GetDayElement(); auto weather = battleutils::GetWeather(PChar, false); + if (!elementValid) + { + ShowWarning("CStatusEffectContainer::TickRegen() - Pet %s (PetID %u) has invalid element %u for avatar perpetuation. Check pet_list.sql.", + PPet->getName(), + PPet->m_PetID, + PPet->m_Element); + } + else + { + petElementIdx = static_cast(petElement) - 1; + } + static const Mod strong[8] = { Mod::FIRE_AFFINITY_PERP, Mod::ICE_AFFINITY_PERP, Mod::WIND_AFFINITY_PERP, Mod::EARTH_AFFINITY_PERP, Mod::THUNDER_AFFINITY_PERP, Mod::WATER_AFFINITY_PERP, Mod::LIGHT_AFFINITY_PERP, Mod::DARK_AFFINITY_PERP }; static const Weather weatherStrong[8] = { Weather::HotSpell, Weather::Snow, Weather::Wind, Weather::DustStorm, Weather::Thunder, Weather::Rain, Weather::Auroras, Weather::Gloom }; // Day / Weather elemental matches. - bool dayMatch = dayElement == petElement; - bool weatherMatch = weather == weatherStrong[petElementIdx] || weather == static_cast(static_cast(weatherStrong[petElementIdx]) + 1); + bool dayMatch = elementValid && dayElement == petElement; + bool weatherMatch = elementValid && (weather == weatherStrong[petElementIdx] || weather == static_cast(static_cast(weatherStrong[petElementIdx]) + 1)); // Halve perpetuation cost before all regular reductions. bool halfFromCarby = PChar->getMod(Mod::HALF_PERPETUATION_CARBUNCLE) != 0 && PPet->m_PetID == PETID_CARBUNCLE; @@ -2188,7 +2201,10 @@ void CStatusEffectContainer::TickRegen(timer::time_point tick) perpetuationCost = perpetuationCost - PChar->getMod(Mod::PERPETUATION_REDUCTION); // Apply elemental affinity perpetuation bonus/penalty. - perpetuationCost = perpetuationCost - PChar->getMod(strong[petElementIdx]); + if (elementValid) + { + perpetuationCost = perpetuationCost - PChar->getMod(strong[petElementIdx]); + } // Apply day element perpetuation reduction. if (dayMatch)