void DoNaturalWeaponHB(object oPC = OBJECT_SELF); #include "prc_inc_combat" #include "prc_inc_template" object GetProperTarget(object oPC, object oTarget) { location lTarget = GetLocation(oPC); // Use the function to get the closest creature as a target object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE); while(GetIsObjectValid(oAreaTarget)) { if(oAreaTarget != oPC && // Not you. GetIsEnemy(oPC, oAreaTarget) && // Enemies only, please GetIsInMeleeRange(oPC, oAreaTarget)) // They must be in melee range { return oAreaTarget; } //Select the next target within the spell shape. oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE); } return oTarget; } void DoNaturalAttack(object oWeapon) { //if weapon is not valid, abort if(!GetIsObjectValid(oWeapon)) return; // object oTarget = GetAttackTarget(); object oTarget = GetAttemptedAttackTarget(); // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant /* //no point attacking plot if(GetPlotFlag(oTarget)) return; */ if (DEBUG) DoDebug("DoNaturalAttack oTarget1 "+GetName(oTarget)); object oPC = OBJECT_SELF; // Make sure you don't hit yourself. Some idiot didn't check that. if (GetIsFriend(oPC, oTarget) || oPC == oTarget) oTarget = GetProperTarget(oPC, oTarget); if (DEBUG) DoDebug("DoNaturalAttack oTarget2 "+GetName(oTarget)); // if not melee/ranged fighting, abort if (!GetIsObjectValid(oTarget)) return; // natural attacks are (usually) not ranged attacks, so PerformAttack() will move to a target, if it is out of melee range // However, we do not want to run to the target if we are in the midst of doing ranged attacks, e.g. if we have a ranged // weapon equipped (and are still out of melee range, so we can effectively do ranged attacks) // so check for a ranged weapon in the right hand slot and abort if we have one and are out of melee range object oWeaponR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); if(GetIsObjectValid(oWeaponR) && GetIsRangedWeaponType(GetBaseItemType(oWeaponR)) && !GetIsInMeleeRange(oTarget, oPC)) return; /* //if not fighting, abort // motu99: PRCGetIsFighting() not only checks. whether GetAttemptedAttackTarget() (relevant for melee attacks) returns a valid object // it also checks whether we have attempted to attack a valid target with a spell // but spell attacks don't make sense for natural attacks (which are pure melee), so commented out if(!PRCGetIsFighting(oPC)) { DoDebug(COLOR_WHITE + "DoNaturalAttack(): not fighting any more - aborting"); return; } */ //null effect effect eInvalid; string sMessageSuccess; string sMessageFailure; if(DEBUG) { sMessageSuccess += GetName(oWeapon); //add attack sMessageSuccess += " natural attack"; //copy it to failure sMessageFailure = sMessageSuccess; //add hit/miss sMessageSuccess += " hit"; sMessageFailure += " miss"; //add stars around messages sMessageSuccess = "*"+sMessageSuccess+"*"; sMessageFailure = "*"+sMessageFailure+"*"; } //secondary attacks are -5 to hit int nAttackMod = -5; if (GetHasTemplate(TEMPLATE_GRAVETOUCHED_GHOUL, oPC)) nAttackMod = -2; // This is a standin for multiattack /* //check for (Improved) Multiattack if(GetHasFeat(FEAT_IMPROVED_MULTIATTACK, oPC)) nAttackMod = 0; else if(GetHasFeat(FEAT_MULTIATTACK, oPC)) nAttackMod = -2; */ //secondary attacks are half strength (we use offhand for this) // set the prc combat mode int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; int nEssentia = GetEssentiaInvested(oPC, MELD_GIRALLON_ARMS); if (nEssentia) IPSafeAddItemProperty(oWeapon, ItemPropertyEnhancementBonus(nEssentia), 9999.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE); int nStrHalf = GetAbilityModifier(ABILITY_STRENGTH, oPC)/2; nStrHalf = nStrHalf * -1; if(DEBUG) DoDebug(PRC_TEXT_WHITE + "initiating a secondary natural attack with "+GetName(oWeapon)+", attack mod " + IntToString(nAttackMod) + ", nStrHalf " + IntToString(nStrHalf)); PerformAttack(oTarget, oPC, // eInvalid, //effect eSpecialEffect, 0.0, //float eDuration = 0.0 nAttackMod, //int iAttackBonusMod = 0 nStrHalf, //int iDamageModifier = Half strength DAMAGE_TYPE_SLASHING, //int iDamageType = DAMAGE_TYPE_SLASHING, otherwise it uses magical damage. sMessageSuccess, //sMessageSuccess sMessageFailure, //sMessageFailure FALSE, //int iTouchAttackType = FALSE oWeaponR, // we should have something in the right hand (might need it for some calculations) oWeapon, // we put the creature weapon in the left hand slot FALSE, //offhand override (for half strength) bCombatMode // prc scripted combat mode ); } void DoOffhandAttack(int nAttackMod) { object oPC = OBJECT_SELF; object oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); // check for offhand or double sided weapon, if not - return if (!GetIsOffhandWeapon(oWeapon)) { object oWeaponR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); if (!GetIsDoubleSidedWeapon(oWeaponR)) return; oWeapon = oWeaponR; } // object oTarget = GetAttackTarget(); object oTarget = GetAttemptedAttackTarget(); // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant /* //no point attacking plot if(GetPlotFlag(oTarget)) return; */ // Make sure you don't hit yourself. Some idiot didn't check that. if (GetIsFriend(oPC, oTarget) || oPC == oTarget) oTarget = GetProperTarget(oPC, oTarget); // if not melee fighting, abort if (!GetIsObjectValid(oTarget)) return; /* //if not fighting, abort if(!PRCGetIsFighting(oPC)) { DoDebug(COLOR_WHITE + "DoOffhandAttack(): not fighting any more - aborting"); return; } */ string sMessageSuccess; string sMessageFailure; if (DEBUG) { sMessageSuccess += GetName(oWeapon); //add attack sMessageSuccess += " scripted offhand"; //copy it to failure sMessageFailure = sMessageSuccess; //add hit/miss sMessageSuccess += " hit"; sMessageFailure += " miss"; //add stars around messages sMessageSuccess = "*"+sMessageSuccess+"*"; sMessageFailure = "*"+sMessageFailure+"*"; } //null effect effect eInvalid; // set the prc combat mode int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; if (DEBUG) DoDebug(PRC_TEXT_WHITE + "initiating an overflow offhand attack with "+GetName(oWeapon)+" and attack mod "+IntToString(nAttackMod)); PerformAttack(oTarget, oPC, // eInvalid, //effect eSpecialEffect, 0.0, //float eDuration = 0.0 nAttackMod, //int iAttackBonusMod = 0 0, //int iDamageModifier = 0 DAMAGE_TYPE_SLASHING, //int iDamageType = DAMAGE_TYPE_SLASHING, otherwise it uses magical damage. sMessageSuccess, //sMessageSuccess, //string sMessageSuccess = "" sMessageFailure, //sMessageFailure, //string sMessageFailure = "" FALSE, //int iTouchAttackType = FALSE oWeapon, //object oRightHandOverride = OBJECT_INVALID, oWeapon, //object oLeftHandOverride = OBJECT_INVALID, 1, // offhand attack bCombatMode // prc combat mode ); } void DoOverflowOnhandAttack(int nAttackMod) { object oPC = OBJECT_SELF; object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); //if weapon is not valid, abort if(!GetIsObjectValid(oWeapon)) return; // object oTarget = GetAttackTarget(); object oTarget = GetAttemptedAttackTarget(); // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant /* //no point attacking plot if(GetPlotFlag(oTarget)) return; */ // Make sure you don't hit yourself. Some idiot didn't check that. if (GetIsFriend(oPC, oTarget) || oPC == oTarget) oTarget = GetProperTarget(oPC, oTarget); // if not melee fighting, abort if (!GetIsObjectValid(oTarget)) return; /* //if not fighting, abort if(!PRCGetIsFighting(oPC)) { DoDebug(COLOR_WHITE + "DoOverflowOnhandAttack(): not fighting any more - aborting"); return; } */ string sMessageSuccess; string sMessageFailure; if (DEBUG) { sMessageSuccess += GetName(oWeapon); //add attack sMessageSuccess += " scripted overflow"; //copy it to failure sMessageFailure = sMessageSuccess; //add hit/miss sMessageSuccess += " hit"; sMessageFailure += " miss"; //add stars around messages sMessageSuccess = "*"+sMessageSuccess+"*"; sMessageFailure = "*"+sMessageFailure+"*"; } //null effect effect eInvalid; // set the prc combat mode int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; if (DEBUG) DoDebug(PRC_TEXT_WHITE+"initiating an overflow onhand attack with "+GetName(oWeapon)+" and attack mod "+IntToString(nAttackMod)); PerformAttack(oTarget, oPC, // eInvalid, //effect eSpecialEffect, 0.0, //float eDuration = 0.0 nAttackMod, //int iAttackBonusMod = 0 0, //int iDamageModifier = 0 DAMAGE_TYPE_SLASHING, //int iDamageType = DAMAGE_TYPE_SLASHING, otherwise it uses magical damage. sMessageSuccess,//sMessageSuccess, //string sMessageSuccess = "" sMessageFailure,//sMessageFailure, //string sMessageFailure = "" FALSE, //int iTouchAttackType = FALSE oWeapon, //object oRightHandOverride = OBJECT_INVALID, OBJECT_INVALID, //object oLeftHandOverride = OBJECT_INVALID 0, bCombatMode ); } void DoNaturalWeaponHB(object oPC = OBJECT_SELF) { //not in combat, abort if(!GetIsInCombat(oPC)) return; // if(DEBUG) DoDebug("entered DoNaturalWeaponHB"); float fDelay = 0.1 + IntToFloat(Random(10))/100.0; //no natural weapons, abort //in a different form, abort for now fix it later if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) && !GetIsPolyMorphedOrShifted(oPC)) { // DoDebug("DoNaturalWeaponHB: creature has natural secondary weapons"); UpdateSecondaryWeaponSizes(oPC); int i; while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) { //get the resref to use string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); //if null, move to next if(sResRef != "") { //get the created item object oWeapon = GetObjectByTag(sResRef); if(!GetIsObjectValid(oWeapon)) { object oLimbo = GetObjectByTag("HEARTOFCHAOS"); location lLimbo = GetLocation(oLimbo); if(!GetIsObjectValid(oLimbo)) lLimbo = GetStartingLocation(); oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo); } // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); //do the attack within a delay /* // motu99: commented this out; AssignCommand ist not needed, because OBJECT_SELF is oPC - using AssignCommand will only degrade performance AssignCommand(oPC, DelayCommand(fDelay, DoNaturalAttack(oWeapon))); */ DelayCommand(fDelay, DoNaturalAttack(oWeapon)); //calculate the delay to use next time fDelay += 2.05; if(fDelay > 6.0) fDelay -= 6.0; } i++; } } int iMod = 5; // motu99: added check for monk weapon if(GetHasMonkWeaponEquipped(oPC)) iMod = 3; // check for overflow (main hand) attacks int nOverflowAttackCount = GetLocalInt(oPC, "OverflowBaseAttackCount"); if(nOverflowAttackCount) { int i; // the first overflow attack would be the seventh main hand attack, at an AB of -30 int nAttackPenalty = -6 * iMod; // -30 for normal bab, -18 for monks // DoDebug("DoNaturalWeaponHB(): number of scripted overflow attacks: "+IntToString(nOverflowAttackCount)); for(i = 0; i < nOverflowAttackCount; i++) { // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted overflow attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); /* // motu99: see comment above why this is commented out AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); */ DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty)); //calculate the delay to use fDelay += 2.05; if(fDelay > 6.0) fDelay -= 6.0; //calculate new attack penalty nAttackPenalty -= iMod; // motu99: usually -5, for monks -3 (unarmed or kama) } } // motu99: this is only here for debugging in order to test PerformAttackRound() // must be deleted after debugging!!! //if (GetPRCSwitch(PRC_PNP_TRUESEEING)) DelayCommand(0.01, DoOffhandAttackRound()); // check for overflow offhand attacks int nOffhandAttackCount = GetLocalInt(oPC, "OffhandOverflowAttackCount"); // if (DEBUG) DoDebug("DoNaturalWeaponHB: number of scripted offhand attacks = "+IntToString(nOffhandAttackCount)); if(nOffhandAttackCount) { int i; int nAttackPenalty = -2 * iMod; // offhand attacks always come at -5 per additional attack (but for monks we assume -3) for(i = 0; i < nOffhandAttackCount; i++) { // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted offhand attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); DelayCommand(fDelay, DoOffhandAttack(nAttackPenalty)); //calculate the delay to use fDelay += 2.05; if(fDelay > 6.0) fDelay -= 6.0; //calculate new attack penalty nAttackPenalty -= iMod; } } } /* * motu99's test functions. Not actually used by PRC scripts */ // motu99: This is only for test purposes (in order to test PerformAttackRound()) void DoOffhandAttackRound(object oPC = OBJECT_SELF) { // object oTarget = GetAttackTarget(); object oTarget = GetAttemptedAttackTarget(); // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant /* //no point attacking plot if(GetPlotFlag(oTarget)) return; */ // if not melee fighting, abort if (!GetIsObjectValid(oTarget)) return; /* //if not fighting, abort if(!PRCGetIsFighting(oPC)) { DoDebug(COLOR_WHITE + "DoOffhandAttack(): not fighting any more - aborting"); return; } */ string sMessageSuccess; string sMessageFailure; if (DEBUG) { // sMessageSuccess += GetName(oWeapon); //add attack // sMessageSuccess += " scripted offhand"; //copy it to failure // sMessageFailure = sMessageSuccess; //add hit/miss sMessageSuccess += "s hit"; sMessageFailure += "s miss"; //add stars around messages sMessageSuccess = "*"+sMessageSuccess+"*"; sMessageFailure = "*"+sMessageFailure+"*"; } //null effect effect eInvalid; // set the prc combat mode int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; DoDebug(PRC_TEXT_WHITE + "initiating an overflow offhand attack round"); PerformAttackRound(oTarget, // object oDefender oPC, // object oAttacker, eInvalid, // effect eSpecialEffect, 0.0, // float eDuration = 0.0, 0, // int iAttackBonusMod = 0, 0, // int iDamageModifier = 0, 0, // int iDamageType = 0, TRUE, // int bEffectAllAttacks = FALSE, sMessageSuccess, // string sMessageSuccess = "", sMessageFailure, // string sMessageFailure = "", 0, // int bApplyTouchToAll = FALSE, 0, // int iTouchAttackType = FALSE, 0, //int bInstantAttack = FALSE); bCombatMode // CombatMode ); }