From 65c993c17ccf6bf2f0042c4fa0fbf62cdd1a83c8 Mon Sep 17 00:00:00 2001 From: sootstains Date: Sat, 15 Nov 2025 02:21:04 -0500 Subject: [PATCH 1/8] Added 6 new parameters to gameplaymodifiers.json using existing functions in mod_tools.hpp. The first 3 parameters allow modders to change player AC effectiveness. - "playerACEpassive" modifies the player's AC effectiveness when they are not defending (default 75%) - "playerACEactive" modifies the player's AC effectiveness while blocking (default 100%) - "playerACEbless" modifies the impact each armor blessing has on AC effectiveness (default 2.5%) The latter 3 parameters allow modders to enable a conditional kill XP modifier, based on the level difference between source and recipient. - "doConditionalXPModifier" is a boolean that determines whether this system is used at all. - "conditionalXPModLvlThreshold" determines the level difference required for the XP modifier to be applied. - "conditionalXPModPercent" determines the XP modifier applied. --- src/entity.cpp | 25 ++++++++++++++++++++++++- src/mod_tools.hpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/entity.cpp b/src/entity.cpp index 2a764de9a..862331387 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -5453,7 +5453,15 @@ real_t Entity::getACEffectiveness(Entity* my, Stat* myStats, bool isPlayer, Enti if ( myStats->defending ) { - return 1.0; + if ( gameplayCustomManager.inUse() && my->behavior == &actPlayer ) + { + double playerACEffActive = gameplayCustomManager.playerACEactive / 100.0; + return std::max(0.0, std::min(1.0, playerACEffActive)); + } + else + { + return 1.0; + } } int blessings = 0; @@ -5496,6 +5504,13 @@ real_t Entity::getACEffectiveness(Entity* my, Stat* myStats, bool isPlayer, Enti blessings += cursedItemIsBuff ? abs(myStats->amulet->beatitude) : myStats->amulet->beatitude; } outNumBlessings = blessings; + + if ( gameplayCustomManager.inUse() && my->behavior == &actPlayer ) + { + double playerACEffPassive = gameplayCustomManager.playerACEpassive / 100.0; + double blessingModifier = gameplayCustomManager.playerACEbless / 100.0; + return std::max(0.0, std::min(1.0, playerACEffPassive + blessingModifier * blessings)); + } return std::max(0.0, std::min(1.0, .75 + 0.025 * blessings)); } @@ -12964,6 +12979,14 @@ void Entity::awardXP(Entity* src, bool share, bool root) if ( gameplayCustomManager.inUse() ) { xpGain = (gameplayCustomManager.globalXPPercent / 100.f) * xpGain; + + if ( gameplayCustomManager.doConditionalXPModifier ) + { + if ( destStats->LVL - srcStats->LVL >= gameplayCustomManager.conditionalXPModLvlThreshold ) // check if level difference exceeds level threshold + { + xpGain = (gameplayCustomManager.conditionalXPModPercent / 100.f) * xpGain; + } + } } else if ( gameModeManager.currentSession.challengeRun.isActive() && gameModeManager.currentSession.challengeRun.globalXPPercent != 100 ) diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index bd95e51b9..0fe10f94e 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -1855,6 +1855,12 @@ class GameplayCustomManager bool minimapShareProgress = false; int playerWeightPercent = 100; double playerSpeedMax = 12.5; + int playerACEpassive = 75; + int playerACEactive = 100; + double playerACEbless = 2.5; + bool doConditionalXPModifier = false; + int conditionalXPModLvlThreshold = 0; + int conditionalXPModPercent = 100; inline bool inUse() { return usingCustomManager; }; void resetValues() { @@ -1865,6 +1871,12 @@ class GameplayCustomManager minimapShareProgress = false; playerWeightPercent = 100; playerSpeedMax = 12.5; + playerACEpassive = 75; + playerACEactive = 100; + playerACEbless = 2.5; + doConditionalXPModifier = false; + conditionalXPModLvlThreshold = 0; + conditionalXPModPercent = 100; minotaurForceEnableFloors.first.clear(); minotaurForceEnableFloors.second.clear(); @@ -1932,6 +1944,12 @@ class GameplayCustomManager CustomHelpers::addMemberToRoot(d, "player_share_minimap_progress", rapidjson::Value(minimapShareProgress)); CustomHelpers::addMemberToRoot(d, "player_speed_weight_impact_percent", rapidjson::Value(playerWeightPercent)); CustomHelpers::addMemberToRoot(d, "player_speed_max", rapidjson::Value(playerSpeedMax)); + CustomHelpers::addMemberToRoot(d, "player_AC_eff_passive_percent", rapidjson::Value(playerACEpassive)); + CustomHelpers::addMemberToRoot(d, "player_AC_eff_block_percent", rapidjson::Value(playerACEactive)); + CustomHelpers::addMemberToRoot(d, "player_AC_eff_blessing_percent", rapidjson::Value(playerACEbless)); + CustomHelpers::addMemberToRoot(d, "enable_conditional_xp_modifier", rapidjson::Value(doConditionalXPModifier)); + CustomHelpers::addMemberToRoot(d, "conditional_mod_lvl_difference_threshold", rapidjson::Value(conditionalXPModLvlThreshold)); + CustomHelpers::addMemberToRoot(d, "conditional_xp_modifier_percent", rapidjson::Value(conditionalXPModPercent)); rapidjson::Value obj(rapidjson::kObjectType); rapidjson::Value arr(rapidjson::kArrayType); @@ -2126,6 +2144,30 @@ class GameplayCustomManager playerSpeedMax = itr->value.GetDouble(); return true; } + else if ( name.compare("player_AC_eff_passive_percent") == 0 ) + { + playerACEpassive = itr->value.GetInt(); + } + else if ( name.compare("player_AC_eff_block_percent") == 0 ) + { + playerACEactive = itr->value.GetInt(); + } + else if ( name.compare("player_AC_eff_blessing_percent") == 0 ) + { + playerACEbless = itr->value.GetDouble(); + } + else if ( name.compare("enable_conditional_xp_modifier") == 0 ) + { + doConditionalXPModifier = itr->value.GetBool(); + } + else if ( name.compare("conditional_mod_lvl_difference_threshold") == 0 ) + { + conditionalXPModLvlThreshold = itr->value.GetInt(); + } + else if ( name.compare("conditional_xp_modifier_percent") == 0 ) + { + conditionalXPModPercent = itr->value.GetInt(); + } else if ( name.compare("minotaur_force_disable_on_floors") == 0 ) { for ( rapidjson::Value::ConstValueIterator arr_itr = itr->value["normal_floors"].Begin(); arr_itr != itr->value["normal_floors"].End(); ++arr_itr ) From f76ea635c31b838c324e882e2931ccfdbbeb24b0 Mon Sep 17 00:00:00 2001 From: sootstains Date: Sun, 16 Nov 2025 01:26:52 -0500 Subject: [PATCH 2/8] Added 11 new parameters for json modding Added 11 new parameters for gameplaymodifiers json using existing functions in mod_tools.hpp. The first 6 parameters allow modders to apply multipliers to each of the player's core stats in the various statGet functions. The next two parameters allow modders to modify the base amount of HP and MP (current and max) the player gains upon leveling up. The last three parameters introduce a new, optional mechanic for modders to play with: trauma damage. When enabled, a portion of damage taken is applied to the player's max HP, in addition to reducing their HP normally. - doTraumaDamage is a boolean that determines whether this system is used at all. (default false) - traumaDamagePercent determines the portion of damage dealt as trauma damage. (default 0%) - traumaDamageHPLimit determines the minimum value trauma damage can reduce a player's max HP to. (default 25) --- src/entity.cpp | 64 ++++++++++++++++++++++++++++++++++-- src/mod_tools.hpp | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index 862331387..e1ec72d1e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -2528,6 +2528,19 @@ void Entity::modHP(int amount) { Compendium_t::Events_t::eventUpdateCodex(skill[2], Compendium_t::CPDM_HP_LOST_RUN, "hp", oldHP - entitystats->HP); Compendium_t::Events_t::eventUpdateCodex(skill[2], Compendium_t::CPDM_HP_LOST_TOTAL, "hp", oldHP - entitystats->HP); + + if ( gameplayCustomManager.inUse() && gameplayCustomManager.doTraumaDamage ) // damage maxHP based on settings + { + double damage = oldHP - entitystats->HP; + double percent = ( (double)gameplayCustomManager.traumaDamagePercent / 100.0 ); + double traumaDmg = percent * damage; + int oldMaxHP = entitystats->MAXHP; + + entitystats->MAXHP -= traumaDmg; + entitystats->MAXHP = std::min(oldMaxHP, std::max( gameplayCustomManager.traumaDamageHPLimit, entitystats->MAXHP ) ); + entitystats->HP = std::min(entitystats->HP, entitystats->MAXHP); + // prevent maxHP from dropping below minimum, don't increase maxHP by mistake, don't allow HP to exceed maxHP + } } } } @@ -3141,13 +3154,29 @@ void Entity::handleEffects(Stat* myStats) static ConsoleVariable cvar_lvlup_ally_sfx("/lvlup_ally_sfx", 520); // increase MAXHP/MAXMP - myStats->MAXHP += HP_MOD; + if ( gameplayCustomManager.inUse() && behavior == &actPlayer ) + { + myStats->MAXHP += gameplayCustomManager.playerLevelupHP; + } + else + { + myStats->MAXHP += HP_MOD; + } modHP(getHPRestoreOnLevelUp()); myStats->HP = std::min(myStats->HP, myStats->MAXHP); if ( !(behavior == &actMonster && monsterAllySummonRank != 0) ) { - myStats->MP += MP_MOD; - myStats->MAXMP += MP_MOD; + if ( gameplayCustomManager.inUse() && behavior == &actPlayer) + { + myStats->MP += gameplayCustomManager.playerLevelupMP; + myStats->MAXMP += gameplayCustomManager.playerLevelupMP; + } + else + { + myStats->MP += MP_MOD; + myStats->MAXMP += MP_MOD; + } + if ( behavior == &actPlayer && myStats->playerRace == RACE_INSECTOID && myStats->stat_appearance == 0 ) { myStats->MAXMP = std::min(100, myStats->MAXMP); @@ -5851,6 +5880,10 @@ Sint32 statGetSTR(Stat* entitystats, Entity* my) break; } } + if ( gameplayCustomManager.inUse() && my && my->behavior == &actPlayer ) + { + STR *= gameplayCustomManager.playerMultiplierSTR; + } return STR; } @@ -6099,6 +6132,10 @@ Sint32 statGetDEX(Stat* entitystats, Entity* my) { DEX += 8; } + if ( gameplayCustomManager.inUse() && my && my->behavior == &actPlayer ) + { + DEX *= gameplayCustomManager.playerMultiplierDEX; + } return DEX; } @@ -6206,6 +6243,10 @@ Sint32 statGetCON(Stat* entitystats, Entity* my) percentHP = 100 - percentHP; CON += percentHP / 10; } + if ( gameplayCustomManager.inUse() && my && my->behavior == &actPlayer ) + { + CON *= gameplayCustomManager.playerMultiplierCON; + } return CON; } @@ -6313,6 +6354,10 @@ Sint32 statGetINT(Stat* entitystats, Entity* my) { INT -= std::max(8, static_cast(INT * 0.25)); } + if ( gameplayCustomManager.inUse() && my && my->behavior == &actPlayer ) + { + INT *= gameplayCustomManager.playerMultiplierINT; + } return INT; } @@ -6479,6 +6524,10 @@ Sint32 statGetPER(Stat* entitystats, Entity* my) { PER -= std::max(5, PER / 2); } + if ( gameplayCustomManager.inUse() && my && my->behavior == &actPlayer ) + { + PER *= gameplayCustomManager.playerMultiplierPER; + } return PER; } @@ -6570,6 +6619,10 @@ Sint32 statGetCHR(Stat* entitystats, Entity* my) { CHR += std::max(4, static_cast(CHR * .25)); } + if ( gameplayCustomManager.inUse() && my && my->behavior == &actPlayer ) + { + CHR *= gameplayCustomManager.playerMultiplierCHR; + } return CHR; } @@ -23136,6 +23189,11 @@ int Entity::getHPRestoreOnLevelUp() { int hpMod = HP_MOD; + if ( gameplayCustomManager.inUse() && behavior == &actPlayer ) + { + hpMod = gameplayCustomManager.playerLevelupHP; + } + if ( Stat* myStats = getStats() ) { if ( myStats->helmet && myStats->helmet->type == HAT_CROWN ) diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index 0fe10f94e..76b3eb731 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -1861,6 +1861,17 @@ class GameplayCustomManager bool doConditionalXPModifier = false; int conditionalXPModLvlThreshold = 0; int conditionalXPModPercent = 100; + double playerMultiplierSTR = 1.0; + double playerMultiplierDEX = 1.0; + double playerMultiplierCON = 1.0; + double playerMultiplierINT = 1.0; + double playerMultiplierPER = 1.0; + double playerMultiplierCHR = 1.0; + int playerLevelupHP = 5; + int playerLevelupMP = 5; + bool doTraumaDamage = false; + int traumaDamagePercent = 0; + int traumaDamageHPLimit = 25; inline bool inUse() { return usingCustomManager; }; void resetValues() { @@ -1877,6 +1888,17 @@ class GameplayCustomManager doConditionalXPModifier = false; conditionalXPModLvlThreshold = 0; conditionalXPModPercent = 100; + playerMultiplierSTR = 1.0; + playerMultiplierDEX = 1.0; + playerMultiplierCON = 1.0; + playerMultiplierINT = 1.0; + playerMultiplierPER = 1.0; + playerMultiplierCHR = 1.0; + playerLevelupHP = 5; + playerLevelupMP = 5; + doTraumaDamage = false; + traumaDamagePercent = 0; + traumaDamageHPLimit = 25; minotaurForceEnableFloors.first.clear(); minotaurForceEnableFloors.second.clear(); @@ -1944,13 +1966,29 @@ class GameplayCustomManager CustomHelpers::addMemberToRoot(d, "player_share_minimap_progress", rapidjson::Value(minimapShareProgress)); CustomHelpers::addMemberToRoot(d, "player_speed_weight_impact_percent", rapidjson::Value(playerWeightPercent)); CustomHelpers::addMemberToRoot(d, "player_speed_max", rapidjson::Value(playerSpeedMax)); + CustomHelpers::addMemberToRoot(d, "player_AC_eff_passive_percent", rapidjson::Value(playerACEpassive)); CustomHelpers::addMemberToRoot(d, "player_AC_eff_block_percent", rapidjson::Value(playerACEactive)); CustomHelpers::addMemberToRoot(d, "player_AC_eff_blessing_percent", rapidjson::Value(playerACEbless)); + CustomHelpers::addMemberToRoot(d, "enable_conditional_xp_modifier", rapidjson::Value(doConditionalXPModifier)); CustomHelpers::addMemberToRoot(d, "conditional_mod_lvl_difference_threshold", rapidjson::Value(conditionalXPModLvlThreshold)); CustomHelpers::addMemberToRoot(d, "conditional_xp_modifier_percent", rapidjson::Value(conditionalXPModPercent)); + CustomHelpers::addMemberToRoot(d, "player_STR_multiplier", rapidjson::Value(playerMultiplierSTR)); + CustomHelpers::addMemberToRoot(d, "player_DEX_multiplier", rapidjson::Value(playerMultiplierDEX)); + CustomHelpers::addMemberToRoot(d, "player_CON_multiplier", rapidjson::Value(playerMultiplierCON)); + CustomHelpers::addMemberToRoot(d, "player_INT_multiplier", rapidjson::Value(playerMultiplierINT)); + CustomHelpers::addMemberToRoot(d, "player_PER_multiplier", rapidjson::Value(playerMultiplierPER)); + CustomHelpers::addMemberToRoot(d, "player_CHR_multiplier", rapidjson::Value(playerMultiplierCHR)); + + CustomHelpers::addMemberToRoot(d, "player_levelup_HP", rapidjson::Value(playerLevelupHP)); + CustomHelpers::addMemberToRoot(d, "player_levelup_MP", rapidjson::Value(playerLevelupMP)); + + CustomHelpers::addMemberToRoot(d, "enable_player_trauma_damage", rapidjson::Value(doTraumaDamage)); + CustomHelpers::addMemberToRoot(d, "trauma_damage_percent", rapidjson::Value(traumaDamagePercent)); + CustomHelpers::addMemberToRoot(d, "trauma_damage_hp_floor", rapidjson::Value(traumaDamageHPLimit)); + rapidjson::Value obj(rapidjson::kObjectType); rapidjson::Value arr(rapidjson::kArrayType); CustomHelpers::addMemberToRoot(d, "minotaur_force_disable_on_floors", obj); @@ -2168,6 +2206,50 @@ class GameplayCustomManager { conditionalXPModPercent = itr->value.GetInt(); } + else if ( name.compare("player_STR_multiplier") == 0 ) + { + playerMultiplierSTR = itr->value.GetDouble(); + } + else if ( name.compare("player_DEX_multiplier") == 0 ) + { + playerMultiplierDEX = itr->value.GetDouble(); + } + else if ( name.compare("player_CON_multiplier") == 0 ) + { + playerMultiplierCON = itr->value.GetDouble(); + } + else if ( name.compare("player_INT_multiplier") == 0 ) + { + playerMultiplierINT = itr->value.GetDouble(); + } + else if ( name.compare("player_PER_multiplier") == 0 ) + { + playerMultiplierPER = itr->value.GetDouble(); + } + else if ( name.compare("player_CHR_multiplier") == 0 ) + { + playerMultiplierCHR = itr->value.GetDouble(); + } + else if ( name.compare("player_levelup_HP") == 0 ) + { + playerLevelupHP = itr->value.GetInt(); + } + else if ( name.compare("player_levelup_MP") == 0 ) + { + playerLevelupMP = itr->value.GetInt(); + } + else if ( name.compare("enable_player_trauma_damage") == 0 ) + { + doTraumaDamage = itr->value.GetBool(); + } + else if ( name.compare("trauma_damage_percent") == 0 ) + { + traumaDamagePercent = itr->value.GetInt(); + } + else if ( name.compare("trauma_damage_hp_floor") == 0 ) + { + traumaDamageHPLimit = itr->value.GetInt(); + } else if ( name.compare("minotaur_force_disable_on_floors") == 0 ) { for ( rapidjson::Value::ConstValueIterator arr_itr = itr->value["normal_floors"].Begin(); arr_itr != itr->value["normal_floors"].End(); ++arr_itr ) From 408d5f8a4f31fb48716343815123e946394805b4 Mon Sep 17 00:00:00 2001 From: sootstains Date: Mon, 17 Nov 2025 01:52:37 -0500 Subject: [PATCH 3/8] Added parameter for ZAP Brigade spawn rate Added one parameter for gameplaymodifiers json using existing functions in mod_tools.hpp. zapBrigadeSpawnPercent allows modders to control the spawn rate of the ZAP Brigade. Previously, it was only possible to control the presence of the minotaur - not the ZAP Brigade. I slightly altered actMinotaurTimer's ZAP Brigade roll in order to achieve this efficiently. The rate should be approximately the same in vanilla, but the individual results across thousands of seeds may be slightly different. --- src/mod_tools.hpp | 8 ++++++++ src/monster_minotaur.cpp | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index 76b3eb731..750be0f50 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -1872,6 +1872,7 @@ class GameplayCustomManager bool doTraumaDamage = false; int traumaDamagePercent = 0; int traumaDamageHPLimit = 25; + int zapBrigadeSpawnPercent = 20; inline bool inUse() { return usingCustomManager; }; void resetValues() { @@ -1899,6 +1900,7 @@ class GameplayCustomManager doTraumaDamage = false; traumaDamagePercent = 0; traumaDamageHPLimit = 25; + zapBrigadeSpawnPercent = 20; minotaurForceEnableFloors.first.clear(); minotaurForceEnableFloors.second.clear(); @@ -1989,6 +1991,8 @@ class GameplayCustomManager CustomHelpers::addMemberToRoot(d, "trauma_damage_percent", rapidjson::Value(traumaDamagePercent)); CustomHelpers::addMemberToRoot(d, "trauma_damage_hp_floor", rapidjson::Value(traumaDamageHPLimit)); + CustomHelpers::addMemberToRoot(d,"zap_brigade_spawnrate_percent", rapidjson::Value(zapBrigadeSpawnPercent)); + rapidjson::Value obj(rapidjson::kObjectType); rapidjson::Value arr(rapidjson::kArrayType); CustomHelpers::addMemberToRoot(d, "minotaur_force_disable_on_floors", obj); @@ -2250,6 +2254,10 @@ class GameplayCustomManager { traumaDamageHPLimit = itr->value.GetInt(); } + else if ( name.compare("zap_brigade_spawnrate_percent") == 0 ) + { + zapBrigadeSpawnPercent = itr->value.GetInt(); + } else if ( name.compare("minotaur_force_disable_on_floors") == 0 ) { for ( rapidjson::Value::ConstValueIterator arr_itr = itr->value["normal_floors"].Begin(); arr_itr != itr->value["normal_floors"].End(); ++arr_itr ) diff --git a/src/monster_minotaur.cpp b/src/monster_minotaur.cpp index 0178bc000..edb4cb185 100644 --- a/src/monster_minotaur.cpp +++ b/src/monster_minotaur.cpp @@ -687,9 +687,17 @@ void actMinotaurTimer(Entity* my) if ( !my ) { return; } auto& rng = my->entity_rng ? *my->entity_rng : local_rng; + int zapChance = 20; + if ( gameplayCustomManager.inUse() ) + { + zapChance = gameplayCustomManager.zapBrigadeSpawnPercent; + zapChance = std::min(100, zapChance); + zapChance = std::max(0, zapChance); + } + MINOTAURTIMER_LIFE++; if ( MINOTAURTIMER_LIFE == (getMinotaurTimeToArrive() - (TICKS_PER_SECOND * 30)) - && rng.rand() % 5 == 0 ) + && rng.rand() % 100 < zapChance ) { int c; bool spawnedsomebody = false; From bef6a8da728fe3a658ae2ddef5bb0c4c0e75b558 Mon Sep 17 00:00:00 2001 From: sootstains Date: Mon, 17 Nov 2025 02:25:09 -0500 Subject: [PATCH 4/8] Updated AC effectiveness modifiers New logic allows for active AC effectiveness values less than 1 to benefit from blessings --- src/entity.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index e1ec72d1e..259bbd5e3 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -5482,12 +5482,7 @@ real_t Entity::getACEffectiveness(Entity* my, Stat* myStats, bool isPlayer, Enti if ( myStats->defending ) { - if ( gameplayCustomManager.inUse() && my->behavior == &actPlayer ) - { - double playerACEffActive = gameplayCustomManager.playerACEactive / 100.0; - return std::max(0.0, std::min(1.0, playerACEffActive)); - } - else + if ( !gameplayCustomManager.inUse() || my->behavior != &actPlayer ) { return 1.0; } @@ -5537,8 +5532,21 @@ real_t Entity::getACEffectiveness(Entity* my, Stat* myStats, bool isPlayer, Enti if ( gameplayCustomManager.inUse() && my->behavior == &actPlayer ) { double playerACEffPassive = gameplayCustomManager.playerACEpassive / 100.0; + double playerACEffActive = gameplayCustomManager.playerACEactive / 100.0; + double ACEtoCalculate = 0.0; + + if ( myStats->defending ) + { + ACEtoCalculate = playerACEffActive; + } + else + { + ACEtoCalculate = playerACEffPassive; + } + ACEtoCalculate = std::max(0.0, std::min(1.0, ACEtoCalculate)); + double blessingModifier = gameplayCustomManager.playerACEbless / 100.0; - return std::max(0.0, std::min(1.0, playerACEffPassive + blessingModifier * blessings)); + return std::max(0.0, std::min(1.0, ACEtoCalculate + blessingModifier * blessings)); } return std::max(0.0, std::min(1.0, .75 + 0.025 * blessings)); } From d227509b6db8c1b61305fbf91c34d31f21d33f1a Mon Sep 17 00:00:00 2001 From: sootstains Date: Mon, 17 Nov 2025 02:41:11 -0500 Subject: [PATCH 5/8] Fixed <1 trauma damage incorrectly rounding to 1 --- src/entity.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/entity.cpp b/src/entity.cpp index 259bbd5e3..a9fa8a6e0 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -2536,6 +2536,11 @@ void Entity::modHP(int amount) double traumaDmg = percent * damage; int oldMaxHP = entitystats->MAXHP; + if (traumaDmg < 1.0) + { + traumaDmg = 0.0; // don't allow player to take unfair trauma damage + } + entitystats->MAXHP -= traumaDmg; entitystats->MAXHP = std::min(oldMaxHP, std::max( gameplayCustomManager.traumaDamageHPLimit, entitystats->MAXHP ) ); entitystats->HP = std::min(entitystats->HP, entitystats->MAXHP); From a9dfb3aea5a7a9e49fea1c757aadb689723fca6d Mon Sep 17 00:00:00 2001 From: sootstains Date: Sun, 23 Nov 2025 23:40:06 -0500 Subject: [PATCH 6/8] Fixed bug with classic mode conduct Classic conduct is now removed automatically after stage 25, can't be applied after stage 25 --- src/scores.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/scores.cpp b/src/scores.cpp index dc2c67476..8cdc79718 100644 --- a/src/scores.cpp +++ b/src/scores.cpp @@ -4061,11 +4061,18 @@ void updatePlayerConductsInMainLoop() } if ( !conductGameChallenges[CONDUCT_CLASSIC_MODE] ) { - if ( (svFlags & SV_FLAG_CLASSIC) ) + if ( (svFlags & SV_FLAG_CLASSIC) && currentlevel < 25 ) { conductGameChallenges[CONDUCT_CLASSIC_MODE] = 1; } } + else + { + if ( currentlevel >= 25 ) + { + conductGameChallenges[CONDUCT_CLASSIC_MODE] = 0; + } + } if ( !conductGameChallenges[CONDUCT_MODDED] ) { if ( Mods::numCurrentModsLoaded >= 0 ) From ca4ad0df5cd57f27c6c9d1b12d53a53d6d35418b Mon Sep 17 00:00:00 2001 From: sootstains Date: Wed, 10 Dec 2025 07:13:35 -0500 Subject: [PATCH 7/8] fixed missing feedback for usingCustomManager Added missing code so new properties are independently capable of enabling custom manager, if others are missing for any reason --- src/mod_tools.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index 750be0f50..3907bba11 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -2189,74 +2189,92 @@ class GameplayCustomManager else if ( name.compare("player_AC_eff_passive_percent") == 0 ) { playerACEpassive = itr->value.GetInt(); + return true; } else if ( name.compare("player_AC_eff_block_percent") == 0 ) { playerACEactive = itr->value.GetInt(); + return true; } else if ( name.compare("player_AC_eff_blessing_percent") == 0 ) { playerACEbless = itr->value.GetDouble(); + return true; } else if ( name.compare("enable_conditional_xp_modifier") == 0 ) { doConditionalXPModifier = itr->value.GetBool(); + return true; } else if ( name.compare("conditional_mod_lvl_difference_threshold") == 0 ) { conditionalXPModLvlThreshold = itr->value.GetInt(); + return true; } else if ( name.compare("conditional_xp_modifier_percent") == 0 ) { conditionalXPModPercent = itr->value.GetInt(); + return true; } else if ( name.compare("player_STR_multiplier") == 0 ) { playerMultiplierSTR = itr->value.GetDouble(); + return true; } else if ( name.compare("player_DEX_multiplier") == 0 ) { playerMultiplierDEX = itr->value.GetDouble(); + return true; } else if ( name.compare("player_CON_multiplier") == 0 ) { playerMultiplierCON = itr->value.GetDouble(); + return true; } else if ( name.compare("player_INT_multiplier") == 0 ) { playerMultiplierINT = itr->value.GetDouble(); + return true; } else if ( name.compare("player_PER_multiplier") == 0 ) { playerMultiplierPER = itr->value.GetDouble(); + return true; } else if ( name.compare("player_CHR_multiplier") == 0 ) { playerMultiplierCHR = itr->value.GetDouble(); + return true; } else if ( name.compare("player_levelup_HP") == 0 ) { playerLevelupHP = itr->value.GetInt(); + return true; } else if ( name.compare("player_levelup_MP") == 0 ) { playerLevelupMP = itr->value.GetInt(); + return true; } else if ( name.compare("enable_player_trauma_damage") == 0 ) { doTraumaDamage = itr->value.GetBool(); + return true; } else if ( name.compare("trauma_damage_percent") == 0 ) { traumaDamagePercent = itr->value.GetInt(); + return true; } else if ( name.compare("trauma_damage_hp_floor") == 0 ) { traumaDamageHPLimit = itr->value.GetInt(); + return true; } else if ( name.compare("zap_brigade_spawnrate_percent") == 0 ) { zapBrigadeSpawnPercent = itr->value.GetInt(); + return true; } else if ( name.compare("minotaur_force_disable_on_floors") == 0 ) { From ecf0ce135435f448ff016fa650746136fd085afe Mon Sep 17 00:00:00 2001 From: sootstains Date: Sun, 4 Jan 2026 00:03:56 -0500 Subject: [PATCH 8/8] Fixed hunger debuff persisting for skeletons Added separate hunger intervals for skeleton players so their hunger debuffs are cleared until they polymorph again. i'm not sure if this is even unintended, but people have requested it, so here's a working fix if it's to be accepted. :xyggi: --- src/entity.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/entity.cpp b/src/entity.cpp index a9fa8a6e0..6b0625aeb 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -22911,6 +22911,7 @@ int getEntityHungerInterval(int player, Entity* my, Stat* myStats, EntityHungerI { bool isInsectoidPlayer = false; bool isAutomatonPlayer = false; + bool isSkeletonPlayer = false; if ( player >= 0 ) { if ( stats[player]->type == AUTOMATON ) @@ -22921,6 +22922,10 @@ int getEntityHungerInterval(int player, Entity* my, Stat* myStats, EntityHungerI { isInsectoidPlayer = true; } + else if ( stats[player]->type == SKELETON ) + { + isSkeletonPlayer = true; + } } else if ( my && my->behavior == &actPlayer && myStats ) { @@ -22932,6 +22937,10 @@ int getEntityHungerInterval(int player, Entity* my, Stat* myStats, EntityHungerI { isInsectoidPlayer = true; } + else if ( myStats->type == SKELETON ) + { + isSkeletonPlayer = true; + } } else if ( myStats ) { @@ -22947,6 +22956,10 @@ int getEntityHungerInterval(int player, Entity* my, Stat* myStats, EntityHungerI { isInsectoidPlayer = true; } + else if ( myStats->type == SKELETON ) + { + isSkeletonPlayer = true; + } break; } } @@ -22972,6 +22985,20 @@ int getEntityHungerInterval(int player, Entity* my, Stat* myStats, EntityHungerI return 1000; } } + else if ( isSkeletonPlayer ) + { + switch ( hungerInterval ) + { + case HUNGER_INTERVAL_OVERSATIATED: + return 5000; // unreachable + case HUNGER_INTERVAL_HUNGRY: + return -1; //unreachable + case HUNGER_INTERVAL_WEAK: + return -1; //unreachable + case HUNGER_INTERVAL_STARVING: + return -1; //unreachable + } + } switch ( hungerInterval ) {