diff --git a/TemplePlus/ability_fixes.cpp b/TemplePlus/ability_fixes.cpp index 7659d1455..366c1d76a 100644 --- a/TemplePlus/ability_fixes.cpp +++ b/TemplePlus/ability_fixes.cpp @@ -8,6 +8,9 @@ #include "d20.h" #include "float_line.h" #include "history.h" +#include "gamesystems/objects/objsystem.h" +#include "gamesystems/gamesystems.h" +#include "gamesystems/particlesystems.h" // Ability Condition Fixes (for buggy abilities, including monster abilities) class AbilityConditionFixes : public TempleFix { @@ -15,12 +18,16 @@ class AbilityConditionFixes : public TempleFix { #define ABFIX(fname) static int fname ## (DispatcherCallbackArgs args); #define HOOK_ORG(fname) static int (__cdecl* org ##fname)(DispatcherCallbackArgs) = replaceFunction static int PoisonedOnBeginRound(DispatcherCallbackArgs args);; - + static int MonsterSplittingHpChange(DispatcherCallbackArgs args); + static int MonsterOozeSplittingOnDamage(DispatcherCallbackArgs args); void apply() override { replaceFunction(0x100EA040, PoisonedOnBeginRound); + replaceFunction(0x100F7550, MonsterSplittingHpChange); + replaceFunction(0x100F7490, MonsterOozeSplittingOnDamage); + // fixes animal companion runoff crash (it didn't null the second part of the obj handle stored in args[1,2] static void (__cdecl*orgCompanionRunoff)(SubDispNode*, objHndl , objHndl ) = replaceFunction(0x100FC3D0, [](SubDispNode* sdn, objHndl owner, objHndl handle){ orgCompanionRunoff(sdn, owner, handle); @@ -138,3 +145,70 @@ int AbilityConditionFixes::PoisonedOnBeginRound(DispatcherCallbackArgs args){ conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode); return 0; } + +int AbilityConditionFixes::MonsterSplittingHpChange(DispatcherCallbackArgs args){ + auto protoId = objSystem->GetProtoId(args.objHndCaller); + auto obj = objSystem->GetObject(args.objHndCaller); + auto loc = obj->GetLocationFull(); + auto hpCur = objects.StatLevelGet(args.objHndCaller, stat_hp_current); + auto baseHp = obj->GetInt32(obj_f_hp_pts); + auto hpDam = obj->GetInt32(obj_f_hp_damage); + + auto hpMod = (hpCur + hpDam) - baseHp; + + if (hpCur <= 10) + return 0; + + obj->SetInt32(obj_f_hp_pts, baseHp / 2); + obj->SetInt32(obj_f_hp_damage, (hpDam + hpMod) / 2); + + + auto protoHandle = objSystem->GetProtoHandle(protoId); + auto newMonster = objSystem->CreateObject(protoHandle, loc.location); + auto newMonObj = objSystem->GetObject(newMonster); + newMonObj->SetInt32(obj_f_hp_pts, baseHp / 2); + newMonObj->SetInt32(obj_f_hp_damage, (hpDam + hpMod) / 2); + newMonObj->SetInt32(obj_f_critter_flags, newMonObj->GetInt32(obj_f_critter_flags) | OCF_EXPERIENCE_AWARDED); + + gameSystems->GetParticleSys().CreateAtObj("hit-Acid-medium", args.objHndCaller); + conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode); + + return 0; +} + +int AbilityConditionFixes::MonsterOozeSplittingOnDamage(DispatcherCallbackArgs args){ + + auto curHp = objects.StatLevelGet(args.objHndCaller, stat_hp_current); + if (curHp <= 10) + return 0; + + GET_DISPIO(dispIOTypeDamage, DispIoDamage); + auto isSplitting = false; + auto protoId = objSystem->GetProtoId(args.objHndCaller); + if (protoId == 14142 && dispIo->damage.GetOverallDamageByType(DamageType::Electricity) > 0){ + isSplitting = true; + dispIo->damage.AddModFactor(0.0f, DamageType::Electricity, 132); + } + + if (dispIo->damage.GetOverallDamageByType(DamageType::Slashing) > 0) { + isSplitting = true; + dispIo->damage.AddModFactor(0.0f, DamageType::Slashing, 132); + } + + if (dispIo->damage.GetOverallDamageByType(DamageType::Piercing) > 0) { + isSplitting = true; + dispIo->damage.AddModFactor(0.0f, DamageType::Piercing, 132); + } + + if (dispIo->damage.GetOverallDamageByType(DamageType::PiercingAndSlashing) > 0) { + isSplitting = true; + dispIo->damage.AddModFactor(0.0f, DamageType::PiercingAndSlashing, 132); + } + + + if (isSplitting){ + conds.AddTo(args.objHndCaller, "Monster Splitting", {}); + } + + return 0; +} diff --git a/TemplePlus/damage.cpp b/TemplePlus/damage.cpp index d1a2489a7..ced703820 100644 --- a/TemplePlus/damage.cpp +++ b/TemplePlus/damage.cpp @@ -160,16 +160,48 @@ void DamagePacket::CalcFinalDamage(){ // todo hook this int DamagePacket::GetOverallDamageByType(DamageType damType) { - // TODO auto damTot = (double)0.0; for (auto i=0u; idiceCount; i++) { - + if (damType == this->dice[i].type || damType == DamageType::Unspecified){ + damTot += this->dice[i].rolledDamage; + if (i == 0) { + damTot += this->bonuses.GetEffectiveBonusSum(); + } + } + + } + + for (auto i=0; idamModCount; i++){ + addresses.CalcDamageModFromFactor(this, &this->damageFactorModifiers[i], DamageType::Unspecified, DamageType::Unspecified); + } + + for (auto i=0; idamResCount; i++){ + addresses.CalcDamageModFromDR(this, &this->damageResistances[i], DamageType::Unspecified, DamageType::Unspecified); + } + + for (auto i=0; idamResCount; i++){ + if (damage.DamageTypeMatch(damType, this->damageResistances[i].type)){ + damTot += this->damageResistances[i].damageReduced; + } + } + + for (auto i = 0; i < this->damModCount; i++) { + if (damage.DamageTypeMatch(damType, this->damageResistances[i].type)){ + damTot += this->damageFactorModifiers[i].damageReduced; + } } + if (damTot < 0.0) + damTot = 0.0; + return damTot; } +int DamagePacket::AddModFactor(float factor, DamageType damType, int damageMesLine){ + return addresses.AddDamageModFactor(this, factor, damType, damageMesLine); +} + DamagePacket::DamagePacket(){ diceCount = 0; damResCount = 0; @@ -562,6 +594,46 @@ int Damage::AddDamageDiceWithDescr(DamagePacket* dmgPkt, int dicePacked, DamageT return 0; } +BOOL Damage::DamageTypeMatch(DamageType reduction, DamageType attackType){ + + if (attackType == DamageType::Subdual) { + if (reduction != DamageType::Subdual) + return FALSE; + } + else if (attackType == DamageType::Unspecified) + return TRUE; + + if (reduction == DamageType::Unspecified || attackType == reduction) + return TRUE; + + switch (attackType){ + case DamageType::BludgeoningAndPiercing: + if (reduction == DamageType::Bludgeoning || reduction == DamageType::Piercing) + return TRUE; + break; + case DamageType::PiercingAndSlashing: + if (reduction == DamageType::Piercing || reduction == DamageType::Slashing) + return TRUE; + break; + case DamageType::SlashingAndBludgeoning: + if (reduction == DamageType::Slashing || reduction == DamageType::Bludgeoning) + return TRUE; + break; + case DamageType::SlashingAndBludgeoningAndPiercing: + if (reduction == DamageType::Bludgeoning || reduction == DamageType::Slashing + || reduction == DamageType::Piercing || reduction == DamageType::BludgeoningAndPiercing + || reduction == DamageType::PiercingAndSlashing || reduction == DamageType::SlashingAndBludgeoning + || reduction == DamageType::Subdual) + return TRUE; + break; + default: + return FALSE; + + } + + return FALSE; +} + Damage::Damage(){ damageMes = 0; // damageMes = addresses.damageMes; diff --git a/TemplePlus/damage.h b/TemplePlus/damage.h index 11bc6999c..013988dc1 100644 --- a/TemplePlus/damage.h +++ b/TemplePlus/damage.h @@ -53,7 +53,7 @@ struct DamagePacket { void AddAttackPower(int attackPower); void CalcFinalDamage(); // calcualtes the finalDamage field int GetOverallDamageByType(DamageType damType); - + int AddModFactor(float factor, DamageType damType, int damageMesLine); DamagePacket(); }; @@ -129,6 +129,7 @@ class Damage { const char* GetMesline(unsigned damageMesLine); int AddDamageDice(DamagePacket *dmgPkt, int dicePacked, DamageType damType, unsigned int damageMesLine); int AddDamageDiceWithDescr(DamagePacket *dmgPkt, int dicePacked, DamageType damType, unsigned int damageMesLine, char* descr); + BOOL DamageTypeMatch(DamageType reduction, DamageType attackType); Damage(); private: void Init(); diff --git a/TemplePlus/spell.cpp b/TemplePlus/spell.cpp index b8604e611..2c0bde463 100644 --- a/TemplePlus/spell.cpp +++ b/TemplePlus/spell.cpp @@ -2034,7 +2034,7 @@ uint32_t LegacySpellSystem::pickerArgsFromSpellEntry(SpellEntry* spEntry, Picker if (spEntry->IsBaseModeTarget(UiPickerType::Area) && (spEntry->spellEnum == 133 // Dispel Magic || spEntry->spellEnum == 434) ){ // Silence - (*(uint64_t*)args->modeTarget ) |= (uint64_t)UiPickerType::AreaOrObj; + (*(uint64_t*)&args->modeTarget ) |= (uint64_t)UiPickerType::AreaOrObj; }