diff --git a/TemplePlus/action_sequence.cpp b/TemplePlus/action_sequence.cpp index ca0110cba..b3f14bfd5 100644 --- a/TemplePlus/action_sequence.cpp +++ b/TemplePlus/action_sequence.cpp @@ -2138,6 +2138,20 @@ int32_t ActionSequenceSystem::DoAoosByAdjcentEnemies(objHndl obj) // return _AOOSthg2_100981C0(obj); } +int32_t ActionSequenceSystem::ProvokeAooFrom(objHndl provoker, objHndl enemy) +{ + if (objects.GetFlags(enemy) & OF_INVULNERABLE) // see above + return 0; + + if (!combatSys.CanMeleeTarget(enemy, provoker)) return 0; + if (critterSys.IsFriendly(provoker, enemy)) return 0; + if (!d20Sys.d20QueryWithData(enemy, DK_QUE_AOOPossible, provoker)) return 0; + if (!d20Sys.d20QueryWithData(enemy, DK_QUE_AOOWillTake, provoker)) return 0; + + DoAoo(enemy, provoker); + return 1; +} + bool ActionSequenceSystem::SpellTargetsFilterInvalid(D20Actn& d20a){ auto valid = true; @@ -2810,13 +2824,25 @@ void ActionSequenceSystem::ActionPerform() } else{ + bool preempted = false; + switch (d20->D20ActionTriggersAoO(d20a, &tbStatus)) + { + case 0: + break; + case 2: + preempted = ProvokeAooFrom(d20a->d20APerformer, d20a->d20ATarget); + break; + case 1: + default: + preempted = DoAoosByAdjcentEnemies(d20a->d20APerformer); + break; + } - if ( d20->D20ActionTriggersAoO(d20a, &tbStatus) && DoAoosByAdjcentEnemies(d20a->d20APerformer)) { + if (preempted) { logger->debug("ActionPerform: \t Sequence Preempted {}", d20a->d20APerformer); --*(curIdx); sequencePerform(); - } - else{ + } else { curSeq->tbStatus = tbStatus; *(uint32_t*)(&curSeq->tbStatus.tbsFlags) |= TBSF_HasActedThisRound; InterruptCounterspell(d20a); diff --git a/TemplePlus/action_sequence.h b/TemplePlus/action_sequence.h index 63bb3d231..f4dcd8051 100644 --- a/TemplePlus/action_sequence.h +++ b/TemplePlus/action_sequence.h @@ -180,6 +180,7 @@ struct ActionSequenceSystem : temple::AddressTable uint32_t seqCheckFuncs(TurnBasedStatus *tbStatus); void DoAoo(objHndl, objHndl); int32_t DoAoosByAdjcentEnemies(objHndl); + int32_t ProvokeAooFrom(objHndl provoker, objHndl enemy); bool SpellTargetsFilterInvalid(D20Actn &d20a); void HandleInterruptSequence(); diff --git a/TemplePlus/animgoals/anim.cpp b/TemplePlus/animgoals/anim.cpp index 159f5f56c..02a2b4511 100644 --- a/TemplePlus/animgoals/anim.cpp +++ b/TemplePlus/animgoals/anim.cpp @@ -457,62 +457,83 @@ int AnimSystem::PushAnimate(objHndl obj, int anim) { return addresses.PushAnimate(obj, anim); } -BOOL AnimSystem::PushSpellCast(SpellPacketBody & spellPkt, objHndl item) +void AnimSystem::SetGoalDataForSpellPacket(SpellPacketBody & pkt, AnimSlotGoalStackEntry & goalData, bool wand, bool conjure) { - - // note: the original included the spell ID generation & registration, this is separated here. - auto caster = spellPkt.caster; + auto caster = pkt.caster; auto casterObj = objSystem->GetObject(caster); - AnimSlotGoalStackEntry goalData; - if (!goalData.InitWithInterrupt(caster, ag_throw_spell_w_cast_anim)) - return FALSE; + SpellEntry ent(pkt.spellEnum); - goalData.skillData.number = spellPkt.spellId; - - SpellEntry spEntry(spellPkt.spellEnum); + goalData.skillData.number = pkt.spellId; // if self-targeted spell - if (spEntry.IsBaseModeTarget(UiPickerType::Single) && spellPkt.targetCount == 0 ){ - goalData.target.obj = spellPkt.caster; - - if (spellPkt.aoeCenter.location.location == 0){ + if (ent.IsBaseModeTarget(UiPickerType::Single) && pkt.targetCount == 0) { + goalData.target.obj = caster; + if (pkt.aoeCenter.location.location == 0) goalData.targetTile.location = casterObj->GetLocationFull(); - } - else{ - goalData.targetTile.location = spellPkt.aoeCenter.location; - } - } + else + goalData.targetTile.location = pkt.aoeCenter.location; - else{ - auto tgt = spellPkt.targetListHandles[0]; + } else { + auto tgt = pkt.targetListHandles[0]; goalData.target.obj = tgt; - if (tgt && spellPkt.aoeCenter.location.location == 0 ){ + if (tgt && pkt.aoeCenter.location.location == 0) { goalData.targetTile.location = objSystem->GetObject(tgt)->GetLocationFull(); - } - else { - goalData.targetTile.location = spellPkt.aoeCenter.location; + } else { + goalData.targetTile.location = pkt.aoeCenter.location; } } - if (inventory.UsesWandAnim(item)){ - goalData.animIdPrevious.number = temple::GetRef(0x100757C0)(spEntry.spellSchoolEnum); // GetAnimIdWand + if (wand){ + goalData.animIdPrevious.number = GetWandAnimId(ent.spellSchoolEnum, conjure); } else{ - goalData.animIdPrevious.number = temple::GetRef(0x100757B0)(spEntry.spellSchoolEnum); // GetSpellSchoolAnimId + goalData.animIdPrevious.number = GetCastingAnimId(ent.spellSchoolEnum, conjure); } +} + +BOOL AnimSystem::PushSpellCast(SpellPacketBody & spellPkt, objHndl item) +{ + // note: the original included the spell ID generation & registration, this is separated here. + AnimSlotGoalStackEntry goalData; + if (!goalData.InitWithInterrupt(spellPkt.caster, ag_throw_spell_w_cast_anim)) + return FALSE; + + SetGoalDataForSpellPacket(spellPkt, goalData, inventory.UsesWandAnim(item), false); return goalData.Push(nullptr); } +BOOL AnimSystem::PushSpellDismiss(SpellPacketBody & pkt) +{ + AnimSlotGoalStackEntry goalData; + if (!goalData.InitWithInterrupt(pkt.caster, ag_throw_spell_w_cast_anim)) + return FALSE; + + SetGoalDataForSpellPacket(pkt, goalData, true, true); + + return goalData.Push(nullptr); +} + +int AnimSystem::GetWandAnimId(int school, bool conjure) { + auto dec = conjure ? 1 : 0; + return temple::GetRef(0x100757C0)(school) - dec; +} + +int AnimSystem::GetCastingAnimId(int school, bool conjure) { + auto dec = conjure ? 1 : 0; + return temple::GetRef(0x100757B0)(school) - dec; +} + + BOOL AnimSystem::PushSpellInterrupt(const objHndl& caster, objHndl item, AnimGoalType animGoalType, int spellSchool){ AnimSlotGoalStackEntry goalData; goalData.InitWithInterrupt(caster, animGoalType); goalData.target.obj = (*actSeqSys.actSeqCur)->spellPktBody.caster; goalData.skillData.number = 0; if (inventory.UsesWandAnim(item)) - goalData.animIdPrevious.number = temple::GetRef(0x100757C0)(spellSchool); // GetAnimIdWand + goalData.animIdPrevious.number = GetWandAnimId(spellSchool); else - goalData.animIdPrevious.number = temple::GetRef(0x100757B0)(spellSchool); // GetSpellSchoolAnimId + goalData.animIdPrevious.number = GetCastingAnimId(spellSchool); return goalData.Push(nullptr); } diff --git a/TemplePlus/animgoals/anim.h b/TemplePlus/animgoals/anim.h index d67a19c9d..43ffc2519 100644 --- a/TemplePlus/animgoals/anim.h +++ b/TemplePlus/animgoals/anim.h @@ -182,9 +182,16 @@ friend struct AnimSlotGoalStackEntry; pushes spell animation, including wand animation if relevant */ BOOL PushSpellCast(SpellPacketBody &spellPkt, objHndl item); - BOOL PushSpellInterrupt(const objHndl& caster, objHndl item, AnimGoalType animGoalType, int spellSchool); + /* + pushes an animation for dismissing an active spell + */ + BOOL PushSpellDismiss(SpellPacketBody &spellPkt); + int GetWandAnimId(int school, bool conjure = false); + int GetCastingAnimId(int school, bool conjure = false); + void SetGoalDataForSpellPacket(SpellPacketBody &pkt, AnimSlotGoalStackEntry &goalData, bool wand, bool conjure=false); + /* used by the general out-of-combat mouse LMB click handler */ diff --git a/TemplePlus/condition.cpp b/TemplePlus/condition.cpp index b35d631e5..a4d4b11e9 100644 --- a/TemplePlus/condition.cpp +++ b/TemplePlus/condition.cpp @@ -261,6 +261,7 @@ class ClassAbilityCallbacks // Aura Of Courage static int __cdecl CouragedAuraSavingThrow(DispatcherCallbackArgs args); + static int FailedCopyScroll(DispatcherCallbackArgs args); static int FeatBrewPotionRadialMenu(DispatcherCallbackArgs args); static int FeatScribeScrollRadialMenu(DispatcherCallbackArgs args); @@ -568,6 +569,9 @@ class ConditionFunctionReplacement : public TempleFix { SubDispDefNew sdd(dispTypeDealingDamageWeaponlikeSpell, 0, classAbilityCallbacks.SneakAttackDamage, 0u,0u); // Weapon-like spell damage hook write(0x102ED2A8, &sdd, sizeof(SubDispDefNew)); + // Wizard + replaceFunction(0x10102AE0, classAbilityCallbacks.FailedCopyScroll); + // D20Mods countdown handler replaceFunction(0x100EC9B0, genericCallbacks.D20ModCountdownHandler); replaceFunction(0x100E98B0, genericCallbacks.D20ModCountdownEndHandler); @@ -1153,7 +1157,8 @@ int GenericCallbacks::TripAooQuery(DispatcherCallbackArgs args) int GenericCallbacks::ImprovedTripBonus(DispatcherCallbackArgs args) { auto dispIo = dispatch.DispIoCheckIoType10(args.dispIO); - if (dispIo->flags & 1){ + // 2 seems to be defender + if (dispIo->flags & 1 && !(dispIo->flags & 2)) { dispIo->bonOut->AddBonusWithDesc(4, 0, 114, feats.GetFeatName(FEAT_IMPROVED_TRIP)); } return 0; @@ -1820,8 +1825,10 @@ int ArmorCritMultiplier(DispatcherCallbackArgs args) int ShieldBashProficiencyPenalty(DispatcherCallbackArgs args) { auto dispIo = dispatch.DispIoCheckIoType5(args.dispIO); - auto invIdx = args.GetCondArg(2); auto attacker = dispIo->attackPacket.attacker; + if (!attacker) return 0; + + auto invIdx = args.GetCondArg(2); auto shield = inventory.GetItemAtInvIdx(attacker, invIdx); if (dispIo->attackPacket.weaponUsed != shield) return 0; @@ -1928,14 +1935,28 @@ int __cdecl GlobalToHitBonus(DispatcherCallbackArgs args) } if (dualWielding) { - if (d20Sys.UsingSecondaryWeapon(args.objHndCaller, attackCode)) - bonusSys.bonusAddToBonusList(&dispIo->bonlist, -10, 26, 121); // penalty for dualwield on offhand attack - else - bonusSys.bonusAddToBonusList(&dispIo->bonlist, -6, 27, 122); // penalty for dualwield on primary attack - if (critterSys.OffhandIsLight(args.objHndCaller)) - { - bonusSys.bonusAddToBonusList(&dispIo->bonlist, 2, 0, 167); // Light Off-hand Weapon + //See if python is going to handle the penalty + const bool overridePenalty = d20Sys.D20QueryPython(args.objHndCaller, "Override Two Weapon Penalty", dualWielding ? 1 :0); + if (overridePenalty) { + auto penalty = d20Sys.D20QueryPython(args.objHndCaller, "Get Two Weapon Penalty", dualWielding ? 1 : 0); + if (d20Sys.UsingSecondaryWeapon(args.objHndCaller, attackCode)) { + bonusSys.bonusAddToBonusList(&dispIo->bonlist, penalty, 26, 121); + } + else { + bonusSys.bonusAddToBonusList(&dispIo->bonlist, penalty, 27, 122); + } + } + else { + if (d20Sys.UsingSecondaryWeapon(args.objHndCaller, attackCode)) + bonusSys.bonusAddToBonusList(&dispIo->bonlist, -10, 26, 121); // penalty for dualwield on offhand attack + else + bonusSys.bonusAddToBonusList(&dispIo->bonlist, -6, 27, 122); // penalty for dualwield on primary attack + + if (critterSys.OffhandIsLight(args.objHndCaller)) + { + bonusSys.bonusAddToBonusList(&dispIo->bonlist, 2, 0, 167); // Light Off-hand Weapon + } } } } @@ -1943,7 +1964,8 @@ int __cdecl GlobalToHitBonus(DispatcherCallbackArgs args) // helplessness bonus if (dispIo->attackPacket.victim && d20Sys.d20Query(dispIo->attackPacket.victim, DK_QUE_Helpless) - && !d20Sys.d20Query(dispIo->attackPacket.victim, DK_QUE_Critter_Is_Stunned)) + && !d20Sys.d20Query(dispIo->attackPacket.victim, DK_QUE_Critter_Is_Stunned) + && !(dispIo->attackPacket.flags & D20CAF_RANGED)) bonusSys.bonusAddToBonusList(&dispIo->bonlist, 4, 30, 136); // flanking bonus @@ -3256,6 +3278,24 @@ void ConditionSystem::RegisterNewConditions() DispatcherHookInit(cond, 1, dispTypeRadialMenuEntry, 0, AidAnotherRadialMenu, 0, 0); // + { + static CondStructNew condHeld; + condHeld.ExtendExisting("Held"); + condHeld.subDispDefs[11].dispCallback = [](DispatcherCallbackArgs args) { + static auto orig = temple::GetRef(0x100EDF10); + // disable effect tooltip if freedom of movement + if (!d20Sys.d20Query(args.objHndCaller, DK_QUE_Critter_Has_Freedom_of_Movement)) + return orig(args); + return 0; + }; + condHeld.AddHook(dispTypeAbilityScoreLevel, DK_STAT_STRENGTH, HeldCapStatBonus); + condHeld.AddHook(dispTypeAbilityScoreLevel, DK_STAT_DEXTERITY, HeldCapStatBonus); + + static CondStructNew condSleeping; + condSleeping.ExtendExisting("Sleeping"); + condSleeping.AddHook(dispTypeAbilityScoreLevel, DK_STAT_DEXTERITY, HelplessCapStatBonus); + } + /* char mCondIndomitableWillName[100]; CondStructNew *mCondIndomitableWill; @@ -3507,22 +3547,25 @@ int __cdecl GreaterTWFRanger(DispatcherCallbackArgs args) int __cdecl TwoWeaponFightingBonus(DispatcherCallbackArgs args) { - DispIoAttackBonus *dispIo = dispatch.DispIoCheckIoType5((DispIoAttackBonus*)args.dispIO); - char *featName; + DispIoAttackBonus* dispIo = dispatch.DispIoCheckIoType5((DispIoAttackBonus*)args.dispIO); - feat_enums feat = (feat_enums)conds.CondNodeGetArg(args.subDispNode->condNode, 0); - int attackCode = dispIo->attackPacket.dispKey; - int dualWielding = 0; - int attackNumber = 1; - if (d20Sys.UsingSecondaryWeapon(args.objHndCaller, attackCode)) - { - featName = feats.GetFeatName(feat); - bonusSys.bonusAddToBonusListWithDescr(&dispIo->bonlist, 6, 0, 114, featName); - } - else if ( d20Sys.ExtractAttackNumber(args.objHndCaller, attackCode, &attackNumber, &dualWielding), dualWielding != 0) - { - featName = feats.GetFeatName(feat); - bonusSys.bonusAddToBonusListWithDescr(&dispIo->bonlist, 2, 0, 114, featName); + //Disable when using agile shield fighter for examle + if (d20Sys.D20QueryPython(args.objHndCaller, "Disable Two Weapon Fighting Bonus") == 0) { + char* featName; + feat_enums feat = (feat_enums)conds.CondNodeGetArg(args.subDispNode->condNode, 0); + int attackCode = dispIo->attackPacket.dispKey; + int dualWielding = 0; + int attackNumber = 1; + if (d20Sys.UsingSecondaryWeapon(args.objHndCaller, attackCode)) + { + featName = feats.GetFeatName(feat); + bonusSys.bonusAddToBonusListWithDescr(&dispIo->bonlist, 6, 0, 114, featName); + } + else if (d20Sys.ExtractAttackNumber(args.objHndCaller, attackCode, &attackNumber, &dualWielding), dualWielding != 0) + { + featName = feats.GetFeatName(feat); + bonusSys.bonusAddToBonusListWithDescr(&dispIo->bonlist, 2, 0, 114, featName); + } } return 0; } @@ -3649,6 +3692,31 @@ int TacticalOptionAbusePrevention(DispatcherCallbackArgs args) return temple::GetRef(0x100F7ED0)(args); // replaced in ability_fixes.cpp } +// Port of 0x100E7F80. Was used in Unconscious but missing in similar +// conditions. Helpless critters should have 0 effective dexterity, and +// paralyzed creatures should have 0 effective strength. +int HelplessCapStatBonus(DispatcherCallbackArgs args) +{ + DispIoBonusList *dispIo = dispatch.DispIoCheckIoType2(args.dispIO); + + dispIo->bonlist.AddCap(0, 0, 109); + + return 0; +} + +// As above, but check for freedom of movement, since it doesn't remove the held +// condition. +int HeldCapStatBonus(DispatcherCallbackArgs args) +{ + DispIoBonusList *dispIo = dispatch.DispIoCheckIoType2(args.dispIO); + + if (!d20Sys.d20Query(args.objHndCaller, DK_QUE_Critter_Has_Freedom_of_Movement)) + dispIo->bonlist.AddCap(0, 0, 109); + + return 0; +} + + #pragma region Barbarian Stuff int __cdecl CombatExpertiseRadialMenu(DispatcherCallbackArgs args) @@ -3994,7 +4062,11 @@ int RendOnDamage(DispatcherCallbackArgs args) auto previousTarget = args.GetCondArgObjHndl(2); if (hasDeliveredDamage && attackDescr == previousAttackDescr && previousTarget == dispIo->attackPacket.victim) { - Dice dice(2, 6, 9); + int strScore = objects.StatLevelGet(args.objHndCaller, stat_strength); + int bonus = objects.GetModFromStatLevel(strScore); + if (bonus > 0) bonus += bonus/2; + + Dice dice(2, 6, bonus); dispIo->damage.AddDamageDice(dice.ToPacked(), DamageType::PiercingAndSlashing, 133); //damage.AddDamageDice(&dispIo->damage, dice.ToPacked(), DamageType::PiercingAndSlashing, 133); floatSys.FloatCombatLine(args.objHndCaller, 203); @@ -6279,6 +6351,24 @@ int ClassAbilityCallbacks::CouragedAuraSavingThrow(DispatcherCallbackArgs args) return 0; } +int ClassAbilityCallbacks::FailedCopyScroll(DispatcherCallbackArgs args) { + auto dispIo = dispatch.DispIoCheckIoType7(args.dispIO); + + auto failedScRanks = args.GetCondArg(1); + auto curScRanks = critterSys.SkillBaseGet(args.objHndCaller, SkillEnum::skill_spellcraft); + auto failedSpellEnum = args.GetCondArg(0); + + // if we've gained spellcraft ranks, get rid of the condition + if (curScRanks > failedScRanks) { + conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode); + // if the scroll being copied matches, set return value + } else if (failedSpellEnum == dispIo->data1) { + dispIo->return_val = 1; + } + + return 0; +} + int ClassAbilityCallbacks::FeatBrewPotionRadialMenu(DispatcherCallbackArgs args){ return ItemCreationBuildRadialMenuEntry(args, BrewPotion, "TAG_BREW_POTION", 5066); } @@ -7539,6 +7629,16 @@ void Conditions::AddConditionsToTable(){ } } + { + static CondStructNew condColorSprayStun; + condColorSprayStun.ExtendExisting("sp-Color Spray Stun"); + condColorSprayStun.AddHook(dispTypeD20Query, DK_QUE_SneakAttack, genericCallbacks.QuerySetReturnVal1); + + static CondStructNew condColorSprayBlind; + condColorSprayBlind.ExtendExisting("sp-Color Spray Blind"); + condColorSprayBlind.AddHook(dispTypeTurnBasedStatusInit, DK_NONE, TurnBasedStatusInitNoActions); + } + // New Conditions! conds.hashmethods.CondStructAddToHashtable((CondStruct*)conds.mConditionDisableAoO); conds.hashmethods.CondStructAddToHashtable((CondStruct*)conds.mCondGreaterTwoWeaponFighting); diff --git a/TemplePlus/condition.h b/TemplePlus/condition.h index 85bdde2df..c2264ce88 100644 --- a/TemplePlus/condition.h +++ b/TemplePlus/condition.h @@ -320,6 +320,9 @@ int RecklessOffenseAcPenalty(DispatcherCallbackArgs args); int RecklessOffenseToHitBonus(DispatcherCallbackArgs args); int TacticalOptionAbusePrevention(DispatcherCallbackArgs args); +int HeldCapStatBonus(DispatcherCallbackArgs args); +int HelplessCapStatBonus(DispatcherCallbackArgs args); + int CombatExpertiseRadialMenu(DispatcherCallbackArgs args); int CombatExpertiseSet(DispatcherCallbackArgs args); diff --git a/TemplePlus/d20.cpp b/TemplePlus/d20.cpp index a1d2b7985..343642cb6 100644 --- a/TemplePlus/d20.cpp +++ b/TemplePlus/d20.cpp @@ -75,6 +75,7 @@ class D20ActionCallbacks { // Action Checks static ActionErrorCode ActionCheckAidAnotherWakeUp(D20Actn* d20a, TurnBasedStatus* tbStat); static ActionErrorCode ActionCheckCastSpell(D20Actn* d20a, TurnBasedStatus* tbStat); + static ActionErrorCode ActionCheckCopyScroll(D20Actn* d20a, TurnBasedStatus* tbStat); static ActionErrorCode ActionCheckDisarm(D20Actn* d20a, TurnBasedStatus* tbStat); static ActionErrorCode ActionCheckDisarmedWeaponRetrieve(D20Actn* d20a, TurnBasedStatus* tbStat); static ActionErrorCode ActionCheckDivineMight(D20Actn* d20a, TurnBasedStatus* tbStat); @@ -88,6 +89,9 @@ class D20ActionCallbacks { static ActionErrorCode ActionCheckSunder(D20Actn* d20a, TurnBasedStatus* tbStat); static ActionErrorCode ActionCheckTripAttack(D20Actn* d20a, TurnBasedStatus* tbStat); + // Was part of an action check, but seems to be called directly from the + // Perform function. + static ActionErrorCode CanCopyScroll(D20Actn* d20a); // Target Check //static ActionErrorCode TargetCheckStandardAttack(D20Actn* d20a, TurnBasedStatus* tbStat); @@ -136,6 +140,7 @@ class D20ActionCallbacks { static BOOL ActionFrameAidAnotherWakeUp(D20Actn* d20a); static BOOL ActionFrameAoo(D20Actn* d20a); static BOOL ActionFrameCharge(D20Actn* d20a); + static BOOL ActionFrameCoupDeGrace(D20Actn* d20a); static BOOL ActionFrameDisarm(D20Actn* d20a); static BOOL ActionFramePython(D20Actn* d20a); static BOOL ActionFrameQuiveringPalm(D20Actn* d20a); @@ -496,6 +501,9 @@ void LegacyD20System::NewD20ActionsInit() d20Defs[d20Type].performFunc = d20Callbacks.PerformCharge; d20Defs[d20Type].actionFrameFunc = d20Callbacks.ActionFrameCharge; + d20Type = D20A_COUP_DE_GRACE; + d20Defs[d20Type].actionFrameFunc = d20Callbacks.ActionFrameCoupDeGrace; + d20Type = D20A_DEATH_TOUCH; d20Defs[d20Type].performFunc = d20Callbacks.PerformTouchAttack; @@ -521,10 +529,15 @@ void LegacyD20System::NewD20ActionsInit() d20Defs[d20Type].performFunc = d20Callbacks.PerformUseItem; d20Type = D20A_COPY_SCROLL; + d20Defs[d20Type].actionCheckFunc = d20Callbacks.ActionCheckCopyScroll; d20Defs[d20Type].performFunc = d20Callbacks.PerformCopyScroll; d20Type = D20A_DISMISS_SPELLS; d20Defs[d20Type].performFunc = d20Callbacks.PerformDismissSpell; + // Dismissing a spell is supposed to take a standard action + if (config.stricterRulesEnforcement) { + d20Defs[d20Type].actionCost = d20Callbacks.ActionCostStandardAction; + } d20Type = D20A_USE_POTION; d20Defs[d20Type].addToSeqFunc = d20Callbacks.AddToSeqSpellCast; @@ -1086,7 +1099,10 @@ int32_t LegacyD20System::D20ActionTriggersAoO(D20Actn* d20a, TurnBasedStatus* tb && d20QueryWithData(d20a->d20APerformer, DK_QUE_ActionTriggersAOO, (int)d20a, 0)) { if (d20a->d20ActType == D20A_DISARM) - return feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_DISARM) == 0; + if (!feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_DISARM)) + return 2; + else + return 0; return 1; } @@ -1100,21 +1116,27 @@ int32_t LegacyD20System::D20ActionTriggersAoO(D20Actn* d20a, TurnBasedStatus* tb return FALSE; if (feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_UNARMED_STRIKE)) return FALSE; - return feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_TRIP) == 0; + if (feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_TRIP)) + return FALSE; + return 2; } - - - - if (d20a->d20ActType == D20A_SUNDER) - return feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_SUNDER) == 0; + if (d20a->d20ActType == D20A_SUNDER) { + if (feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_SUNDER)) + return FALSE; + else + return 2; + } if (d20a->d20Caf & D20CAF_TOUCH_ATTACK || d20Sys.GetAttackWeapon(d20a->d20APerformer, d20a->data1, (D20CAF)d20a->d20Caf) || dispatch.DispatchD20ActionCheck(d20a, tbStat, dispTypeGetCritterNaturalAttacksNum)) return 0; - return feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_UNARMED_STRIKE) == 0; + if (feats.HasFeatCountByClass(d20a->d20APerformer, FEAT_IMPROVED_UNARMED_STRIKE)) + return 0; + else + return 2; /* __asm{ @@ -2572,12 +2594,17 @@ ActionErrorCode D20ActionCallbacks::PerformCharge(D20Actn* d20a){ ActionErrorCode D20ActionCallbacks::PerformCopyScroll(D20Actn * d20a){ auto performer = d20a->d20APerformer; - auto check = temple::GetRef(0x10091B80)(d20a); - if (check == AEC_INVALID_ACTION){ + switch (CanCopyScroll(d20a)) + { + case AEC_OK: + break; + case AEC_INVALID_ACTION: skillSys.FloatError(performer, 17); return AEC_OK; - } - else if (check!= AEC_OK){ + case AEC_CANNOT_CAST_NOT_ENOUGH_GP: + skillSys.FloatError(performer, 3); + return AEC_OK; + default: return AEC_OK; } @@ -2601,6 +2628,12 @@ ActionErrorCode D20ActionCallbacks::PerformCopyScroll(D20Actn * d20a){ return AEC_OK; } + if (config.stricterRulesEnforcement) { + if (party.IsInParty(performer)){ + party.DebitMoney(0, spLvl == 0 ? 100 : 100*spLvl, 0, 0); + } + } + spellSys.SpellKnownAdd(performer, spEnum, spellSys.GetSpellClass(stat_level_wizard), spLvl, 1, 0); auto scrollObj = inventory.GetItemAtInvIdx(performer, d20a->data1); if (scrollObj){ @@ -2633,6 +2666,31 @@ BOOL D20ActionCallbacks::ActionFrameCharge(D20Actn* d20a){ return FALSE; } +// version of 0x10090C80 that actually performs a fortitude save +// TODO: only allow regenerators to be killed by lethal damage? +BOOL D20ActionCallbacks::ActionFrameCoupDeGrace(D20Actn* d20a) { + auto performer = d20a->d20APerformer; + auto target = d20a->d20ATarget; + auto caf = static_cast(d20a->d20Caf); + auto dmg = damage.DealAttackDamage(performer, target, d20a->data1, caf, d20a->d20ActType); + + if (dmg <= 0 || critterSys.IsDeadNullDestroyed(target)) + return 0; + + auto dc = 10 + dmg; + + if (damage.SavingThrow(target, performer, dc, SavingThrowType::Fortitude, D20STF_NONE)) + return 0; + + auto leader = party.GetConsciousPartyLeader(); + auto desc = description.getDisplayName(target); + auto msg = fmt::format("{} {}\n", desc, combatSys.GetCombatMesLine(0xAE)).c_str(); + histSys.CreateFromFreeText(msg); + critterSys.Kill(target, performer); + + return 0; +} + BOOL D20ActionCallbacks::ActionFrameDisarm(D20Actn* d20a){ objHndl performer = d20a->d20APerformer; int failedOnce = 0; @@ -2902,12 +2960,23 @@ ActionErrorCode D20ActionCallbacks::PerformDisarmedWeaponRetrieve(D20Actn* d20a) d20Sys.d20SendSignal(d20a->d20APerformer, DK_SIG_Disarmed_Weapon_Retrieve, (int)d20a, 0); return AEC_OK; } + ActionErrorCode D20ActionCallbacks::PerformDismissSpell(D20Actn * d20a){ auto spellId = d20a->data1; - d20Sys.d20SendSignal(d20a->d20APerformer, DK_SIG_Dismiss_Spells, spellId, 0); SpellPacketBody spPkt(spellId); - if (!spPkt.spellEnum) + + // in case of bad spell + if (!spPkt.spellEnum) { + d20Sys.d20SendSignal(d20a->d20APerformer, DK_SIG_Dismiss_Spells, spellId, 0); return AEC_OK; + } + + if (gameSystems->GetAnim().PushSpellDismiss(spPkt)) { + d20a->animID = gameSystems->GetAnim().GetActionAnimId(d20a->d20APerformer); + d20a->d20Caf |= D20CAF_NEED_ANIM_COMPLETED; + } + + d20Sys.d20SendSignal(d20a->d20APerformer, DK_SIG_Dismiss_Spells, spellId, 0); if (spPkt.caster) d20Sys.d20SendSignal(spPkt.caster, DK_SIG_Dismiss_Spells, spellId, 0); @@ -3626,6 +3695,38 @@ ActionErrorCode D20ActionCallbacks::ActionCheckCastSpell(D20Actn* d20a, TurnBase return d20Sys.TargetCheck(d20a) != 0 ? AEC_OK : AEC_TARGET_INVALID; } +// Broke up and enhanced 0x10091B80 for better floats. +// Action check only checks for whether you have 'time' to copy the scroll. +ActionErrorCode D20ActionCallbacks::ActionCheckCopyScroll(D20Actn* d20a, TurnBasedStatus* tbStat) +{ + if (combatSys.isCombatActive()) + return AEC_OUT_OF_COMBAT_ONLY; + else + return AEC_OK; +} + +// Checks whether you have the resources, and haven't already failed to copy the +// scroll with the given spellcraft ranks. +ActionErrorCode D20ActionCallbacks::CanCopyScroll(D20Actn* d20a) { + auto performer = d20a->d20APerformer; + int spEnum = 0; + d20a->d20SpellData.Extract(&spEnum, nullptr, nullptr, nullptr, nullptr, nullptr); + + if (config.stricterRulesEnforcement && party.IsInParty(performer)) { + int spClass = spellSys.GetSpellClass(stat_level_wizard); + auto spLvl = spellSys.GetSpellLevelBySpellClass(spEnum, spClass); + auto gpCost = spLvl == 0 ? 100 : 100 * spLvl; + auto money = party.GetMoney(); + if (money >= 0 && money < gpCost*100) + return AEC_CANNOT_CAST_NOT_ENOUGH_GP; + } + + auto failed = d20Sys.d20QueryWithData(performer, DK_QUE_Failed_Copy_Scroll, spEnum, 0); + + if (failed) return AEC_INVALID_ACTION; + else return AEC_OK; +} + ActionErrorCode D20ActionCallbacks::AddToSeqCharge(D20Actn* d20a, ActnSeq* actSeq, TurnBasedStatus* tbStat){ auto tgt = d20a->d20ATarget; diff --git a/TemplePlus/d20_class.cpp b/TemplePlus/d20_class.cpp index 4dc3df5eb..44ae8f3f4 100644 --- a/TemplePlus/d20_class.cpp +++ b/TemplePlus/d20_class.cpp @@ -735,7 +735,7 @@ void D20ClassSystem::LevelupInitSpellSelection(objHndl handle, Stat classEnum, i int D20ClassHooks::HookedLvl1SkillPts(int intStatLvl){ - auto result = (intStatLvl - 10) / 2; + auto result = (intStatLvl - 10) >> 1; auto &selPkt = chargen.GetCharEditorSelPacket(); auto classLevelled = selPkt.classCode; result += d20ClassSys.GetSkillPts(classLevelled); diff --git a/TemplePlus/feat.cpp b/TemplePlus/feat.cpp index bf5e7277b..04b6c7d42 100644 --- a/TemplePlus/feat.cpp +++ b/TemplePlus/feat.cpp @@ -1104,6 +1104,7 @@ char* LegacyFeatSystem::GetFeatDescription(feat_enums feat) MesLine mesLine(5000 + feat); if (feat >= FEAT_NONE + || feat == FEAT_IMPROVED_SHIELD_BASH || feat == FEAT_IMPROVED_DISARM || feat == FEAT_GREATER_WEAPON_SPECIALIZATION || feat == FEAT_IMPROVED_SUNDER @@ -1126,7 +1127,9 @@ char* LegacyFeatSystem::GetFeatPrereqDescription(feat_enums feat) MesLine mesLineFeatDesc(10000 + feat); MesLine mesLineNone(9998); MesHandle * mesHnd = feats.featMes; + if (feat >= FEAT_NONE + || feat == FEAT_IMPROVED_SHIELD_BASH || feat == FEAT_IMPROVED_DISARM || feat == FEAT_GREATER_WEAPON_SPECIALIZATION || feat == FEAT_IMPROVED_SUNDER diff --git a/TemplePlus/python/python_dispatcher.cpp b/TemplePlus/python/python_dispatcher.cpp index 434b59da6..6017d3580 100644 --- a/TemplePlus/python/python_dispatcher.cpp +++ b/TemplePlus/python/python_dispatcher.cpp @@ -755,7 +755,7 @@ PYBIND11_EMBEDDED_MODULE(tpdp, m) { .value("Alchemy", Alchemy) .value("Movement", Movement) .value("Offense", Offense) - .value("Tacitical", Tactical) + .value("Tactical", Tactical) .value("Options", Options) .value("Potions", Potions) .value("Wands", Wands) diff --git a/TemplePlus/python/python_integration_feat.cpp b/TemplePlus/python/python_integration_feat.cpp index 2d100a740..680072d97 100644 --- a/TemplePlus/python/python_integration_feat.cpp +++ b/TemplePlus/python/python_integration_feat.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "python_integration_feat.h" #include "python_object.h" +#include PythonFeatIntegration pyFeatIntegration; @@ -10,6 +11,11 @@ PythonFeatIntegration::PythonFeatIntegration() bool PythonFeatIntegration::CheckPrereq(int featId, objHndl handle, Stat classCodeBeingLevelledUp, Stat abilityScoreBeingIncreased) { + if (featId < NUM_FEATS) { + auto featName = feats.GetFeatName(static_cast(featId)); + featId = ElfHash::Hash(featName); + } + auto featEntry = mScripts.find(featId); if (featEntry == mScripts.end()) return false; diff --git a/TemplePlus/python/python_object.cpp b/TemplePlus/python/python_object.cpp index 233ff7090..460672e7d 100644 --- a/TemplePlus/python/python_object.cpp +++ b/TemplePlus/python/python_object.cpp @@ -1840,6 +1840,15 @@ static PyObject* PyObjHandle_ConditionsGet(PyObject* obj, PyObject* args) { } } + // default to all conditions by being distinct from 0 and 1 + uint32_t active = 0xff; + if (PyTuple_GET_SIZE(args) > 1) { + PyObject* arg = PyTuple_GET_ITEM(args, 1); + if (PyInt_Check(arg) || PyLong_Check(arg)) { + active = PyInt_AsLong(arg); + } + } + CondNode* node = dispatcher->conditions; if (kind == 1) { node = dispatcher->permanentMods; @@ -1852,6 +1861,14 @@ static PyObject* PyObjHandle_ConditionsGet(PyObject* obj, PyObject* args) { auto list = PyList_New(0); while (node) { + // if active == 1 and inactive flag is set, skip + // if active == 0 and inactive flag isn't set, skip + // if active is anything else, don't skip + if ((node->flags & 1) == active) { + node = node->nextCondNode; + continue; + } + auto cname = PyString_FromString(node->condStruct->condName); auto tuple = PyTuple_New(2); PyTuple_SET_ITEM(tuple, 0, cname); diff --git a/TemplePlus/python/python_spell.cpp b/TemplePlus/python/python_spell.cpp index ca0c5e59c..19a9c510e 100644 --- a/TemplePlus/python/python_spell.cpp +++ b/TemplePlus/python/python_spell.cpp @@ -592,7 +592,9 @@ static PyObject* PySpell_GetTargetList(PyObject* obj, void*) { static PyObject* PySpell_GetSpellRadius(PyObject* obj, void*) { auto self = (PySpell*)obj; auto spellEntry = spellEntryRegistry.get(self->spellEnum); - return PyInt_FromLong(spellEntry->radiusTarget); + auto baseRadius = spellEntry->radiusTarget; + auto scale = 1 + self->metaMagic.metaMagicWidenSpellCount; + return PyInt_FromLong(baseRadius * scale); } static PyObject* PySpell_GetSpell(PyObject* obj, void*) { diff --git a/TemplePlus/spell_condition.cpp b/TemplePlus/spell_condition.cpp index 41b0be5bb..fdb80a8ef 100644 --- a/TemplePlus/spell_condition.cpp +++ b/TemplePlus/spell_condition.cpp @@ -318,6 +318,16 @@ class SpellConditionFixes : public TempleFix { // Grease fix for Freedom of Movement replaceFunction(0x100C8270, GreaseSlippage); + // Don't bother trying to 'break free' of Hold X while freedom of movement + // is active; it wastes your turn if you succeed. + static int (*origHoldBreakFree)(DispatcherCallbackArgs) = + replaceFunction(0x100C3FE0, + [](DispatcherCallbackArgs args) { + if (!d20Sys.d20Query(args.objHndCaller, DK_QUE_Critter_Has_Freedom_of_Movement)) + return origHoldBreakFree(args); + return 0; + }); + static int (*orgImmunityCheckHandler )(DispatcherCallbackArgs)= replaceFunction(0x100ED650, [](DispatcherCallbackArgs args) { if (!ImmunityCheckHandler(args)) diff --git a/TemplePlus/temple_enums.h b/TemplePlus/temple_enums.h index 571204b03..1acc27f86 100644 --- a/TemplePlus/temple_enums.h +++ b/TemplePlus/temple_enums.h @@ -2739,7 +2739,7 @@ enum SpellFlags : uint32_t SF_2000 = 0x2000, SF_4000 = 0x4000, SF_8000 = 0x8000, - SF_10000 = 0x10000, // Used in goalstatefunc_124 + SF_10000 = 0x10000, // Used in goalstatefunc_124, might be 'held' or similar SF_20000 = 0x20000, SF_40000 = 0x40000, SF_80000 = 0x80000, diff --git a/tpdata/tpmes/feat.mes b/tpdata/tpmes/feat.mes index 2ba34e40d..0c7b46477 100644 --- a/tpdata/tpmes/feat.mes +++ b/tpdata/tpmes/feat.mes @@ -1,3 +1,4 @@ +{216}{Improved Shield Bash} {340}{Improved Sunder} {649}{None} {650}{Exotic Weapon Proficiency} @@ -105,6 +106,7 @@ {5064} Greater Two Weapon Fighting {You get a third attack with your off-hand weapon, albeit at a -10 penalty.} {5137} Greater Weapon Specialization{You gain another +2 bonus on all damage rolls you make using the selected weapon, on top of the one from Weapon Specialization.} {5211} Improved Disarm{You do not provoke an attack of opportunity when you attempt to disarm an opponent, nor does the opponent have a chance to disarm you. You also gain a +4 bonus on the opposed attack roll you make to disarm your opponent.} +{5216} Improved Shield Bash {When you perform a shield bash, you may still apply the shield's shield bonus to your AC.} {5340} Improved Sunder{When you strike at an object held by an opponent, you do not provoke an attack of opportunity. You also gain a +4 bonus on the attack roll.} {5658} Diamond Body {You are immune to all poisons.} {5659} Abundant Step {You can slip magically between spaces, as if using the spell dimension door, once per day. To use this ability you must be wearing a Monk's Belt.} @@ -203,6 +205,7 @@ {10064}Greater Two Weapon Fighting {Dex 19, Improved Two-Weapon Fighting, Two-Weapon Fighting, base attack bonus +11.} {10137} Greater Weapon Specialization {Weapon Specialization + Greater Weapon Focus w/ selected weapon, fighter level 12th} {10211} Improved Disarm {Int 13, Combat Expertise.} +{10216} Improved Shield Bash {Shield Proficiency.} {10339} Stunning Fist {Dex 13, Wis 13, Improved Unarmed Strike, base attack bonus +8.} {10340} Improved Sunder {Str 13, Power Attack.} {10658} Diamond Body {} diff --git a/tpdata/tpmes/help_extensions.tab b/tpdata/tpmes/help_extensions.tab index f52a37112..c4c1716f6 100644 --- a/tpdata/tpmes/help_extensions.tab +++ b/tpdata/tpmes/help_extensions.tab @@ -109,4 +109,5 @@ TAG_SPELLS_ENERVATION TAG_SPELLS TAG_SORCERER_4 TAG_WIZARD_4 Enervation ~Necrom TAG_SPELLS_ENERGY_DRAIN TAG_SPELLS TAG_CLERIC_9 TAG_SORCERER_9 TAG_WIZARD_9 Energy Drain ~Necromancy~[TAG_MAGIC_SCHOOLS_NECROMANCY] ~Cleric~[TAG_CLERICS] 9, ~Sorcerer~[TAG_SORCERERS] 9, ~Wizard~[TAG_WIZARDS] 9 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Close (25 ft. + 5 ft./2 levels) Duration: Instantaneous This spell functions like ~enervation~[TAG_SPELLS_ENERVATION], except that the creature gains 2d4 ~negative levels~[TAG_SPECIAL_ABILITIES_ENERGY_DRAIN_AND_NEGATIVE_LEVELS] (double on a critical hit), and the negative levels last longer. There is no ~saving throw~[TAG_SAVING_THROW_DESC] to avoid gaining the negative levels, but 24 hours after gaining them, the subject must make a ~Fortitude~[TAG_FORTITUDE] saving throw (DC = the spell's save DC) for each negative level. If the save succeeds, that negative level is removed. If it fails, the negative level instead becomes a permanent negative level. A separate save must be made for each negative level. Like enervation, an undead creature struck by the ray instead gains 5 ~temporary hit points~[TAG_TEMPORARY_HIT_POINTS] for each negative level that would be bestowed. TAG_SPELLS_DISRUPTING_WEAPON TAG_SPELLS TAG_CLERIC_5 Disrupting Weapon ~Transmutation~[TAG_MAGIC_SCHOOLS_TRANSMUTATION] ~Cleric~[TAG_CLERICS] 5 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Touch Target: One melee weapon Duration: 1 round/level Saving Throw: No Spell Resistance: No This spell makes a melee weapon deadly to undead. When struck with the weapon, an undead creature with ~hit dice~[TAG_HIT_DICE] equal to or less than the caster level must succeed on a ~Will~[TAG_WILL] save or be destroyed. Spell resistance does not apply against the destruction effect. TAG_SPELLS_HOLD_PERSON TAG_SPELLS TAG_BARD_2 TAG_CLERIC_2 TAG_SORCERER_3 TAG_WIZARD_3 Hold Person ~Enchantment~[TAG_MAGIC_SCHOOLS_ENCHANTMENT] (Compulsion) Mind-Affecting Level: ~Bard~[TAG_BARDS] 2, ~Cleric~[TAG_CLERICS] 2, ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 3 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Medium (100 ft. + 10 ft./level) Target: One humanoid creature Duration: 1 round/level (D); see text Saving Throw: ~Will~[TAG_WILL] negates; see text Spell Resistance: Yes The subject becomes ~paralyzed~[TAG_PARALYZED] and freezes in place. It is aware and breathes normally but cannot take any actions, even speech. Each round on its turn, the subject may attempt a new ~saving throw~[TAG_SAVING_THROW_DESC] to end the effect. (In ToEE, this is a ~full-round action~[TAG_FULL_ROUND_ACTION] that does not provoke ~attacks of opportunity~[TAG_AOO].) +TAG_CLASS_FEATURES_WIZARD_SPELLBOOK TAG_WIZARDS Spellbook A wizard must study his spellbook each day to prepare his spells. A wizard begins play with a spellbook containing all 0-level wizard spells (except those from his prohibited schools, if any) plus two 1st-level spells of your choice. For each point of ~Intelligence~[TAG_INTELLIGENCE] bonus the wizard has, the spellbook holds one additional 1st-level spell of your choice. At each new wizard level, he gains two new spells of any spell level or levels that he can cast (based on his new wizard level) for his spellbook. A wizard can also copy spells into his spellbook from scrolls. To start, first the wizard needs at least 1 ~skill point~[TAG_ACQUIRE_SKILLS] in the ~Spellcraft~[TAG_SPELLCRAFT] skill for training. Then you select a scroll that the wizard has in his ~inventory~[TAG_HMU_CHAR_INVENTORY_UI] by using the ~radial menu~[TAG_RADIAL_MENU] and under the Person icon, select Copy Scroll. The wizard must make a Spellcraft check against a DC of: 15 + the spell's level If successful, the spell is entered into his spellbook and the scroll is destroyed. If unsuccessful, the scroll is unharmed, but the wizard may not try again until he gains at least one more rank in Spellcraft. ~Strict Rules~[TAG_STRICT_RULES] Scribing a spell uses up materials costing 100GP per level of the spell. Thus, a scribing a 5th level spell requires spending 500GP on materials to do so. This cost is in addition to the cost of the scroll being copied. The spells learned when a wizard levels up do not incur this cost. diff --git a/tpdata/tprules/feat_properties.tab b/tpdata/tprules/feat_properties.tab index b1a75b4d2..72153acdd 100644 --- a/tpdata/tprules/feat_properties.tab +++ b/tpdata/tprules/feat_properties.tab @@ -214,7 +214,7 @@ 213 Improved Grapple 18 1 13 1220 1 -1 -1 0 0 0 0 0 0 0 0 0 0 214 Improved Initiative 16 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 215 Improved Overrun 18 0 13 1268 1 -1 -1 0 0 0 0 0 0 0 0 0 0 -216 Improved Shield Bash 18 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +216 Improved Shield Bash 16 1278 1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 217 Improved Trip 144 3 13 1013 1 -1 -1 0 0 0 0 0 0 0 0 0 0 218 Improved Two-Weapon Fighting 16 1345 1 266 6 -1 -1 0 0 0 0 0 0 0 0 0 0 219 Improved Turning 0 -3 1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tpdatasrc/co8infra/scr/Spell170 - Finger of Death.py b/tpdatasrc/co8infra/scr/Spell170 - Finger of Death.py new file mode 100644 index 000000000..9ea9c3a2c --- /dev/null +++ b/tpdatasrc/co8infra/scr/Spell170 - Finger of Death.py @@ -0,0 +1,44 @@ +from toee import * +from utilities import * + +def OnBeginSpellCast( spell ): + print "Finger of Death OnBeginSpellCast" + print "spell.target_list=", spell.target_list + print "spell.caster=", spell.caster, " caster.level= ", spell.caster_level + game.particles( "sp-necromancy-conjure", spell.caster ) + +def OnSpellEffect ( spell ): + print "Finger of Death OnSpellEffect" + + damage_dice = dice_new( "3d6" ) + damage_dice.bonus = min( 25, spell.caster.stat_level_get( spell.caster_class ) ) + + target = spell.target_list[0] + + game.particles( 'sp-Slay Living', target.obj ) + + # damage target + if target.obj.saving_throw_spell( spell.dc, D20_Save_Fortitude, D20STD_F_NONE, spell.caster, spell.id ): + target.obj.float_mesfile_line( 'mes\\spell.mes', 30001 ) + + # saving throw succesful, damage target + target.obj.spell_damage( spell.caster, D20DT_NEGATIVE_ENERGY, damage_dice, D20DAP_UNSPECIFIED, D20A_CAST_SPELL, spell.id ) + else: + target.obj.float_mesfile_line( 'mes\\spell.mes', 30002 ) + + # saving throw unsuccesful, kill target + + # set attribute for proper XP award + if target.obj.type == obj_t_npc: + target.obj.obj_set_obj(obj_f_last_hit_by, spell.caster) + + target.obj.critter_kill_by_effect() + + spell.target_list.remove_target( target.obj ) + spell.spell_end(spell.id) + +def OnBeginRound( spell ): + print "Finger of Death OnBeginRound" + +def OnEndSpellCast( spell ): + print "Finger of Death OnEndSpellCast" diff --git a/tpdatasrc/co8infra/tpmes/help_extensions.tab b/tpdatasrc/co8infra/tpmes/help_extensions.tab index 8c22e4fd6..153ab218d 100644 --- a/tpdatasrc/co8infra/tpmes/help_extensions.tab +++ b/tpdatasrc/co8infra/tpmes/help_extensions.tab @@ -121,4 +121,5 @@ TAG_ULTIMATE_MAGUS TAG_PRESTIGE_CLASSES Ultimate Magus Sorcerers channel unkno TAG_CLASS_FEATURES_ULTIMATE_MAGUS TAG_ULTIMATE_MAGUS Ultimate Magus Base Attack & Base Save Bonuses Level ~Base Attack Bonus~[TAG_LEVEL_BONUSES] ~Fortitude~[TAG_FORTITUDE] ~Save~[TAG_LEVEL_BONUSES] ~Reflex~[TAG_REFLEX] ~Save~[TAG_LEVEL_BONUSES] ~Will~[TAG_WILL] ~Save~[TAG_LEVEL_BONUSES] 1 @t+0 @t+0 @t+0 @t+2 2 @t+1 @t+0 @t+0 @t+3 3 @t+1 @t+1 @t+1 @t+3 4 @t+2 @t+1 @t+1 @t+4 5 @t+2 @t+1 @t+1 @t+4 6 @t+3 @t+2 @t+2 @t+5 7 @t+3 @t+2 @t+2 @t+5 8 @t+4 @t+2 @t+2 @t+6 9 @t+4 @t+3 @t+3 @t+6 10 @t+5 @t+3 @t+3 @t+7 TAG_UNSEEN_SEER TAG_PRESTIGE_CLASSES Unseen Seer Mysterious and elusive, the unseen seer trades in secrets. Subterfuge is his business and he uses his magic to help his gather other people's secrets while keeping his own. Requirements: Skills: Hide 8 ranks, Search 8 ranks, Sense Motive 4 ranks, Spellcraft 4 ranks, Spot 8 ranks Spellcasting: Ability to cast 1st-level arcane spells, including at least two divination spells. Hit Die: d4 Base Attack and Base Save Bonuses: see ~table~[TAG_CLASS_FEATURES_UNSEEN_SEER]. Class Skills: ~Bluff~[TAG_BLUFF], ~Concentration~[TAG_CONCENTRATION], ~Diplomacy~[TAG_DIPLOMACY], ~Disable Device~[TAG_DISABLE_DEVICE], ~Gather Information~[TAG_GATHER_INFORMATION], ~Hide~[TAG_HIDE], ~Listen~[TAG_LISTEN], ~Move Silently~[TAG_MOVE_SILENTLY], ~Open Lock~[TAG_OPEN_LOCK], ~Search~[TAG_SEARCH], ~Sense Motive~[TAG_SENSE_MOTIVE], ~Spot~[TAG_SPOT], ~Spellcraft~[TAG_SPELLCRAFT], ~Spot~[TAG_SPOT]. Skill Points at Each Level: 6 + Int modifier Class Features: Weapon and Armor Proficiency: Unseen Seers gain no proficiency with ayn weapon or armor. Spells per Day: When a new unseen seer level is gained, the character gains new spells per day as if he had also gained a level in a spellcasting class he belonged to before adding the prestige class. He does not, however, gain any other benefit a character of that class would have gained (improved chance of controlling or rebuking undead, metamagic or item creation feats, and so on), except for an increased effective level of spellcasting. If a character had more than one spellcasting class before becoming an arcane trickster, the highest one is chosen for adding the new level for the purposes of determining spells per day. Note: Spells that require an attack roll, such as Chill Touch, are considered weapon-like spells and as such benefit from Sneak Attack or Skirmish damage. Damage Bonus: At 1st level, the extra damage you deal with your sneak attack, skirmish, or sudden strike ability increases by 1d6. If you have more than one of these abilities, only one ability gains this increase (choose each time you gain this benefit).Your sneak attack, skirmish, or sudden strike damage increases by another 1d6 at 4th level, 7th level, and 10th level. Advanced Learning (Ex): At 2nd, 5th, and 8th level, you can add a new spell to your spellbook or list of spells known, representing the result of personal study and experimentation. The spell must be a divination spell of a level no higher than that of the highest-level arcane spell you already know. The spell can be from any class's spell list (arcane or divine). Once a new spell is selected, it is forever added to your spell list and can be cast just like any other spell on your list. Select the spell after levelup through the radial menu. Silent Spell: At 2nd level, you gain Silent Spell as a bonus feat. Divination Spell Power (Ex): At 3rd level, you gain a +1 bonus to your caster level when casting an arcane divination spell. This bonus improves to +2 at 6th level, and to +3 at 9th level.This benefit comes at a cost: Your caster level for all other arcane spells is reduced by 1 at 3rd level. This reduction becomes 2 at 6th level and becomes 3 at 9th level. For example, a 4th-level rogue/1st-level sorcerer/6th-level unseen seer would have a caster level of 9th for his arcane divination spells, but only 5th for his nondivination arcane spells. Guarded Mind (Su): Any successful unseen seer must learn to protect himself from magic that would reveal his identity. At 5th level, you become protected by nondetection (as the spell, but with a permanent duration). For the purpose of divinations attempted against you, your caster level equals your character level (no in game effect). TAG_CLASS_FEATURES_UNSEEN_SEER TAG_DUMMY Unseen Seer Base Attack & Base Save Bonuses Level ~Base Attack Bonus~[TAG_LEVEL_BONUSES] ~Fortitude~[TAG_FORTITUDE] ~Save~[TAG_LEVEL_BONUSES] ~Reflex~[TAG_REFLEX] ~Save~[TAG_LEVEL_BONUSES] ~Will~[TAG_WILL] ~Save~[TAG_LEVEL_BONUSES] 1 @t+0 @t+0 @t+0 @t+2 2 @t+1 @t+0 @t+0 @t+3 3 @t+2 @t+1 @t+1 @t+3 4 @t+3 @t+1 @t+1 @t+4 5 @t+3 @t+1 @t+1 @t+4 6 @t+4 @t+2 @t+2 @t+5 7 @t+5 @t+2 @t+2 @t+5 8 @t+6 @t+2 @t+2 @t+6 9 @t+6 @t+3 @t+3 @t+6 10 @t+7 @t+3 @t+3 @t+7 +TAG_CLASS_FEATURES_WIZARD_SPELLBOOK TAG_WIZARDS Spellbook A wizard must study his spellbook each day to prepare his spells. A wizard begins play with a spellbook containing all 0-level wizard spells (except those from his prohibited schools, if any) plus two 1st-level spells of your choice. For each point of ~Intelligence~[TAG_INTELLIGENCE] bonus the wizard has, the spellbook holds one additional 1st-level spell of your choice. At each new wizard level, he gains two new spells of any spell level or levels that he can cast (based on his new wizard level) for his spellbook. A wizard can also copy spells into his spellbook from scrolls. To start, first the wizard needs at least 1 ~skill point~[TAG_ACQUIRE_SKILLS] in the ~Spellcraft~[TAG_SPELLCRAFT] skill for training. Then you select a scroll that the wizard has in his ~inventory~[TAG_HMU_CHAR_INVENTORY_UI] by using the ~radial menu~[TAG_RADIAL_MENU] and under the Person icon, select Copy Scroll. The wizard must make a Spellcraft check against a DC of: 15 + the spell's level If successful, the spell is entered into his spellbook and the scroll is destroyed. If unsuccessful, the scroll is unharmed, but the wizard may not try again until he gains at least one more rank in Spellcraft. ~Strict Rules~[TAG_STRICT_RULES] Scribing a spell uses up materials costing 100GP per level of the spell. Thus, a scribing a 5th level spell requires spending 500GP on materials to do so. This cost is in addition to the cost of the scroll being copied. The spells learned when a wizard levels up do not incur this cost. diff --git a/tpdatasrc/kotbfixes/tpmes/help_extensions.tab b/tpdatasrc/kotbfixes/tpmes/help_extensions.tab index 8c22e4fd6..153ab218d 100644 --- a/tpdatasrc/kotbfixes/tpmes/help_extensions.tab +++ b/tpdatasrc/kotbfixes/tpmes/help_extensions.tab @@ -121,4 +121,5 @@ TAG_ULTIMATE_MAGUS TAG_PRESTIGE_CLASSES Ultimate Magus Sorcerers channel unkno TAG_CLASS_FEATURES_ULTIMATE_MAGUS TAG_ULTIMATE_MAGUS Ultimate Magus Base Attack & Base Save Bonuses Level ~Base Attack Bonus~[TAG_LEVEL_BONUSES] ~Fortitude~[TAG_FORTITUDE] ~Save~[TAG_LEVEL_BONUSES] ~Reflex~[TAG_REFLEX] ~Save~[TAG_LEVEL_BONUSES] ~Will~[TAG_WILL] ~Save~[TAG_LEVEL_BONUSES] 1 @t+0 @t+0 @t+0 @t+2 2 @t+1 @t+0 @t+0 @t+3 3 @t+1 @t+1 @t+1 @t+3 4 @t+2 @t+1 @t+1 @t+4 5 @t+2 @t+1 @t+1 @t+4 6 @t+3 @t+2 @t+2 @t+5 7 @t+3 @t+2 @t+2 @t+5 8 @t+4 @t+2 @t+2 @t+6 9 @t+4 @t+3 @t+3 @t+6 10 @t+5 @t+3 @t+3 @t+7 TAG_UNSEEN_SEER TAG_PRESTIGE_CLASSES Unseen Seer Mysterious and elusive, the unseen seer trades in secrets. Subterfuge is his business and he uses his magic to help his gather other people's secrets while keeping his own. Requirements: Skills: Hide 8 ranks, Search 8 ranks, Sense Motive 4 ranks, Spellcraft 4 ranks, Spot 8 ranks Spellcasting: Ability to cast 1st-level arcane spells, including at least two divination spells. Hit Die: d4 Base Attack and Base Save Bonuses: see ~table~[TAG_CLASS_FEATURES_UNSEEN_SEER]. Class Skills: ~Bluff~[TAG_BLUFF], ~Concentration~[TAG_CONCENTRATION], ~Diplomacy~[TAG_DIPLOMACY], ~Disable Device~[TAG_DISABLE_DEVICE], ~Gather Information~[TAG_GATHER_INFORMATION], ~Hide~[TAG_HIDE], ~Listen~[TAG_LISTEN], ~Move Silently~[TAG_MOVE_SILENTLY], ~Open Lock~[TAG_OPEN_LOCK], ~Search~[TAG_SEARCH], ~Sense Motive~[TAG_SENSE_MOTIVE], ~Spot~[TAG_SPOT], ~Spellcraft~[TAG_SPELLCRAFT], ~Spot~[TAG_SPOT]. Skill Points at Each Level: 6 + Int modifier Class Features: Weapon and Armor Proficiency: Unseen Seers gain no proficiency with ayn weapon or armor. Spells per Day: When a new unseen seer level is gained, the character gains new spells per day as if he had also gained a level in a spellcasting class he belonged to before adding the prestige class. He does not, however, gain any other benefit a character of that class would have gained (improved chance of controlling or rebuking undead, metamagic or item creation feats, and so on), except for an increased effective level of spellcasting. If a character had more than one spellcasting class before becoming an arcane trickster, the highest one is chosen for adding the new level for the purposes of determining spells per day. Note: Spells that require an attack roll, such as Chill Touch, are considered weapon-like spells and as such benefit from Sneak Attack or Skirmish damage. Damage Bonus: At 1st level, the extra damage you deal with your sneak attack, skirmish, or sudden strike ability increases by 1d6. If you have more than one of these abilities, only one ability gains this increase (choose each time you gain this benefit).Your sneak attack, skirmish, or sudden strike damage increases by another 1d6 at 4th level, 7th level, and 10th level. Advanced Learning (Ex): At 2nd, 5th, and 8th level, you can add a new spell to your spellbook or list of spells known, representing the result of personal study and experimentation. The spell must be a divination spell of a level no higher than that of the highest-level arcane spell you already know. The spell can be from any class's spell list (arcane or divine). Once a new spell is selected, it is forever added to your spell list and can be cast just like any other spell on your list. Select the spell after levelup through the radial menu. Silent Spell: At 2nd level, you gain Silent Spell as a bonus feat. Divination Spell Power (Ex): At 3rd level, you gain a +1 bonus to your caster level when casting an arcane divination spell. This bonus improves to +2 at 6th level, and to +3 at 9th level.This benefit comes at a cost: Your caster level for all other arcane spells is reduced by 1 at 3rd level. This reduction becomes 2 at 6th level and becomes 3 at 9th level. For example, a 4th-level rogue/1st-level sorcerer/6th-level unseen seer would have a caster level of 9th for his arcane divination spells, but only 5th for his nondivination arcane spells. Guarded Mind (Su): Any successful unseen seer must learn to protect himself from magic that would reveal his identity. At 5th level, you become protected by nondetection (as the spell, but with a permanent duration). For the purpose of divinations attempted against you, your caster level equals your character level (no in game effect). TAG_CLASS_FEATURES_UNSEEN_SEER TAG_DUMMY Unseen Seer Base Attack & Base Save Bonuses Level ~Base Attack Bonus~[TAG_LEVEL_BONUSES] ~Fortitude~[TAG_FORTITUDE] ~Save~[TAG_LEVEL_BONUSES] ~Reflex~[TAG_REFLEX] ~Save~[TAG_LEVEL_BONUSES] ~Will~[TAG_WILL] ~Save~[TAG_LEVEL_BONUSES] 1 @t+0 @t+0 @t+0 @t+2 2 @t+1 @t+0 @t+0 @t+3 3 @t+2 @t+1 @t+1 @t+3 4 @t+3 @t+1 @t+1 @t+4 5 @t+3 @t+1 @t+1 @t+4 6 @t+4 @t+2 @t+2 @t+5 7 @t+5 @t+2 @t+2 @t+5 8 @t+6 @t+2 @t+2 @t+6 9 @t+6 @t+3 @t+3 @t+6 10 @t+7 @t+3 @t+3 @t+7 +TAG_CLASS_FEATURES_WIZARD_SPELLBOOK TAG_WIZARDS Spellbook A wizard must study his spellbook each day to prepare his spells. A wizard begins play with a spellbook containing all 0-level wizard spells (except those from his prohibited schools, if any) plus two 1st-level spells of your choice. For each point of ~Intelligence~[TAG_INTELLIGENCE] bonus the wizard has, the spellbook holds one additional 1st-level spell of your choice. At each new wizard level, he gains two new spells of any spell level or levels that he can cast (based on his new wizard level) for his spellbook. A wizard can also copy spells into his spellbook from scrolls. To start, first the wizard needs at least 1 ~skill point~[TAG_ACQUIRE_SKILLS] in the ~Spellcraft~[TAG_SPELLCRAFT] skill for training. Then you select a scroll that the wizard has in his ~inventory~[TAG_HMU_CHAR_INVENTORY_UI] by using the ~radial menu~[TAG_RADIAL_MENU] and under the Person icon, select Copy Scroll. The wizard must make a Spellcraft check against a DC of: 15 + the spell's level If successful, the spell is entered into his spellbook and the scroll is destroyed. If unsuccessful, the scroll is unharmed, but the wizard may not try again until he gains at least one more rank in Spellcraft. ~Strict Rules~[TAG_STRICT_RULES] Scribing a spell uses up materials costing 100GP per level of the spell. Thus, a scribing a 5th level spell requires spending 500GP on materials to do so. This cost is in addition to the cost of the scroll being copied. The spells learned when a wizard levels up do not incur this cost. diff --git a/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Hunter's Eye.tga b/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Hunter's Eye.tga new file mode 100644 index 000000000..c3e31916c Binary files /dev/null and b/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Hunter's Eye.tga differ diff --git a/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Sure Strike.tga b/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Sure Strike.tga new file mode 100644 index 000000000..adad27ef3 Binary files /dev/null and b/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Sure Strike.tga differ diff --git a/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Tactical Precision.tga b/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Tactical Precision.tga new file mode 100644 index 000000000..5cf6120c8 Binary files /dev/null and b/tpdatasrc/tpgamefiles/art/interface/Player_Conditions/Buffs/Tactical Precision.tga differ diff --git a/tpdatasrc/tpgamefiles/art/meshes/Particle/crossed_swords.mdf b/tpdatasrc/tpgamefiles/art/meshes/Particle/crossed_swords.mdf new file mode 100644 index 000000000..2dc9750e5 --- /dev/null +++ b/tpdatasrc/tpgamefiles/art/meshes/Particle/crossed_swords.mdf @@ -0,0 +1,3 @@ +Textured +Texture "art/meshes/particle/crossed_swords.tga" +Color 255 255 255 255 diff --git a/tpdatasrc/tpgamefiles/art/meshes/Particle/crossed_swords.tga b/tpdatasrc/tpgamefiles/art/meshes/Particle/crossed_swords.tga new file mode 100644 index 000000000..8a65a6ee1 Binary files /dev/null and b/tpdatasrc/tpgamefiles/art/meshes/Particle/crossed_swords.tga differ diff --git a/tpdatasrc/tpgamefiles/art/meshes/Particle/hunters_eye.mdf b/tpdatasrc/tpgamefiles/art/meshes/Particle/hunters_eye.mdf new file mode 100644 index 000000000..c447e9cc6 --- /dev/null +++ b/tpdatasrc/tpgamefiles/art/meshes/Particle/hunters_eye.mdf @@ -0,0 +1,3 @@ +Textured +Texture "art/meshes/particle/hunters_eye.tga" +Color 255 255 255 255 diff --git a/tpdatasrc/tpgamefiles/art/meshes/Particle/hunters_eye.tga b/tpdatasrc/tpgamefiles/art/meshes/Particle/hunters_eye.tga new file mode 100644 index 000000000..ffc606fb8 Binary files /dev/null and b/tpdatasrc/tpgamefiles/art/meshes/Particle/hunters_eye.tga differ diff --git a/tpdatasrc/tpgamefiles/mes/damage_ext.mes b/tpdatasrc/tpgamefiles/mes/damage_ext.mes index 1e4fb3627..c5c8f745e 100644 --- a/tpdatasrc/tpgamefiles/mes/damage_ext.mes +++ b/tpdatasrc/tpgamefiles/mes/damage_ext.mes @@ -13,6 +13,7 @@ {3006}{~Undead Bane Weapon~[TAG_SPELLS_UNDEAD_BANE_WEAPON]} {3007}{~Lawful Sword~[TAG_SPELLS_LAWFUL_SWORD]} {3008}{~Weapon of Energy~[TAG_SPELLS_WEAPON_OF_ENERGY]} +{3009}{~Tactical Precision~[TAG_SPELLS_TACTICAL_PRECISION]} {3010}{~Blades of Fire~[TAG_SPELLS_BLADES_OF_FIRE]} // Feat Damage effects diff --git a/tpdatasrc/tpgamefiles/mes/help/new_feats_help.tab b/tpdatasrc/tpgamefiles/mes/help/new_feats_help.tab index 398cdb18d..60f280c14 100644 --- a/tpdatasrc/tpgamefiles/mes/help/new_feats_help.tab +++ b/tpdatasrc/tpgamefiles/mes/help/new_feats_help.tab @@ -1,10 +1,14 @@ -TAG_AXIOMATIC_STRIKE TAG_FEATS_DES Axiomatic Strike You can turn your fist into an instrument of law. Prerequisite: ~Stunning Fist~[TAG_STUNNING_FIST], ~Ki strike~[TAG_CLASS_FEATURES_MONK_KI_STRIKE] (lawful) Benefit: Against a chaotic opponent, you can make an unarmed attack that does an extra 2d6 points of damage. You must declare that you are using this feat before you make your attack roll (thus, a failed attack ruins the attempt). Each attempt counts as one of your uses of the Stunning Fist feat for the day. Creatures immune to stunning can be affected by this extra damage. -TAG_BRUTAL_THROW TAG_FEATS_DES Brutal Throw You have learned how to hurl weapons to deadly effect. Prerequisite: Benefit: You can add your Strength modifier (instead of your Dexterity modifier) to ~attack rolls~[TAG_RANGED_ATTACKS] with thrown weapons. Special: A fighter may select Brutal Throw as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. Normal: A character attacking with a ranged weapon adds his Dexterity modifier to the attack roll. -TAG_FIERY_FIST TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Fiery Fist By channeling your Ki energy, you sheathe your limbs in magical fire. Your unarmed strikes deal extra fire damage. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Dexterity~[TAG_DEXTERITY] 13, ~Wisdom~[TAG_WISDOM] 13, base attack bonus +8 Benefit: As a swift action, you can expend one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat to surround your fists and feet in flame. For the rest of your turn, you gain an extra 1d6 points of firedamage on your unarmed strikes. When you select this feat, you gain an additional daily use of Stunning Fist. Special: A fighter can select Fiery Fist as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. A monk with the Stunning Fist feat can select Fiery Fist as her bonus feat at 2nd level, even if she does not meet the other prerequisites. -TAG_FIERY_KI_DEFENSE TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Fiery Ki Defense You channel your ki energy into a cloak of fl ame that injures all who attempt to strike you. Prerequisite: ~Fiery Fist~[TAG_FIERY_FIST], ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Dexterity~[TAG_DEXTERITY] 13, ~Wisdom~[TAG_WISDOM] 13, base attack bonus +8 Benefit: As a swift action, you can expend one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat to cloak yourself in flame. Any creature that strikes you with a ~melee attack~[TAG_MELEE_ATTACKS] takes 1d6 points of fire damage. This benefit lasts until the start of your next turn. Special: A fighter can select Fiery Ki Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. A monk with the Stunning Fist feat can select Fiery Ki Defense as her bonus feat at 6th level, as long as she also possesses the Fiery Fist feat (other prerequisites can be ignored). -TAG_FISTS_OF_IRON TAG_FEATS_DES Fists of Iron You have learned the secrets of imbuing your unarmed attacks with extra force. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], base attack bonus +2, Benefit: Declare that you are using this feat before you make your attack roll (thus, a missed attack roll ruins the attempt). You deal an extra 1d6 points of damage when you make a successful unarmed attack. Each attempt counts as one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat for the day. -TAG_FLYING_KICK TAG_FEATS_DES Flying Kick You literally leap into battle, dealing devastating damage. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Power Attack~[TAG_POWER_ATTACK], ~Strength~[TAG_STRENGTH] 13, Jump 4 ranks (omitted as jump is not in the game) Benefit: When fighting unarmed and using the ~charge~[TAG_CHARGE] action, you deal an extra 1d12 points of damage with your unarmed attack. -TAG_FREEZING_THE_LIFEBLOOD TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Freezing the Lifeblood You can paralyze a humanoid opponent with an unarmed attack. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Wisdom~[TAG_WISDOM] 17, base attack bonus +10 Benefit: Declare that you are using this feat before you make your attack roll (thus, a missed attack roll ruins the attempt). Against a humanoid opponent, you can make an unarmed attack that deals no damage but has a chance of ~paralyzing~[TAG_PARALYZED] your target. If your attack is successful, your target must attempt a Fortitude saving throw (DC 10 + 1/2 your character level + your Wis modifier). If the target fails this saving throw, it is paralyzed for 1d4+1 rounds. Each attempt to paralyze an opponent counts as one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat for the day. Creatures immune to stunning cannot be paralyzed in this manner. Special: A fighter can select Fiery Ki Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. -TAG_KI_BLAST TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Ki Blast You focus your ki into a ball of energy that you can hurl at an opponent. Prerequisite: ~Fiery Fist~[TAG_FIERY_FIST], ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Dexterity~[TAG_DEXTERITY] 13, ~Wisdom~[TAG_WISDOM] 13, base attack bonus +8 Benefit: You can expend two daily uses of your ~Stunning Fist~[TAG_STUNNING_FIST] feat as a move action to create an orb of raw ki energy. You can then throw the seething orb as a standard action with a range of 60 feet. This ranged touch attack deals damage equal to 3d6 points + your Wis modifier. The ki orb is a force effect. If you fail to throw the orb before the end of your turn, it dissipates harmlessly. When you take this feat, you gain an additional daily use of ~Stunning Fist~[TAG_STUNNING_FIST]. Special: A fighter can select Fiery Ki Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. A monk with the Stunning Fist feat can select Ki Blast as her bonus feat at 6th level, as long as she also possesses the Fiery Fist feat (other prerequisites can be ignored). -TAG_POWER_THROW TAG_FEATS_DES Power Throw You have learned how to hurl weapons to deadly effect. Prerequisite: ~Strength~[TAG_STRENGTH] 13, ~Brutal Throw~[TAG_BRUTAL_THROW], ~Power Attack~[TAG_POWER_ATTACK] Benefit: On your turn, before making any attack rolls, you can choose to subtract a number from all ~thrown weapon attack rolls~[TAG_RANGED_ATTACKS] and add the same number to all thrown weapon damage rolls. This number may not exceed your base attack bonus. The penalty on attack rolls and the bonus on damage rolls applies until your next turn. Special: A fighter may select Brutal Throw as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. -TAG_ZEN_ARCHERY TAG_FEATS_DES Zen Archery Your intuition guides your hand when you use a ranged weapon. Prerequisite: ~Wisdom~[TAG_WISDOM] 13, base attack bonus +1 Benefit: You can use your Wisdom modifier instead of your Dexterity modifier when making a ~ranged attack~[TAG_RANGED_ATTACKS] roll. +TAG_ACTIVE_SHIELD_DEFENSE TAG_FEATS_DES Active Shield Defense Your expert use of your shield allows you to strike at vulnerable foes even when you forgo your own attacks in favor of defense. Prerequisite: ~Proficiency with Shields~[TAG_SHIELD_PROF], ~Shield Specialization~[TAG_SHIELD_SPECIALIZATION]. Benefit: When ~fighting defensively~[TAG_RADIAL_MENU_FIGHT_DEFENSIVELY] and using a shield, you do not take the standard fighting defensively penalties on ~attacks of opportunity~[TAG_AOO]. When using the ~total defense~[TAG_RADIAL_MENU_TOTAL_DEFENSE] action and a shield, you still threaten the area around you as normal. You can make ~attacks of opportunity~[TAG_AOO] with a -4 penalty. Normal: You take a -4 penalty on all attacks while fighting defensively. You cannot attack while using the total defense action. Special: A fighter can select Active Shield Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. +TAG_AXIOMATIC_STRIKE TAG_FEATS_DES Axiomatic Strike You can turn your fist into an instrument of law. Prerequisite: ~Stunning Fist~[TAG_STUNNING_FIST], ~Ki strike~[TAG_CLASS_FEATURES_MONK_KI_STRIKE] (lawful) Benefit: Against a chaotic opponent, you can make an unarmed attack that does an extra 2d6 points of damage. You must declare that you are using this feat before you make your attack roll (thus, a failed attack ruins the attempt). Each attempt counts as one of your uses of the Stunning Fist feat for the day. Creatures immune to stunning can be affected by this extra damage. +TAG_BRUTAL_THROW TAG_FEATS_DES Brutal Throw You have learned how to hurl weapons to deadly effect. Prerequisite: Benefit: You can add your Strength modifier (instead of your Dexterity modifier) to ~attack rolls~[TAG_RANGED_ATTACKS] with thrown weapons. Special: A fighter may select Brutal Throw as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. Normal: A character attacking with a ranged weapon adds his Dexterity modifier to the attack roll. +TAG_FIERY_FIST TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Fiery Fist By channeling your Ki energy, you sheathe your limbs in magical fire. Your unarmed strikes deal extra fire damage. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Dexterity~[TAG_DEXTERITY] 13, ~Wisdom~[TAG_WISDOM] 13, base attack bonus +8 Benefit: As a swift action, you can expend one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat to surround your fists and feet in flame. For the rest of your turn, you gain an extra 1d6 points of firedamage on your unarmed strikes. When you select this feat, you gain an additional daily use of Stunning Fist. Special: A fighter can select Fiery Fist as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. A monk with the Stunning Fist feat can select Fiery Fist as her bonus feat at 2nd level, even if she does not meet the other prerequisites. +TAG_FIERY_KI_DEFENSE TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Fiery Ki Defense You channel your ki energy into a cloak of fl ame that injures all who attempt to strike you. Prerequisite: ~Fiery Fist~[TAG_FIERY_FIST], ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Dexterity~[TAG_DEXTERITY] 13, ~Wisdom~[TAG_WISDOM] 13, base attack bonus +8 Benefit: As a swift action, you can expend one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat to cloak yourself in flame. Any creature that strikes you with a ~melee attack~[TAG_MELEE_ATTACKS] takes 1d6 points of fire damage. This benefit lasts until the start of your next turn. Special: A fighter can select Fiery Ki Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. A monk with the Stunning Fist feat can select Fiery Ki Defense as her bonus feat at 6th level, as long as she also possesses the Fiery Fist feat (other prerequisites can be ignored). +TAG_FISTS_OF_IRON TAG_FEATS_DES Fists of Iron You have learned the secrets of imbuing your unarmed attacks with extra force. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], base attack bonus +2, Benefit: Declare that you are using this feat before you make your attack roll (thus, a missed attack roll ruins the attempt). You deal an extra 1d6 points of damage when you make a successful unarmed attack. Each attempt counts as one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat for the day. +TAG_FLYING_KICK TAG_FEATS_DES Flying Kick You literally leap into battle, dealing devastating damage. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Power Attack~[TAG_POWER_ATTACK], ~Strength~[TAG_STRENGTH] 13, Jump 4 ranks (omitted as jump is not in the game) Benefit: When fighting unarmed and using the ~charge~[TAG_CHARGE] action, you deal an extra 1d12 points of damage with your unarmed attack. +TAG_FREEZING_THE_LIFEBLOOD TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Freezing the Lifeblood You can paralyze a humanoid opponent with an unarmed attack. Prerequisite: ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Wisdom~[TAG_WISDOM] 17, base attack bonus +10 Benefit: Declare that you are using this feat before you make your attack roll (thus, a missed attack roll ruins the attempt). Against a humanoid opponent, you can make an unarmed attack that deals no damage but has a chance of ~paralyzing~[TAG_PARALYZED] your target. If your attack is successful, your target must attempt a Fortitude saving throw (DC 10 + 1/2 your character level + your Wis modifier). If the target fails this saving throw, it is paralyzed for 1d4+1 rounds. Each attempt to paralyze an opponent counts as one of your uses of the ~Stunning Fist~[TAG_STUNNING_FIST] feat for the day. Creatures immune to stunning cannot be paralyzed in this manner. Special: A fighter can select Fiery Ki Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. +TAG_KI_BLAST TAG_FEATS_DES TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS Ki Blast You focus your ki into a ball of energy that you can hurl at an opponent. Prerequisite: ~Fiery Fist~[TAG_FIERY_FIST], ~Improved Unarmed Strike~[TAG_CLASS_FEATURES_MONK_UNARMED_STRIKE], ~Stunning Fist~[TAG_STUNNING_FIST], ~Dexterity~[TAG_DEXTERITY] 13, ~Wisdom~[TAG_WISDOM] 13, base attack bonus +8 Benefit: You can expend two daily uses of your ~Stunning Fist~[TAG_STUNNING_FIST] feat as a move action to create an orb of raw ki energy. You can then throw the seething orb as a standard action with a range of 60 feet. This ranged touch attack deals damage equal to 3d6 points + your Wis modifier. The ki orb is a force effect. If you fail to throw the orb before the end of your turn, it dissipates harmlessly. When you take this feat, you gain an additional daily use of ~Stunning Fist~[TAG_STUNNING_FIST]. Special: A fighter can select Fiery Ki Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. A monk with the Stunning Fist feat can select Ki Blast as her bonus feat at 6th level, as long as she also possesses the Fiery Fist feat (other prerequisites can be ignored). +TAG_POWER_THROW TAG_FEATS_DES Power Throw You have learned how to hurl weapons to deadly effect. Prerequisite: ~Strength~[TAG_STRENGTH] 13, ~Brutal Throw~[TAG_BRUTAL_THROW], ~Power Attack~[TAG_POWER_ATTACK] Benefit: On your turn, before making any attack rolls, you can choose to subtract a number from all ~thrown weapon attack rolls~[TAG_RANGED_ATTACKS] and add the same number to all thrown weapon damage rolls. This number may not exceed your base attack bonus. The penalty on attack rolls and the bonus on damage rolls applies until your next turn. Special: A fighter may select Brutal Throw as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. +TAG_SHIELD_CHARGE TAG_FEATS_DES Shield Charge You deal extra damage if you use your shield as a weapon when charging. Prerequisite: ~Proficiency with Shields~[TAG_SHIELD_PROF], ~Improved Shield Bash~[TAG_IMPROVED_SHIELD_BASH], Base Attack Bonus +3. Benefit: If you hit an opponent with your shield as part of a ~charge action~[TAG_CHARGE], in addition to dealing damage normally, you may make a ~trip attack~[TAG_TRIP] without provoking an ~attack of opportunity~[TAG_AOO]. If you lose, the defender does not get to try to trip you in return. Special: A fighter can select Active Shield Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. +TAG_SHIELD_SPECIALIZATION TAG_FEATS_DES Shield Specialization You are skilled in using a shield, allowing you to gain greater defensive benefits from it. Prerequisite: ~Proficiency with Shields~[TAG_SHIELD_PROF] Benefit: When using a shield, you increase its shield bonus to AC by 1. Special: A fighter can select Active Shield Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. +TAG_SHIELD_WARD TAG_FEATS_DES Shield Ward You use your shield like a wall of steel and wood. When an opponent attempts to draw in close to you, your shield forces him away or ruins his attacks. Prerequisite: ~Proficiency with Shields~[TAG_SHIELD_PROF], ~Shield Specialization~[TAG_SHIELD_SPECIALIZATION]. Benefit: You apply your shield bonus to your ~touch~[TAG_TOUCH_ATTACK] AC, and on checks or rolls to resist ~bull rush~[TAG_BULLRUSH], ~disarm~[TAG_DISARM], ~grapple~[TAG_GRAPPLE], overrun, or ~trip~[TAG_TRIP] attempts against you. Special: A fighter can select Active Shield Defense as one of his ~fighter bonus feats~[TAG_CLASS_FEATURES_FIGHTER_BONUS_FEATS]. +TAG_ZEN_ARCHERY TAG_FEATS_DES Zen Archery Your intuition guides your hand when you use a ranged weapon. Prerequisite: ~Wisdom~[TAG_WISDOM] 13, base attack bonus +1 Benefit: You can use your Wisdom modifier instead of your Dexterity modifier when making a ~ranged attack~[TAG_RANGED_ATTACKS] roll. diff --git a/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab b/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab index 525e70aac..7dfe25802 100644 --- a/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab +++ b/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab @@ -67,6 +67,7 @@ TAG_SPELLS_HAUNTING_TUNE TAG_SPELLS TAG_BARD_3 Haunting Tune ~Enchantment~[TAG_ TAG_SPELLS_HEART_RIPPER TAG_SPELLS TAG_ASSASSIN_4 Heart Ripper ~Necromancy~[TAG_MAGIC_SCHOOLS_NECROMANCY][Death] Level: ~Assassin~[TAG_ASSASSINS] 4 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Close (25 ft. + 5 ft./ 2 levels) Target: One living creature Duration: Instantaneous; see text Saving Throw: ~Fortitude~[TAG_FORTITUDE] negates Spell Resistance: Yes With a sweep of your hand, invisible magic slams into your target. With a bloody pop and squelch, the heart of your target bursts out its back, dropping the creature like a discarded rag doll. Invisible bolts of force instantly slay the target you designate by driving its heart from its body unless it succeeds on a Fortitude save. If the target has HD higher than your caster level, it does not die on a failed saving throw, but instead is stunned for 1d4 rounds. Creatures that don't depend on their hearts for survival, creatures with no anatomy, and creatures immune to extra damage from critical hits are unaffected by the spell. TAG_SPELLS_HERALDS_CALL TAG_SPELLS TAG_BARD_1 Herald's Call ~Enchantment~[TAG_MAGIC_SCHOOLS_ENCHANTMENT](Compulsion)[Mind-Affecting][Sonic] ~Bard~[TAG_BARDS] 1 Components: V, S Casting Time: ~Free action~[TAG_FREE_ACTION] Range: 20 ft. Area: 20 ft. radius burst centered on you Duration: 1 round By placing your hand to your mouth and calling out, you gain the attention of creatures around you. For a moment, all eyes snap to you. Some creatures seem reluctant or unable to pull their eyes away. Any creature with 5 ~Hit Dice~[TAG_HIT_DICE] or less is ~slowed~[TAG_SPELLS_SLOW] for 1 round. Creatures beyond the radius of the burst might hear the shout, but they are not slowed. TAG_SPELLS_HOLY_STORM TAG_SPELLS TAG_CLERIC_3 TAG_PALADIN_3 Holy Storm ~Conjuration~[TAG_MAGIC_SCHOOLS_CONJURATION](Creation)[Good][Water] Level: ~Cleric~[TAG_CLERICS] 3, ~Paladin~[TAG_PALADINS] 3 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Personal Area: 20-ft. radius Duration: 1 round/level Saving Throw: None Spell Resistance: No You call upon the forces of good, and a heavy rain begins to fall around you, its raindrops soft and warm. A driving rain falls around you. It falls in a fixed area once created. The storm reduces hearing and visibility, resulting in a -4 penalty on ~Listen~[TAG_LISTEN], ~Spot~[TAG_SPOT], and ~Search~[TAG_SEARCH] checks. It also applies a -4 penalty on ~ranged~[TAG_RANGED_ATTACKS] attacks made into, out of, or through the storm. Finally, it automatically extinguishes any unprotected flames and has a 50% chance to extinguish protected flames (such as those of lanterns). The rain damages evil creatures, dealing 2d6 points of damage per round (evil outsiders take double damage) at the beginning of your turn. +TAG_SPELLS_HUNTER'S_EYE TAG_SPELLS TAG_RANGER_2 Hunter's Eye ~Divination~[TAG_MAGIC_SCHOOLS_DIVINATION] Level: ~Ranger~[TAG_RANGERS] 2 Components: V, S Casting Time: ~Swift Action~[TAG_SWIFT_ACTION] Range: Personal Target: You Duration: 1 round Your vision blurs for a moment. When it clears, you can see through your enemies' skin to spot their arteries, organs, and other vulnerable points. Your slice into a foe with uncanny precision, allowing you to strike a foe's vulnerable points and deal extra damage. This spell grants you the ~sneak attack ability~[TAG_CLASS_FEATURES_ROGUE_SNEAK_ATTACK]. You deal an extra 1d6 points of damage per three caster levels. If you already have the sneak attack ability, this damage stacks with it. TAG_SPELLS_IMPROVISATION TAG_SPELLS TAG_BARD_1 Improvisation ~Transmutation~[TAG_MAGIC_SCHOOLS_TRANSMUTATION] Level: ~Bard~[TAG_BARDS] 1 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Personal Target: You Duration: 1 round/level With an elaborate flourish and call for luck, you toss the dice in your hand into the air. Immediately you feel as though fate favors you, filling you with confidence. You gain access to a floating "pool" of luck, which manifests as bonus points you can use as desired to improve your odds of success at various tasks. This bonus pool consists of 2 points per caster level, which you can spend as you like to improve ~attack rolls~[TAG_ATTACK_ROLL], ~skill checks~[TAG_USING_SKILLS], and ~ability checks~[TAG_ABILITIES], although no single check can receive a bonus greater than one-half your caster level. You must declare any bonus point usage before the appropriate roll is made. Used points disappear from the pool, and any points remaining when the spell ends are wasted. These points count as ~luck~[TAG_MODIFIER_LUCK] bonuses for the purpose of stacking. TAG_SPELLS_INSIDIOUS_RHYTHM TAG_SPELLS TAG_BARD_1 Insidious Rhythm ~Enchantment~[TAG_MAGIC_SCHOOLS_ENCHANTMENT](Compulsion)[Mind-Affecting] Level: ~Bard~[TAG_BARDS] 1 Components: V, S Casting Time: ~Free action~[TAG_FREE_ACTION] Range: Medium (100 ft. + 10 ft./level) Target: One Creature Duration: 1 minute/level Saving Throw: ~Will~[TAG_WILL] negates Spell Resistance: Yes You recite a foolhardy ditty, tapping your foot in time. With a wink and a grin you mark your target, who shortly thereafter follows suit. The subject takes a -4 penalty on Intelligence-based skill checks and Concentration checks due to an endlessly recycling melody stuck in its mind. Whenever the subject attempts to cast, concentrate on, or direct a spell, it must succeed on a ~Concentration~[TAG_CONCENTRATION] check (DC equal to insidious rhythm's save DC + spell's level) or fail at the attempt. TAG_SPELLS_INSIGHTFUL_FEINT TAG_SPELLS TAG_ASSASSIN_1 TAG_SORCERER_1 TAG_WIZARD_1 Insightful Feint ~Divination~[TAG_MAGIC_SCHOOLS_DIVINATION] Level: ~Assassin~[TAG_ASSASSINS] 1, ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 1 Components: V, Casting Time: 1 ~Free action~[TAG_FREE_ACTION] Range: Personal Target: You Duration: 1 round With a chuckle you whisper the words that will make your ruse more effective. You gain a +10 ~insight~[TAG_MODIFIER_INSIGHT] bonus on the next single ~Bluff~[TAG_BLUFF] check that you make to ~feint~[TAG_FEINT] in combat (if it is made before the start of your next turn). You can make the feint as a ~move action~[TAG_MOVEMENT_ACTION], or once as a ~free action~[TAG_FREE_ACTION] if you have the ~Improved Feint~[TAG_IMPROVED_FEINT] feat. @@ -118,6 +119,8 @@ TAG_SPELLS_SUMMON_UNDEAD_II TAG_SPELLS TAG_BLACKGUARD_2 TAG_CLERIC_2 TAG_SORCER TAG_SPELLS_SUMMON_UNDEAD_III TAG_SPELLS TAG_BLACKGUARD_3 TAG_CLERIC_3 TAG_SORCERER_3 TAG_WIZARD_3 Summon Undead III ~Conjuration~[TAG_MAGIC_SCHOOLS_CONJURATION](Summoning)[Evil] Level: ~Blackguard~[TAG_BLACKGUARDS] 3, ~Cleric~[TAG_CLERICS] 3, ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 3 Components: V, S Casting Time: ~Full round action~[TAG_FULL_ROUND_ACTION] Range: Close (25 ft. + 5 ft./ 2 levels) Effect: One or more summoned creatures Duration: 1 round/level Saving Throw: None Spell Resistance: No The undead you summon appear in a burst of smoke and fog. The vapor swiftly dissipates, but you can't shake the impression of screaming faces in the cloud's tendrils. This spell functions like ~summon undead I~[TAG_SPELLS_SUMMON_UNDEAD_I], except that you can summon one undead from the 3rd-level list or two from 2nd-level list or four from the 1st-level list. Possible Summons: 1st Level: Skeleton 2nd Level: Gnoll Skeleton or Zombie 3rd Level: Ghoul or Skeleton Guard TAG_SPELLS_SUMMON_UNDEAD_IV TAG_SPELLS TAG_BLACKGUARD_4 TAG_CLERIC_4 TAG_SORCERER_4 TAG_WIZARD_4 Summon Undead IV ~Conjuration~[TAG_MAGIC_SCHOOLS_CONJURATION](Summoning)[Evil] Level: ~Blackguard~[TAG_BLACKGUARDS] 4, ~Cleric~[TAG_CLERICS] 4, ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 4 Components: V, S Casting Time: ~Full round action~[TAG_FULL_ROUND_ACTION] Range: Close (25 ft. + 5 ft./ 2 levels) Effect: One or more summoned creatures Duration: 1 round/level Saving Throw: None Spell Resistance: No The undead you summon appear in a burst of smoke and fog. The vapor swiftly dissipates, but you can't shake the impression of screaming faces in the cloud's tendrils. This spell functions like ~summon undead I~[TAG_SPELLS_SUMMON_UNDEAD_I], except that you can summon one undead from the 4th-level list or two from 3rd-level list or four of the same kind from a lower level list. Possible Summons: 1st Level: Skeleton 2nd Level: Gnoll Skeleton or Zombie 3rd Level: Ghoul or Skeleton Guard 4th Level: Ghast or Greater Temple Bugbear Zombie TAG_SPELLS_SUMMON_UNDEAD_V TAG_SPELLS TAG_CLERIC_5 TAG_SORCERER_5 TAG_WIZARD_5 Summon Undead V ~Conjuration~[TAG_MAGIC_SCHOOLS_CONJURATION](Summoning)[Evil] Level: ~Cleric~[TAG_CLERICS] 5, ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 5 Components: V, S Casting Time: ~Full round action~[TAG_FULL_ROUND_ACTION] Range: Close (25 ft. + 5 ft./ 2 levels) Effect: One or more summoned creatures Duration: 1 round/level Saving Throw: None Spell Resistance: No The undead you summon appear in a burst of smoke and fog. The vapor swiftly dissipates, but you can't shake the impression of screaming faces in the cloud's tendrils. This spell functions like ~summon undead I~[TAG_SPELLS_SUMMON_UNDEAD_I], except that you can summon one undead from the 5th-level list or two from 4th-level list or four of the same kind from a lower level list. Possible Summons: 1st Level: Skeleton 2nd Level: Gnoll Skeleton or Zombie 3rd Level: Ghoul or Skeleton Guard 4th Level: Ghast or Greater Temple Bugbear Zombie 5th level: Shadow or Wight +TAG_SPELLS_SURE_STRIKE TAG_SPELLS TAG_SORCERER_2 TAG_WIZARD_2 Sure Strike ~Divination~[TAG_MAGIC_SCHOOLS_DIVINATION] Level: ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 2 Components: V Casting Time: ~Swift Action~[TAG_SWIFT_ACTION] Range: Personal Target: You Duration: 1 round You gain a fleeting glimpse into the future, enough to guide your impending attack. You cast this spell immediately before you make an ~attack roll~[TAG_ATTACK_ROLL]. You can see into the future for that attack, granting you a +1 ~insight bonus~[TAG_MODIFIER_INSIGHT] per three caster levels on your next attack roll. +TAG_SPELLS_TACTICAL_PRECISION TAG_SPELLS TAG_BARD_2 Dirge of Discord ~Divination~[TAG_MAGIC_SCHOOLS_DIVINATION] Level: ~Bard~[TAG_BARDS] 2 Components: V, S, M Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Close (25 ft. + 5 ft./ 2 levels) Target: One creature/level, no two of which are more than 30 ft. apart Duration: 1 round/level Saving Throw: None Spell Resistance: No You hold aloft a toy soldier and shake it at your allies, calling them to arms as you do so. The toy soldier dissipates, but you feel a connection to your allies as though you could hear each one whispering her intended actions before she performed them. When you cast this spell, you grant your allies greater insight into one another's actions, allowing them to better coordinate their attacks. If two affected allies ~flank~[TAG_FLANKING] the same creature, each gains a +2 ~insight~[TAG_MODIFIER_INSIGHT] bonus on ~melee~[TAG_MELEE_ATTACKS] attack rolls and deals an extra 1d6 points of damage against the flanked creature. Creatures not subject to extra damage from ~sneak attacks~[TAG_CLASS_FEATURES_ROGUE_SNEAK_ATTACK] are immune to this extra damage. TAG_SPELLS_TOUCH_OF_MADNESS TAG_SPELLS TAG_MADNESS_D Touch of Madness ~Enchantment~[TAG_MAGIC_SCHOOLS_ENCHANTMENT][Mind-Affecting] Level: ~Domain: Madness~[TAG_MADNESS_D] 2 Components: V, S Casting Time: ~1 standard action~[TAG_STANDARD_ACTION] Range: Touch Target: Creature touched Duration: 1 round/level Saving Throw: ~Will~[TAG_WILL] negates Spell Resistance: Yes Your hand glows with roiling purple light as you reach out to deliver lunacy with your touch. You can cause one living creature to become ~dazed~[TAG_DAZED] by making a successful ~touch attack~[TAG_TOUCH_ATTACK]. If the target creature does not make a successful ~Will~[TAG_WILL] save, its mind is clouded and it takes no actions for 1 round per caster level. TAG_SPELLS_UNDEAD_BANE_WEAPON TAG_SPELLS TAG_CLERIC_4 TAG_PALADIN_3 Undead Bane Weapon ~Transmutation~[TAG_MAGIC_SCHOOLS_TRANSMUTATION] Level: ~Cleric~[TAG_CLERICS] 4, ~Paladin~[TAG_PALADINS] 3 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Touch Target: Weapon touched Duration: 1 hour/level Saving Throw: None Spell Resistance: No Your hand glows with a dull light, and when you touch the weapon, the light shifts to it, so that it sheds a serene gray radiance as bright as a candle. You give a weapon the undead bane special ability in addition to any other properties it has. Against ~undead~[TAG_MONSTERS], your weapon's ~enhancement~[TAG_ENHANCEMENT_BONUS] bonus is 2 higher than normal, and it deals an extra 2d6 points of damage against undead. The spell has no effect if cast upon a weapon that already has the undead bane special ability. The weapon is treated as ~good-aligned~[TAG_DESCRIPTORS] for the purpose of overcoming ~damage reduction~[TAG_SPECIAL_ABILITIES_DAMAGE_REDUCTION]. TAG_SPELLS_UNDERSONG TAG_SPELLS TAG_BARD_1 Undersong ~Transmutation~[TAG_MAGIC_SCHOOLS_TRANSMUTATION] Level: ~Bard~[TAG_BARDS] 1 Components: V Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Personal Target: You Duration: 10 minutes/level When you cast this spell, a familiar and soothing song wells up in your mind. This spell brings to your mind a song that helps you retain your concentration. The song does not distract you from any task at hand, on the contrary, by humming along to the tune, you can focus your mind with ease. As long as this spell is in effect, you can make a ~Perform~[TAG_PERFORM] check in place of a ~Concentration~[TAG_CONCENTRATION] check. Note: In Temple of Elemental Evil this spell gives you the difference between Perfom and Concentration as a bonus to concentration. diff --git a/tpdatasrc/tpgamefiles/mes/spell_ext/spell_compendium_spell.mes b/tpdatasrc/tpgamefiles/mes/spell_ext/spell_compendium_spell.mes index af7f67bad..2c375362f 100644 --- a/tpdatasrc/tpgamefiles/mes/spell_ext/spell_compendium_spell.mes +++ b/tpdatasrc/tpgamefiles/mes/spell_ext/spell_compendium_spell.mes @@ -135,6 +135,9 @@ {1176}{Draconic Might} {1177}{Vigor, Mass Lesser} {1178}{Vigor, Mass Improved} +{1179}{Sure Strike} +{1180}{Hunter's Eye} +{1181}{Tactical Precision} {1182}{Accuracy} {1183}{Hail of Stone} {1184}{Lesser Orb of Acid} @@ -284,8 +287,11 @@ {6176} Draconic Might {TBD} {6177} Vigor, Mass Lesser {Targets get Fast Healing 1 for 10 +1/level rounds, max 25.} {6178} Vigor, Mass Improved {Targets get Fast Healing 3 for 10 +1/level rounds, max 40.} +{6179} Sure Strike {You gain a +1 insight bonus per three caster levels on your next attack roll.} +{6180} Hunter's Eye {You deal an extra 1d6 points of sneak attack damage per three caster levels.} {6182} Accuracy {The range increment for the affected weapon or weapons is doubled.} {6183} Hail of Stone {You create a rain of stones that deals 1d4 points of damage per caster level (maximum 5d4) to creatures and objects within the area.} +{6181} Tactical Precision {If two affected allies flank the same creature, each gains a +2 insight bonus on melee attack rolls and deals an extra 1d6 points of damage against the flanked creature.} {6184} Lesser Orb of Acid {Ranged Touch Attack that deals 1d8/two levels (max 5d8) acid damage.} {6185} Lesser Orb of Cold {Ranged Touch Attack that deals 1d8/two levels (max 5d8) cold damage.} {6186} Lesser Orb of Electricity {Ranged Touch Attack that deals 1d8/two levels (max 5d8) electricity damage.} diff --git a/tpdatasrc/tpgamefiles/mes/spell_long_descriptions_ext/spell_compendium_spell_long_descriptions.mes b/tpdatasrc/tpgamefiles/mes/spell_long_descriptions_ext/spell_compendium_spell_long_descriptions.mes index fd6e770f7..cd9ee6a32 100644 --- a/tpdatasrc/tpgamefiles/mes/spell_long_descriptions_ext/spell_compendium_spell_long_descriptions.mes +++ b/tpdatasrc/tpgamefiles/mes/spell_long_descriptions_ext/spell_compendium_spell_long_descriptions.mes @@ -931,6 +931,27 @@ Range: Medium (100ft. +10 ft./level) Area: Ray Duration: Inst., Save: Yes, SR: Yes} +{6179} Sure Strike {You gain a +1 insight bonus per three caster levels on your next attack roll. +School: Divination +Casting: 1 swift action [V] +Range: Personal +Target: Self +Duration: 1 round} + +{6180} Hunter's Eye {You deal an extra 1d6 points of sneak attack damage per three caster levels. +School: Divination +Casting: 1 swift action [V] +Range: Personal +Target: Self +Duration: 1 round} + +{6181} Tactical Precision {If two affected allies flank the same creature, each gains a +2 insight bonus on melee attack rolls and deals an extra 1d6 points of damage against the flanked creature. +School: Divination[Mind-Affecting] +Casting: 1 action [V,S] +Range: Close (25ft. + 5ft./2 lvls) +Target: One creature/level +Duration: 1 round/level, Save: None, SR: No} + {6182} Accuracy {For the duration of the spell, the range increment for the affected weapon or weapons is doubled. School: Transmutation Casting: 1 action [V,S] diff --git a/tpdatasrc/tpgamefiles/rules/d20_combat/to_hit_processing.py b/tpdatasrc/tpgamefiles/rules/d20_combat/to_hit_processing.py index 69f9f0dfa..20bc80235 100644 --- a/tpdatasrc/tpgamefiles/rules/d20_combat/to_hit_processing.py +++ b/tpdatasrc/tpgamefiles/rules/d20_combat/to_hit_processing.py @@ -73,6 +73,7 @@ def mirror_image_attack_roll(d20a): #Performer to Hit Bonus to_hit = tpdp.EventObjAttack() + to_hit.attack_packet.attacker = performer to_hit.dispatch(performer, OBJ_HANDLE_NULL, ET_OnToHitBonus2, EK_NONE) to_hit_dice = dice_new("1d20") diff --git a/tpdatasrc/tpgamefiles/rules/feats/active shield defense.txt b/tpdatasrc/tpgamefiles/rules/feats/active shield defense.txt new file mode 100644 index 000000000..6346c4b74 --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/feats/active shield defense.txt @@ -0,0 +1,5 @@ +name: Active Shield Defense +flags: 12582928 +prereqs: +description: Your expert use of your shield allows you to strike at vulnerable foes even when you forgo your own attacks in favor of defense. +prereq descr: Proficiency with shields, Shield Specialization. \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/feats/agile shield fighter.txt b/tpdatasrc/tpgamefiles/rules/feats/agile shield fighter.txt new file mode 100644 index 000000000..1de7039ce --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/feats/agile shield fighter.txt @@ -0,0 +1,5 @@ +name: Agile Shield Fighter +flags: 12582928 +prereqs: +description: When making a shield bash and armed strike attack as part of a full attack action, you take a -2 penalty on each attack. These penalties replace the normal ones you incur for fighting with two weapons. +prereq descr: Improved Shield Bash, Shield Specialization and Shield Specialization. \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/feats/shield charge.txt b/tpdatasrc/tpgamefiles/rules/feats/shield charge.txt new file mode 100644 index 000000000..25a77e988 --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/feats/shield charge.txt @@ -0,0 +1,5 @@ +name: Shield Charge +flags: 12582928 +prereqs: 266 3 1216 1 +description: If you hit an opponent with your shield as part of a charge action, in addition to dealing damage normally, you may make a trip attack without provoking an attack of opportunity. If you lose, the defender does not get to try to trip you in return. +prereq descr: Proficiency with shields, Improved Shield Bash, Base Attack Bonus +3 \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/feats/shield specialization.txt b/tpdatasrc/tpgamefiles/rules/feats/shield specialization.txt new file mode 100644 index 000000000..cdcdf3442 --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/feats/shield specialization.txt @@ -0,0 +1,5 @@ +name: Shield Specialization +flags: 12582928 +prereqs: +description: You are skilled in using a shield, allowing you to gain greater defensive benefits from it. +prereq descr: Proficiency with shields. \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/feats/shield ward.txt b/tpdatasrc/tpgamefiles/rules/feats/shield ward.txt new file mode 100644 index 000000000..b57d8192a --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/feats/shield ward.txt @@ -0,0 +1,5 @@ +name: Shield Ward +flags: 12582928 +prereqs: +description: You use your shield like a wall of steel and wood. When an opponent attempts to draw in close to you, your shield forces him away or ruins his attacks. +prereq descr: Proficiency with shields, Shield Specialization. \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/indicators/hunters_eye.txt b/tpdatasrc/tpgamefiles/rules/indicators/hunters_eye.txt new file mode 100644 index 000000000..7944ba8cd --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/indicators/hunters_eye.txt @@ -0,0 +1,5 @@ +ID_string: HUNTER'S_EYE +effect_type: 0 +texture_file: art\interface\player_conditions\buffs\Hunter's Eye.tga +help_topic: TAG_SPELLS_HUNTER'S_EYE +tooltip_base_text: Hunter's Eye \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/indicators/sure_strike.txt b/tpdatasrc/tpgamefiles/rules/indicators/sure_strike.txt new file mode 100644 index 000000000..50be23f8b --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/indicators/sure_strike.txt @@ -0,0 +1,5 @@ +ID_string: SURE_STRIKE +effect_type: 0 +texture_file: art\interface\player_conditions\buffs\Sure Strike.tga +help_topic: TAG_SPELLS_SURE_STRIKE +tooltip_base_text: Sure Strike \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/indicators/tactical_precision.txt b/tpdatasrc/tpgamefiles/rules/indicators/tactical_precision.txt new file mode 100644 index 000000000..4144b237e --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/indicators/tactical_precision.txt @@ -0,0 +1,5 @@ +ID_string: TACTICAL_PRECISION +effect_type: 0 +texture_file: art\interface\player_conditions\buffs\Tactical Precision.tga +help_topic: TAG_SPELLS_TACTICAL_PRECISION +tooltip_base_text: Tactical Precision \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/partsys/spell_compendium_partsys.tab b/tpdatasrc/tpgamefiles/rules/partsys/spell_compendium_partsys.tab index 1189c09e4..2d3b8abf5 100644 --- a/tpdatasrc/tpgamefiles/rules/partsys/spell_compendium_partsys.tab +++ b/tpdatasrc/tpgamefiles/rules/partsys/spell_compendium_partsys.tab @@ -72,6 +72,9 @@ sp-Hail of Stone pebble 5 30 1 Sprite Polar bigcircle 30 Blend 0 0 0 sp-Holy Storm ice ice baby Model Verts perm 80 Sprite World Polar shard 10 Blend 0 shard 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1600 0 -240?240 0 -240?240 5?25 0 0 0 0,255,255,255,255,255,255,255,0 128 128 128 20 sp-Holy Storm ground busts discs 20 Point perm 120 World Polar Disc World Polar Cartesian fire-sprite 10 Blend 0 Shard-Breaking 0 0 0 0 0 2 0 0 0 0 0 20?-20 0 0 -250?250 0 -250?250 0,40 0 0 0?360 0 0 64,0 255 255 255 20 sp-Holy Storm circle bounding 20 Point perm 120 World Polar Disc World Polar Cartesian fire-sprite 10 Blend 0 Shard-Breaking 0 0 0 0 0 2 0 0 0 0 0 20?-20 0 0 0?360 0 250 0,40 0 0 0?360 0 0 64,0 255 255 255 20 +sp-Hunter's Eye eyes perm 1 Node Pos bip01 head Sprite Same as Emitter hunters_eye 60 Blend 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 0 0 180 0 0 0,192,0 138,0 3,0 3,0 1 +sp-Hunter's Eye left eye sparkle 25 15 10 Node Pos bip01 head Sprite Same as Emitter flare-1 15 Add 0 0 0 0 0 0 0 18 -18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0?360 0 0 0,255,192,128,96,64,32,0 253,0 138,0 140,140 5 +sp-Hunter's Eye right eye sparkle 25 15 10 Node Pos bip01 head Sprite Same as Emitter flare-1 15 Add 0 0 0 0 0 0 0 -18 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0?360 0 0 0,255,192,128,96,64,32,0 253,0 138,0 140,140 5 sp-Ironthunder Horn shockwave 40 150 Object YPR Sprite Same as Emitter Polar ring 20 Add 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 64,0 0 0 0 0 0 0 -15?15 -15?15 -15?15 -30?30 0 0,360 0?75 0 0 0?360 0 0 128,0 15 15 15 1 sp-Ironthunder Horn wave1 10 30 150 Object YPR Sprite Same as Emitter Polar ring 20 Add 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 64,0 0 0 0 0 0 0 -15?15 -15?15 -15?15 -30?30 0 0,360 0?75 0 0 0?360 0 0 128,0 15 15 15 1 sp-Ironthunder Horn wave2 20 20 150 Object YPR Sprite Same as Emitter Polar ring 20 Add 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 64,0 0 0 0 0 0 0 -15?15 -15?15 -15?15 -30?30 0 0,360 0?75 0 0 0?360 0 0 128,0 15 15 15 1 @@ -121,6 +124,8 @@ sp-Storm of Elemental Fury Wind wind 2 10 perm 60 Object Pos bip01 head Spri sp-Storm of Elemental Fury Wind winaround 0 perm 120 Object Pos bip01 head Sprite Polar fire-sprite 15 Add 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,100 0 360?0 0 80,0 0,40 0 0 0,360 0 0 0,64,0 64 64 64 15 sp-Storm of Elemental Fury Earth ice ice baby Model Verts perm 80 Sprite World Polar flare-small 10 Add 0 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1600 0 -240?240 0 -240?240 15?65 0 0 0 0,255,255,255,255,255,255,255,0 139 117 0 50 sp-Storm of Elemental Fury Earth ground busts discs 5 Point perm 120 World Polar Disc World Polar Cartesian Ring-3 10 Add 0 Shard-Breaking 0 0 0 0 0 2 0 0 0 0 0 20?-20 0 0 -250?250 0 -250?250 0,40 0 0 0?360 0 0 64,0 139 117 0 20 +sp-Sure Strike bullseye 60 1 Node Pos bip01 head Sprite Same as Emitter bullseye 60 Blend 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 0 0 180 0 0 0,192,0 138,0 3,0 3,0 1 +sp-Tactical Precision Crossed Swords 60 1 Node Pos bip01 head Sprite Same as Emitter crossed_swords 60 Blend 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 0 0 180 0 0 192?0 0 0 0 1 sp-Touch Of Madness vampire dribblins 90 10 Node Pos bip01 r hand Sprite Polar Polar flare-big 60 Blend 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0?360 0 30,0 0,20 0 0 0 0 0 0,255,192,128,96,64,0 32 0 64 5 sp-Touch Of Madness dribbles bright 90 20 Node Pos bip01 r hand Sprite Polar Polar flare-1 60 Add 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0?360 0 30,0 0,5 0 0 0 0 0 0,255,192,128,96,64,0 128 0 255 5 sp-Undersong note2 0 perm 7 Node Pos bip01 head Sprite World Polar Polar note2 130 Add 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2?-2 2?-2 2?-2 0,720 0 10?30 2?7 0 0 160?200 0 0 0,255,200,150,100,50,0 255 155 112 2 diff --git a/tpdatasrc/tpgamefiles/rules/spell_enums/spell_compendium_spell_enum.mes b/tpdatasrc/tpgamefiles/rules/spell_enums/spell_compendium_spell_enum.mes index 4d2894162..8ecd18494 100644 --- a/tpdatasrc/tpgamefiles/rules/spell_enums/spell_compendium_spell_enum.mes +++ b/tpdatasrc/tpgamefiles/rules/spell_enums/spell_compendium_spell_enum.mes @@ -134,6 +134,9 @@ {1176}{Draconic Might} {1177}{Vigor, Mass Lesser} {1178}{Vigor, Mass Improved} +{1179}{Sure Strike} +{1180}{Hunter's Eye} +{1181}{Tactical Precision} {1182}{Accuracy} {1183}{Hail of Stone} {1184}{Lesser Orb of Acid} @@ -284,6 +287,9 @@ {6176}{Draconic Might} {6177}{Vigor, Mass Lesser} {6178}{Vigor, Mass Improved} +{6179}{Sure Strike} +{6180}{Hunter's Eye} +{6181}{Tactical Precision} {6182}{Accuracy} {6183}{Hail of Stone} {6184}{Lesser Orb of Acid} @@ -436,6 +442,9 @@ {21176}{TAG_SPELLS_DRACONIC_MIGHT} {21177}{TAG_SPELLS_VIGOR,_MASS_LESSER} {21178}{TAG_SPELLS_VIGOR,_MASS_IMPROVED} +{21179}{TAG_SPELLS_SURE_STRIKE} +{21180}{TAG_SPELLS_HUNTER'S_EYE} +{21181}{TAG_SPELLS_TACTICAL_PRECISION} {21182}{TAG_SPELLS_ACCURACY} {21183}{TAG_SPELLS_HAIL_OF_STONE} {21184}{TAG_SPELLS_LESSER_ORB_OF_ACID} diff --git a/tpdatasrc/tpgamefiles/rules/spells/1179 - Sure Strike.txt b/tpdatasrc/tpgamefiles/rules/spells/1179 - Sure Strike.txt new file mode 100644 index 000000000..64637d521 --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/spells/1179 - Sure Strike.txt @@ -0,0 +1,19 @@ +School: Divination +Level: Sor 2 +Level: Wiz 2 +Component: V +Casting Time: Swift Action +Range: Personal +Saving Throw: None +Spell Resistance: No +Projectile: No +flags_Target: Range +inc_flags_Target: Self +exc_flags_Target: Other +exc_flags_Target: Dead +exc_flags_Target: Non-critter +mode_Target: Personal +min_Target: 1 +max_Target: 1 +radius_Target: 0 +ai_type: ai_action_defensive \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/spells/1180 - Hunters Eye.txt b/tpdatasrc/tpgamefiles/rules/spells/1180 - Hunters Eye.txt new file mode 100644 index 000000000..9623cf565 --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/spells/1180 - Hunters Eye.txt @@ -0,0 +1,19 @@ +School: Divination +Level: Rgr 2 +Component: V +Component: S +Casting Time: Swift Action +Range: Personal +Saving Throw: None +Spell Resistance: No +Projectile: No +flags_Target: Range +inc_flags_Target: Self +exc_flags_Target: Other +exc_flags_Target: Dead +exc_flags_Target: Non-critter +mode_Target: Personal +min_Target: 1 +max_Target: 1 +radius_Target: 0 +ai_type: ai_action_defensive \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/rules/spells/1181 - Tactical Precision.txt b/tpdatasrc/tpgamefiles/rules/spells/1181 - Tactical Precision.txt new file mode 100644 index 000000000..d5159da10 --- /dev/null +++ b/tpdatasrc/tpgamefiles/rules/spells/1181 - Tactical Precision.txt @@ -0,0 +1,23 @@ +School: Divination +Subschool: Mind-Affecting +Level: Brd 2 +Component: V +Component: S +Casting Time: 1 action +Range: Close +Saving Throw: None +Spell Resistance: No +Projectile: No +flags_Target: Range +inc_flags_Target: Other +inc_flags_Target: Self +exc_flags_Target: Dead +exc_flags_Target: Non-critter +mode_Target: Multi +mode_Target: Once-Multi +mode_Target: Any 30 Feet +mode_Target: End Early Multi +min_Target: 1 +max_Target: 0 +radius_Target: 0 +ai_type: ai_action_defensive \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/scr/Spell1179 - Sure Strike.py b/tpdatasrc/tpgamefiles/scr/Spell1179 - Sure Strike.py new file mode 100644 index 000000000..9011a7079 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/Spell1179 - Sure Strike.py @@ -0,0 +1,27 @@ +from toee import * + +def OnBeginSpellCast(spell): + print "Sure Strike OnBeginSpellCast" + print "spell.target_list=", spell.target_list + print "spell.caster=", spell.caster, " caster.level= ", spell.caster_level + +def OnSpellEffect(spell): + print "Sure Strike OnSpellEffect" + + spell.duration = 0 #Sure Strike is a Swift Action, works in the current round + spellTarget = spell.target_list[0] + bonusValue = spell.caster_level/3 + + if spellTarget.obj.condition_add_with_args('sp-Sure Strike', spell.id, spell.duration, bonusValue, 0): + spellTarget.partsys_id = game.particles('sp-Sure Strike', spellTarget.obj) + else: + spellTarget.obj.float_mesfile_line('mes\\spell.mes', 30000) + game.particles('Fizzle', spellTarget.obj) + spell.target_list.remove_target(spellTarget.obj) + spell.spell_end(spell.id) + +def OnBeginRound(spell): + print "Sure Strike OnBeginRound" + +def OnEndSpellCast(spell): + print "Sure Strike OnEndSpellCast" \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/scr/Spell1180 - Hunters Eye.py b/tpdatasrc/tpgamefiles/scr/Spell1180 - Hunters Eye.py new file mode 100644 index 000000000..c948e89f7 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/Spell1180 - Hunters Eye.py @@ -0,0 +1,27 @@ +from toee import * + +def OnBeginSpellCast(spell): + print "Hunter's Eye OnBeginSpellCast" + print "spell.target_list=", spell.target_list + print "spell.caster=", spell.caster, " caster.level= ", spell.caster_level + +def OnSpellEffect(spell): + print "Hunter's Eye OnSpellEffect" + + spell.duration = 0 #Hunter's Eye is a Swift Action, works in the current round + spellTarget = spell.target_list[0] + bonusDice = spell.caster_level/3 + + if spellTarget.obj.condition_add_with_args("sp-Hunter's Eye", spell.id, spell.duration, bonusDice, 0): + spellTarget.partsys_id = game.particles("sp-Hunter's Eye", spellTarget.obj) + else: + spellTarget.obj.float_mesfile_line('mes\\spell.mes', 30000) + game.particles('Fizzle', spellTarget.obj) + spell.target_list.remove_target(spellTarget.obj) + spell.spell_end(spell.id) + +def OnBeginRound(spell): + print "Hunter's Eye OnBeginRound" + +def OnEndSpellCast(spell): + print "Hunter's Eye OnEndSpellCast" \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/scr/Spell1181 - Tactical Precicion.py b/tpdatasrc/tpgamefiles/scr/Spell1181 - Tactical Precicion.py new file mode 100644 index 000000000..6b530f81d --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/Spell1181 - Tactical Precicion.py @@ -0,0 +1,29 @@ +from toee import * + +def OnBeginSpellCast(spell): + print "Tactical Precision OnBeginSpellCast" + print "spell.target_list=", spell.target_list + print "spell.caster=", spell.caster, " caster.level= ", spell.caster_level + +def OnSpellEffect(spell): + print "Tactical Precision OnSpellEffect" + + targetsToRemove = [] + spell.duration = 1 * spell.caster_level # 1 round/cl + + for spellTarget in spell.target_list: + if spellTarget.obj.condition_add_with_args('sp-Tactical Precision', spell.id, spell.duration): + spellTarget.partsys_id = game.particles('sp-Tactical Precision', spellTarget.obj) + else: + spellTarget.obj.float_mesfile_line('mes\\spell.mes', 30000) + game.particles('Fizzle', spellTarget.obj) + targetsToRemove.append(spellTarget.obj) + if targetsToRemove: + spell.target_list.remove_list(targetsToRemove) + spell.spell_end(spell.id) + +def OnBeginRound(spell): + print "Tactical Precision OnBeginRound" + +def OnEndSpellCast(spell): + print "Tactical Precision OnEndSpellCast" \ No newline at end of file diff --git a/tpdatasrc/tpgamefiles/scr/feats/feat - Active Shield Defense.py b/tpdatasrc/tpgamefiles/scr/feats/feat - Active Shield Defense.py new file mode 100644 index 000000000..7b3a16fdd --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/feats/feat - Active Shield Defense.py @@ -0,0 +1,9 @@ +from toee import * +import char_editor + +def CheckPrereq(attachee, classLevelled, abilityScoreRaised): + if not char_editor.has_feat(feat_shield_proficiency): + return 0 + elif not char_editor.has_feat("Shield Specialization"): + return 0 + return 1 diff --git a/tpdatasrc/tpgamefiles/scr/feats/feat - Agile Shield Fighter.py b/tpdatasrc/tpgamefiles/scr/feats/feat - Agile Shield Fighter.py new file mode 100644 index 000000000..a558802af --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/feats/feat - Agile Shield Fighter.py @@ -0,0 +1,11 @@ +from toee import * +import char_editor + +def CheckPrereq(attachee, classLevelled, abilityScoreRaised): + if not char_editor.has_feat(feat_shield_proficiency): + return 0 + if not char_editor.has_feat(feat_improved_shield_bash): + return 0 + if not char_editor.has_feat("Shield Specialization"): + return 0 + return 1 diff --git a/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Charge.py b/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Charge.py new file mode 100644 index 000000000..808cf675a --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Charge.py @@ -0,0 +1,8 @@ +from toee import * +import char_editor + +def CheckPrereq(attachee, classLevelled, abilityScoreRaised): + #Prerequisite of Base Attack Bonus of 3 is handled by Engine + if not char_editor.has_feat(feat_shield_proficiency): + return 0 + return 1 diff --git a/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Specialization.py b/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Specialization.py new file mode 100644 index 000000000..d230c76e1 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Specialization.py @@ -0,0 +1,7 @@ +from toee import * +import char_editor + +def CheckPrereq(attachee, classLevelled, abilityScoreRaised): + if char_editor.has_feat(feat_shield_proficiency): + return 1 + return 0 diff --git a/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Ward.py b/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Ward.py new file mode 100644 index 000000000..7b3a16fdd --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/feats/feat - Shield Ward.py @@ -0,0 +1,9 @@ +from toee import * +import char_editor + +def CheckPrereq(attachee, classLevelled, abilityScoreRaised): + if not char_editor.has_feat(feat_shield_proficiency): + return 0 + elif not char_editor.has_feat("Shield Specialization"): + return 0 + return 1 diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/active_shield_defense.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/active_shield_defense.py new file mode 100644 index 000000000..33f154e73 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/active_shield_defense.py @@ -0,0 +1,40 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpdp + +# Active Shield Defense: PHB II, p. 71 + +def getFeatName(): + return "Active Shield Defense" + +print "Registering {}".format(getFeatName) + +def getFeatTag(featName): + return "TAG_{}".format(featName.upper().replace(" ", "_")) + +def nullifyToHitPenalty(attachee, args, evt_obj): + flags = evt_obj.attack_packet.get_flags() + if not flags & D20CAF_ATTACK_OF_OPPORTUNITY: + return 0 + elif not attachee.d20_query("Fighting Defensively Checked"): + return 0 + elif attachee.item_worn_at(item_wear_shield) == OBJ_HANDLE_NULL: + return 0 + elif attachee.d20_query("PQ_Total_Defense_Activated"): + return 0 + else: + featName = getFeatName() + featTag = getFeatTag(featName) + #Fighting Defensively has a vanilla bonus type of 0, which means it can't be negated + #So I add the penalty as a bonus again + bonusValue = 4 + bonusType = 0 #ID 0 = Untyped (stacking!) + evt_obj.bonus_list.add(bonusValue, bonusType, "Feat: ~{}~[{}]".format(featName, featTag)) + return 0 + +# Allowing AoO's while in Total Defense is directly handled in +# total_defense.py (an already existing PythonModifier Extender) + +activeShieldDefenseFeat = PythonModifier(getFeatName(), 2) #featEnum, empty +activeShieldDefenseFeat.MapToFeat(getFeatName(), feat_cond_arg2 = 0) +activeShieldDefenseFeat.AddHook(ET_OnToHitBonus2, EK_NONE, nullifyToHitPenalty, ()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/agile_shield_fighter.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/agile_shield_fighter.py new file mode 100644 index 000000000..bd0e84950 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/agile_shield_fighter.py @@ -0,0 +1,42 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpdp + +# Agile Shield Fighter: PHB II, p. 74 + +def getFeatName(): + return "Agile Shield Fighter" + +print "Registering {}".format(getFeatName) + +def AgileShieldFighterDisableTwoWeaponFightingBonus(attachee, args, evt_obj): + shield = attachee.item_worn_at(item_wear_shield) + if shield == OBJ_HANDLE_NULL: + return 0 + + evt_obj.return_val = 1 #This feat takes over two weapon fighting + return 0 + +def AgileShieldFighterOverrideTwoWeaponPenalty(attachee, args, evt_obj): + shield = attachee.item_worn_at(item_wear_shield) + if shield == OBJ_HANDLE_NULL: + return 0 + + evt_obj.return_val = 1 + return 0 + +def AgileShieldFighterGetTwoWeaponPenalty(attachee, args, evt_obj): + shield = attachee.item_worn_at(item_wear_shield) + if shield == OBJ_HANDLE_NULL: + return 0 + + evt_obj.return_val = -2 + return 0 + +#Need to block two weapon fighting + +agileShieldFighter = PythonModifier(getFeatName(), 2) #Spare, Spare +agileShieldFighter.MapToFeat(getFeatName()) +agileShieldFighter.AddHook(ET_OnD20PythonQuery, "Disable Two Weapon Fighting Bonus", AgileShieldFighterDisableTwoWeaponFightingBonus, ()) +agileShieldFighter.AddHook(ET_OnD20PythonQuery, "Get Two Weapon Penalty", AgileShieldFighterGetTwoWeaponPenalty, ()) +agileShieldFighter.AddHook(ET_OnD20PythonQuery, "Override Two Weapon Penalty", AgileShieldFighterOverrideTwoWeaponPenalty, ()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_charge.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_charge.py new file mode 100644 index 000000000..9d5f63d6c --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_charge.py @@ -0,0 +1,41 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpdp + +# Shield Charge: Complete Warrior, p. 105 + +def getFeatName(): + return "Shield Charge" + +print "Registering {}".format(getFeatName) + +def getFeatTag(featName): + return "TAG_{}".format(featName.upper().replace(" ", "_")) + +def radialEntry(attachee, args, evt_obj): + featName = getFeatName() + featTag = getFeatTag(featName) + toggleRadialId = tpdp.RadialMenuEntryToggle(featName, featTag) + toggleRadialId.link_to_args(args, 1) + toggleRadialId.add_child_to_standard(attachee, tpdp.RadialMenuStandardNode.Options) + return 0 + +def tripAttempt(attachee, args, evt_obj): + enabledFlag = args.get_arg(1) + if enabledFlag and attachee.d20_query("Charging"): + if attachee.item_worn_at(item_wear_shield) != OBJ_HANDLE_NULL: + target = evt_obj.attack_packet.target + if attachee.trip_check(target): + combatMesId = 104 # ID 104 = Tripped! + target.float_mesfile_line('mes\\combat.mes', combatMesId) + target.condition_add_with_args("Prone") + target.fall_down() + else: + combatMesId = 144 #ID 144 = Attempt Fails + attachee.float_mesfile_line('mes\\combat.mes', combatMesId) + return 0 + +shieldChargeFeat = PythonModifier(getFeatName(), 3) #featEnum, enabledFlag, empty +shieldChargeFeat.MapToFeat(getFeatName(), feat_cond_arg2 = 1) +shieldChargeFeat.AddHook(ET_OnBuildRadialMenuEntry, EK_NONE, radialEntry, ()) +shieldChargeFeat.AddHook(ET_OnDealingDamage2, EK_NONE, tripAttempt, ()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_specialization.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_specialization.py new file mode 100644 index 000000000..2a663169d --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_specialization.py @@ -0,0 +1,34 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpdp + +# Shield Specialization: PHB II, p. 82 + +def getFeatName(): + return "Shield Specialization" + +print "Registering {}".format(getFeatName) + +def getFeatTag(featName): + return "TAG_{}".format(featName.upper().replace(" ", "_")) + +#def getSubFeatList(): +# return {0:"Buckler", 1:"Heavy", 2:"Light"} + +def applyBonus(attachee, args, evt_obj): + flags = evt_obj.attack_packet.get_flags() + if attachee.item_worn_at(item_wear_shield) == OBJ_HANDLE_NULL: + return 0 + elif flags & D20CAF_TOUCH_ATTACK: + return 0 + else: + featName = getFeatName() + featTag = getFeatTag(featName) + bonusValue = 1 + bonusType = 0 #ID 0 = Untyped (stacking!) + evt_obj.bonus_list.add(bonusValue, bonusType, "Feat: ~{}~[{}]".format(featName, featTag)) + return 0 + +shieldSpecializationFeat = PythonModifier(getFeatName(), 2) #featEnum, empty +shieldSpecializationFeat.MapToFeat(getFeatName(), feat_cond_arg2 = 0) +shieldSpecializationFeat.AddHook(ET_OnGetAC, EK_NONE, applyBonus, ()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_ward.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_ward.py new file mode 100644 index 000000000..c56232435 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/shield_ward.py @@ -0,0 +1,52 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpactions + +# Shield Ward: PHB II, p. 82 + +def getFeatName(): + return "Shield Ward" + +print "Registering {}".format(getFeatName) + +def getFeatTag(featName): + return "TAG_{}".format(featName.upper().replace(" ", "_")) + +def getShieldBonus(wornShield): + bonusValue = wornShield.item_d20_query(Q_Armor_Get_AC_Bonus) + bonusValue += 1 #Add Shield Specialization Bonus as well; SP is a prereq for this feat + return bonusValue + +def applyBonus(attachee, args, evt_obj): + flags = evt_obj.attack_packet.get_flags() + wornShield = attachee.item_worn_at(item_wear_shield) + if wornShield == OBJ_HANDLE_NULL: + return 0 + elif not flags & D20CAF_TOUCH_ATTACK: + return 0 + else: + featName = getFeatName() + featTag = getFeatTag(featName) + bonusValue = getShieldBonus(wornShield) + bonusType = 0 #ID 0 = Untyped (stacking!) + evt_obj.bonus_list.add(bonusValue, bonusType, "Feat: ~{}~[{}]".format(featName, featTag)) + return 0 + +def addBonusAgainstSpecialAttacks(attachee, args, evt_obj): + currentSequence = tpactions.get_cur_seq() + performer = currentSequence.performer + if performer != attachee: + wornShield = attachee.item_worn_at(item_wear_shield) + flags = evt_obj.flags + if wornShield != OBJ_HANDLE_NULL and flags == 3: + featName = getFeatName() + featTag = getFeatTag(featName) + bonusValue = getShieldBonus(wornShield) + bonusType = 0 #ID 0 = Untyped (stacking!) + evt_obj.bonus_list.add(bonusValue, bonusType, "Feat: ~{}~[{}]".format(featName, featTag)) + return 0 + +shieldWardFeat = PythonModifier(getFeatName(), 2) #featEnum, empty +shieldWardFeat.MapToFeat(getFeatName(), feat_cond_arg2 = 0) +shieldWardFeat.AddHook(ET_OnGetAC, EK_NONE, applyBonus, ()) +shieldWardFeat.AddHook(ET_OnGetAbilityCheckModifier, EK_NONE, addBonusAgainstSpecialAttacks, ()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_hunters_eye.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_hunters_eye.py new file mode 100644 index 000000000..cfa2d3c71 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_hunters_eye.py @@ -0,0 +1,32 @@ +from templeplus.pymod import PythonModifier +from toee import * +from spell_utils import SpellPythonModifier + +print "Registering sp-Hunter's Eye" + +def querySneakDice(attachee, args, evt_obj): + bonusDice = args.get_arg(2) + evt_obj.return_val += bonusDice + return 0 + +def sneakDamage(attachee, args, evt_obj): + #Currently I don't think there is a method + #to add temporaly a feat (in this case feat_sneak_attack) + #So I add sneak damage manually if the character has no + #sneak attack feat. + if not attachee.has_feat(feat_sneak_attack): + target = evt_obj.attack_packet.target + if attachee.can_sneak_attack(target): + bonusDice = dice_new('1d6') + bonusDice.number = args.get_arg(2) + damageType = D20DT_UNSPECIFIED + damageMesId = 106 #ID 106 = ~Sneak Attack~[TAG_CLASS_FEATURES_ROGUE_SNEAK_ATTACK] + evt_obj.damage_packet.add_dice(bonusDice, damageType, damageMesId) + #Add Sneak float and history + attachee.float_mesfile_line('mes\\combat.mes', 90) #ID 90 = Sneak Attack! + game.create_history_from_pattern(26, attachee, target) #ID 26 = [ACTOR] ~sneak attacks~[TAG_CLASS_FEATURES_ROGUE_SNEAK_ATTACK] [TARGET]! + return 0 + +huntersEyeSpell = SpellPythonModifier("sp-Hunter's Eye", 4) # spellId, duration, bonusDice, empty +huntersEyeSpell.AddHook(ET_OnD20PythonQuery, "Sneak Attack Dice", querySneakDice, ()) +huntersEyeSpell.AddHook(ET_OnDealingDamage, EK_NONE, sneakDamage, ()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_sure_strike.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_sure_strike.py new file mode 100644 index 000000000..95aa0c4ae --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_sure_strike.py @@ -0,0 +1,19 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpdp +from utilities import * +from spell_utils import SpellPythonModifier + +print "Registering sp-Sure Strike" + +def applyToHitBonus(attachee, args, evt_obj): + #Sure Strike adds an Insight Bonus of +1/three casterlevels to the next Attack Roll + bonusValue = args.get_arg(2) + bonusType = 18 #ID 18 = Insight + evt_obj.bonus_list.add(bonusValue ,bonusType ,"~Insight~[TAG_MODIFIER_INSIGHT] : ~Sure Strike~[TAG_SPELLS_SURE_STRIKE]") + args.remove_spell() + args.remove_spell_mod() + return 0 + +sureStrikeSpell = SpellPythonModifier("sp-Sure Strike", 4) # spellId, duration, bonusValue, empty +sureStrikeSpell.AddHook(ET_OnToHitBonus2, EK_NONE, applyToHitBonus,()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_tactical_precision.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_tactical_precision.py new file mode 100644 index 000000000..495678112 --- /dev/null +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/sp_tactical_precision.py @@ -0,0 +1,58 @@ +from templeplus.pymod import PythonModifier +from toee import * +import tpdp +from utilities import * +from spell_utils import SpellPythonModifier, getSpellHelpTag + +print "Registering sp-Tactical Precision" + +def verifyTarget(attackPacket, spellId): + flags = attackPacket.get_flags() + + #Only melee attacks qualify + if flags & D20CAF_RANGED: + return False + elif flags & D20CAF_THROWN: + return False + elif flags & D20CAF_THROWN_GRENADE: + return False + + target = attackPacket.target + flankCount = 0 + spellPacket = tpdp.SpellPacket(spellId) + targetCount = spellPacket.target_count + for indexNumber in range(0, targetCount): + if target.is_flanked_by(spellPacket.get_target(indexNumber)): + flankCount += 1 + if flankCount == 2: + break + if flankCount < 2: + return False + return True + +def tacticalPrecisionSpellBonusToHit(attachee, args, evt_obj): + spellId = args.get_arg(0) + if verifyTarget(evt_obj.attack_packet, spellId): + bonusValue = 2 #Tactical Precision grants +2 to hit + bonusType = bonus_type_insight + bonusHelpTag = game.get_mesline("mes\\bonus_description.mes", bonusType) + spellHelpTag = getSpellHelpTag(spellId) + evt_obj.bonus_list.add(bonusValue, bonusType, "{} : {}".format(bonusHelpTag, spellHelpTag)) + game.particles('sp-Tactical Precision', attachee) + return 0 + +def tacticalPrecisionSpellBonusDamage(attachee, args, evt_obj): + spellId = args.get_arg(0) + if verifyTarget(evt_obj.attack_packet, spellId): + if evt_obj.attack_packet.target.d20_query(Q_SneakAttack): + bonusDice = dice_new('1d6') #Tactical Precision Bonus Damage + damageType = D20DT_UNSPECIFIED + damageMesId = 3009 #ID3009 NEW! added in damage.mes + evt_obj.damage_packet.add_dice(bonusDice, damageType, damageMesId) + else: + evt_obj.damage_packet.bonus_list.add_zeroed(377) #ID 377 = NEW! + return 0 + +tacticalPrecisionSpell = SpellPythonModifier("sp-Tactical Precision") # spellId, duration, empty +tacticalPrecisionSpell.AddHook(ET_OnToHitBonus2, EK_NONE, tacticalPrecisionSpellBonusToHit,()) +tacticalPrecisionSpell.AddHook(ET_OnDealingDamage, EK_NONE, tacticalPrecisionSpellBonusDamage,()) diff --git a/tpdatasrc/tpgamefiles/scr/tpModifiers/total_defense.py b/tpdatasrc/tpgamefiles/scr/tpModifiers/total_defense.py index 4886877ce..2b780610e 100644 --- a/tpdatasrc/tpgamefiles/scr/tpModifiers/total_defense.py +++ b/tpdatasrc/tpgamefiles/scr/tpModifiers/total_defense.py @@ -5,18 +5,26 @@ print "Registering Total Defense extender" #The fighting defensively query needs to cover both fighting defensively and total defense -def FightingDefensivelyQuery(attachee, args, evt_obj): - if (args.get_arg(0) != 0): - evt_obj.return_val = 1 - return 0 +def FightingDefensivelyQuery(attachee, args, evt_obj): + #If args.get_arg(0)clause removed by Sagenlicht + #as Total Defense has no args and resulted in an error with the newly added Python Query + #Total Defense is a condition, that will be only added, if the action is actually triggered + #So the query only triggers if the action is activated anyways. + evt_obj.return_val = 1 return 0 # No making AOOs when using total defense def TotalDefenseAOOPossible(attachee, args, evt_obj): - evt_obj.return_val = 0 + #If clause added by Sagenlicht to allow AoO's if character has + #the Active Shield Defense Feat + if not attachee.has_feat("Active Shield Defense"): + evt_obj.return_val = 0 return 0 modExtender = PythonModifier() modExtender.ExtendExisting("Total Defense") modExtender.AddHook(ET_OnD20Query, EK_Q_FightingDefensively, FightingDefensivelyQuery, ()) modExtender.AddHook(ET_OnD20Query, EK_Q_AOOPossible, TotalDefenseAOOPossible, ()) +#PythonQuery added by Sagenlicht, as Active Shield Defense needs +#A query that is only responding to Total Defense +modExtender.AddHook(ET_OnD20PythonQuery, "PQ_Total_Defense_Activated", FightingDefensivelyQuery, ()) diff --git a/tpdatasrc/tpgamefiles/sound/user_sounds/spell_compendium_sounds.mes b/tpdatasrc/tpgamefiles/sound/user_sounds/spell_compendium_sounds.mes index c5796d53b..80cb2c859 100644 --- a/tpdatasrc/tpgamefiles/sound/user_sounds/spell_compendium_sounds.mes +++ b/tpdatasrc/tpgamefiles/sound/user_sounds/spell_compendium_sounds.mes @@ -1440,6 +1440,39 @@ {29567}{} // spell hit {29568}{} // spell struck +// [1179] *[Sure Strike] +{29580}{} // spell begin +{29581}{} // spell end +{29582}{spells\sp_shillelagh.WAV} // spell effect +{29583}{} // spell new round +{29584}{} // spell projectile begin +{29585}{} // spell projectile end +{29586}{} // spell projectile in-flight +{29587}{} // spell hit +{29588}{} // spell struck + +// [1180] *[Hunter's Eye] +{29600}{} // spell begin +{29601}{} // spell end +{29602}{spells\sp_see_invisibilty_end.WAV} // spell effect +{29603}{} // spell new round +{29604}{} // spell projectile begin +{29605}{} // spell projectile end +{29606}{} // spell projectile in-flight +{29607}{} // spell hit +{29608}{} // spell struck + +// [1181] *[Tactical Precision] +{29620}{} // spell begin +{29621}{spells\sp_guidance_end.WAV} // spell end +{29622}{spells\sp_guidance.WAV} // spell effect +{29623}{} // spell new round +{29624}{} // spell projectile begin +{29625}{} // spell projectile end +{29626}{} // spell projectile in-flight +{29627}{} // spell hit +{29628}{} // spell struck + // [1005] *[Fire Shield, Mass] {26100}{} // spell begin {26101}{spells\sp_fire_shield_end.WAV} // spell end