From 7902afec4c61b438afe12cef8eca819e8bf4bf23 Mon Sep 17 00:00:00 2001 From: DMD Date: Sat, 24 Sep 2016 14:14:22 +0300 Subject: [PATCH] Whirlwind fix to allow whirlwind from before beginning combat --- TemplePlus/combat.cpp | 21 +++++++++ TemplePlus/combat.h | 3 +- TemplePlus/d20.cpp | 70 +++++++++++++++++++++++++++++ TemplePlus/python/python_object.cpp | 3 ++ 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/TemplePlus/combat.cpp b/TemplePlus/combat.cpp index 9a76e153c..742f4cc6a 100644 --- a/TemplePlus/combat.cpp +++ b/TemplePlus/combat.cpp @@ -451,6 +451,27 @@ objHndl * LegacyCombatSystem::GetHostileCombatantList(objHndl obj, int * count) return result; } +void LegacyCombatSystem::GetEnemyListInRange(objHndl performer, float rangeFeet, std::vector& enemiesOut){ + + auto perfLoc = objSystem->GetObject(performer)->GetLocationFull(); + + ObjList enemies; + enemies.ListRadius(perfLoc, rangeFeet* INCH_PER_TILE, OLC_CRITTERS); + for (int i = 0; i < enemies.size(); i++) { + auto resHandle = enemies[i]; + if (!resHandle) + break; + + if (critterSys.IsDeadNullDestroyed(resHandle)) + continue; + + if (resHandle != performer && !critterSys.AllegianceShared(resHandle, performer) && !critterSys.IsFriendly(performer, resHandle)) { + enemiesOut.push_back(resHandle); + } + } + +} + bool LegacyCombatSystem::HasLineOfAttack(objHndl obj, objHndl target) { BOOL result = 1; diff --git a/TemplePlus/combat.h b/TemplePlus/combat.h index 369b9143d..61e4e4dab 100644 --- a/TemplePlus/combat.h +++ b/TemplePlus/combat.h @@ -38,7 +38,8 @@ struct LegacyCombatSystem : temple::AddressTable { int GetThreateningCrittersAtLoc(objHndl obj, LocAndOffsets* loc, objHndl threateners[40]); objHndl CheckRangedWeaponAmmo(objHndl obj); // checks if the ammo slot item matches a wielded weapon (primary or secondary), and if so, returns it bool AmmoMatchesItemAtSlot(objHndl obj, EquipSlot equipSlot); - objHndl * GetHostileCombatantList(objHndl obj, int* count); + objHndl * GetHostileCombatantList(objHndl obj, int* count); // gets a list from the combat initiative + void GetEnemyListInRange(objHndl obj, float rangeFeet, std::vector & enemies); bool HasLineOfAttack(objHndl obj, objHndl target); // can shoot or attack target (i.e. target isn't behind a wall or sthg) diff --git a/TemplePlus/d20.cpp b/TemplePlus/d20.cpp index 9dfcbed0e..ae0f99577 100644 --- a/TemplePlus/d20.cpp +++ b/TemplePlus/d20.cpp @@ -123,6 +123,7 @@ class D20ActionCallbacks { AddToSeq(Python); AddToSeq(Simple); AddToSeq(WithTarget); + AddToSeq(WhirlwindAttack); static ActionErrorCode AddToSeqTripAttack(D20Actn* d20a, ActnSeq* actSeq, TurnBasedStatus* tbStat);; @@ -151,6 +152,7 @@ class D20ActionCallbacks { ActionCost(MoveAction); ActionCost(Null); ActionCost(StandardAction); + ActionCost(WhirlwindAttack); static ActionErrorCode LocationCheckDisarmedWeaponRetrieve(D20Actn* d20a, TurnBasedStatus* tbStat, LocAndOffsets* loc); static ActionErrorCode LocationCheckPython(D20Actn* d20a, TurnBasedStatus* tbStat, LocAndOffsets* loc); @@ -486,6 +488,11 @@ void LegacyD20System::NewD20ActionsInit() d20Defs[d20Type].performFunc = d20Callbacks.PerformSneak; + d20Type = D20A_WHIRLWIND_ATTACK; + d20Defs[d20Type].addToSeqFunc = d20Callbacks.AddToSeqWhirlwindAttack; + d20Defs[d20Type].actionCost = d20Callbacks.ActionCostWhirlwindAttack; + + // *(int*)&d20Defs[D20A_USE_POTION].flags |= (int)D20ADF_SimulsCompatible; // need to modify the SimulsEnqueue script because it also checks for san_start_combat being null // *(int*)&d20Defs[D20A_TRIP].flags -= (int)D20ADF_Unk8000; @@ -2511,6 +2518,40 @@ ActionErrorCode D20ActionCallbacks::AddToSeqWithTarget(D20Actn* d20a, ActnSeq* a return static_cast(actSeqSys.AddToSeqWithTarget(d20a, actSeq, tbStat)); } +ActionErrorCode D20ActionCallbacks::AddToSeqWhirlwindAttack(D20Actn* d20a, ActnSeq* actSeq, struct TurnBasedStatus* tbStat){ + auto performer = d20a->d20APerformer; + if (d20Sys.d20Query(performer, DK_QUE_Prone)){ + D20Actn standUp; + standUp = *d20a; + standUp.d20ActType = D20A_STAND_UP; + actSeq->d20ActArray[actSeq->d20ActArrayNum++] = standUp; + } + + actSeq->d20ActArray[actSeq->d20ActArrayNum++] = *d20a; + + + + auto reach = critterSys.GetReach(performer, D20A_UNSPECIFIED_ATTACK); + auto perfSizeFeet = objects.GetRadius(performer)/INCH_PER_FEET; + + std::vector enemies; + combatSys.GetEnemyListInRange(performer, 1.0 + perfSizeFeet + reach, enemies); + + for (auto i=0u; id20ActArray[actSeq->d20ActArrayNum++] = stdAttack; + } + } + + + return AEC_OK; +} + ActionErrorCode D20ActionCallbacks::AddToSeqTripAttack(D20Actn* d20a, ActnSeq* actSeq, TurnBasedStatus* tbStat){ auto tgt = d20a->d20ATarget; @@ -2643,5 +2684,34 @@ ActionErrorCode D20ActionCallbacks::ActionCostStandardAction(D20Actn*, TurnBased return AEC_OK; }; +ActionErrorCode D20ActionCallbacks::ActionCostWhirlwindAttack(D20Actn* d20a, TurnBasedStatus* tbStat, ActionCostPacket*acp) { + acp->chargeAfterPicker = 0; + acp->moveDistCost = 0; + acp->hourglassCost = 4; + if ( ( d20a->d20Caf & D20CAF_FREE_ACTION) || !combatSys.isCombatActive()){ + acp->hourglassCost = 0; + } + + if (tbStat->attackModeCode >= tbStat->baseAttackNumCode && tbStat->hourglassState >= 4 && !tbStat->numBonusAttacks){ + auto performer = d20a->d20APerformer; + auto reach = critterSys.GetReach(performer, D20A_UNSPECIFIED_ATTACK); + auto perfSizeFeet = objects.GetRadius(performer) / INCH_PER_FEET; + + std::vector enemies; + combatSys.GetEnemyListInRange(performer, 1.0 + perfSizeFeet + reach, enemies); + auto numEnemies = 0; + for (auto i=0; inumBonusAttacks = 0; + tbStat->baseAttackNumCode = 0; + tbStat->numAttacks = numEnemies; + tbStat->attackModeCode = ATTACK_CODE_PRIMARY; + tbStat->surplusMoveDistance = 0; + } + + return AEC_OK; +} diff --git a/TemplePlus/python/python_object.cpp b/TemplePlus/python/python_object.cpp index 4f7adcd25..581ccbb11 100644 --- a/TemplePlus/python/python_object.cpp +++ b/TemplePlus/python/python_object.cpp @@ -1006,6 +1006,9 @@ static PyObject* PyObjHandle_DamageWithReduction(PyObject* obj, PyObject* args) static PyObject* PyObjHandle_Heal(PyObject* obj, PyObject* args) { auto self = GetSelf(obj); + if (!self->handle) + Py_RETURN_NONE; + objHndl healer; Dice dice; D20ActionType actionType = D20A_NONE;