//:://///////////////////////////////////////////// //:: Astral Construct spawn include //:: psi_inc_ac_spawn //::////////////////////////////////////////////// //::////////////////////////////////////////////// //:: Created By: Ornedan //:: Created On: 21.01.2005 //::////////////////////////////////////////////// #include "psi_inc_ac_const" #include "prc_ipfeat_const" #include "prc_feat_const" #include "inc_vfx_const" ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// void HandleAstralConstructSpawn(object oConstruct); void DoEffect(object oConstruct, effect eEffect); void DoFeat(object oHide, int nIPFeatID); void DoDeflect(object oConstruct, int nACVal); void DoResistance(object oHide, int nElementFlags); void DoEnergyTouch(object oWeapon, int nElementFlags, object oCreator); int GetConstructSizeFromTag(object oConstruct); int GetBaseDamageFromSize(int nSize); int GetNextBaseDamage(int nDamVal); int GetDamageReduction(object oConstruct); int GetDamageReductionConstantFromAmount(int nDamRed); //void ActionUseInstantPower( invalid ); ////////////////////////////////////////////////// /* Function defintions */ ////////////////////////////////////////////////// // Applies the chosen Astral Construct options to the creature. // // Note! Buff series is handled separately void HandleAstralConstructSpawn(object oConstruct) { // Get the flag set int nFlags = GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_OPTION_FLAGS); // Get the construct's size and use it to determine the base attack damage int nSize = GetConstructSizeFromTag(oConstruct); if (nSize < CREATURE_SIZE_SMALL) { WriteTimestampedLogEntry("Invalid construct size: " + IntToString(nSize) + " for appearance " + IntToString(GetAppearanceType(oConstruct))); } if (DEBUG) WriteTimestampedLogEntry("Creating construct " + GetTag(oConstruct) + " of size " + IntToString(nSize)); string sWeaponResRef = ASTRAL_CONSTRUCT_SLAM; object oHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oConstruct); /* Check through the flags and take appropriate action */ /// The Deflect series int nDeflect = 0; // Add +1 deflection AC if(nFlags & ASTRAL_CONSTRUCT_OPTION_DEFLECTION) { nDeflect += 1; } // Add +4 deflection AC if(nFlags & ASTRAL_CONSTRUCT_OPTION_HEAVY_DEFLECT) { nDeflect += 4; } // Add +8 deflection AC if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DEFLECT) { nDeflect += 8; } if(nDeflect) DoDeflect(oConstruct, nDeflect); if (DEBUG) WriteTimestampedLogEntry(" Construct deflection AC: " + IntToString(nDeflect)); /// The Damage reductions // First, get the base damage reduction int nDamRed = GetDamageReduction(oConstruct); // Increases the damage reduction variable by 3 if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_DAM_RED) { nDamRed += 3; } // Increases the damage reduction variable by 6 if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DAM_RED) { nDamRed += 6; } if (DEBUG) WriteTimestampedLogEntry(" Construct damage reduction: " + IntToString(nDamRed)); if(nDamRed) { nDamRed = GetDamageReductionConstantFromAmount(nDamRed); itemproperty ipDamRed = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_1, nDamRed); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamRed, oHide); } /// Add various feats to the hide // Add the Cleave feat to slam if(nFlags & ASTRAL_CONSTRUCT_OPTION_CLEAVE) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Cleave"); DoFeat(oHide, IP_CONST_FEAT_CLEAVE); } // Add the Mobility feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_MOBILITY) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Mobility"); DoFeat(oHide, IP_CONST_FEAT_MOBILITY); } // Add the Power Attack feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_POWER_ATTACK) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Power Attack"); DoFeat(oHide, IP_CONST_FEAT_POWERATTACK); } // Add the Knockdown feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_KNOCKDOWN) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Knockdown"); DoFeat(oHide, IP_CONST_FEAT_KNOCKDOWN); } // Add the Improved Critical (Creature) feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_CRIT) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Improved Critical"); DoFeat(oHide, IP_CONST_FEAT_ImpCritCreature); } // Add the Blindsight feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_BLINDFIGHT) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Blind Fight"); DoFeat(oHide, IP_CONST_FEAT_BLINDFIGHT); } // Add feat for spell/power resistance 10+HD to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_POWER_RESIST) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Power Resistance"); DoFeat(oHide, IP_CONST_FEAT_SPELL10); } // Add the Rend feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_REND) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Rend"); DoFeat(oHide, IP_CONST_FEAT_REND); sWeaponResRef = ASTRAL_CONSTRUCT_CLAW; } // Add the Spring Attack feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_SPRING_ATTACK) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Spring Attack"); DoFeat(oHide, IP_CONST_FEAT_SPRINGATTACK); } // Add the Whirlwind feat to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_WHIRLWIND) { if (DEBUG) WriteTimestampedLogEntry(" Added feat: Whirlwind"); DoFeat(oHide, IP_CONST_FEAT_WHIRLWIND); } /// Add various itemproperties to the hide // Adds regeneration 2 to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_FAST_HEALING) { itemproperty ipRegen = ItemPropertyRegeneration(2); AddItemProperty(DURATION_TYPE_PERMANENT, ipRegen, oHide); if (DEBUG) WriteTimestampedLogEntry(" Added fast healing 2"); } // Give a +4 STR bonus to hide if(nFlags & ASTRAL_CONSTRUCT_OPTION_MUSCLE) { itemproperty ipSTR = ItemPropertyAbilityBonus(IP_CONST_ABILITY_STR, 4); AddItemProperty(DURATION_TYPE_PERMANENT, ipSTR, oHide); if (DEBUG) WriteTimestampedLogEntry(" Added muscle (+4 STR)"); } // Add energy resistance 5 to chosen elements if(nFlags & ASTRAL_CONSTRUCT_OPTION_RESISTANCE) { DoResistance(oHide, GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_RESISTANCE_FLAGS)); if (DEBUG) WriteTimestampedLogEntry(" Added energy resistance 5"); } /// Handle effects // Add the transparency effect effect eVis = EffectVisualEffect(VFX_DUR_GHOSTLY_PULSE_QUICK);//VFX_DUR_GHOST_TRANSPARENT); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eVis, oConstruct); // Increase speed by 10 feet per round if(nFlags & ASTRAL_CONSTRUCT_OPTION_CELERITY) { int nSpeedIncrease; switch(nSize) { case CREATURE_SIZE_SMALL: nSpeedIncrease = 33; break; case CREATURE_SIZE_MEDIUM: case CREATURE_SIZE_LARGE: nSpeedIncrease = 25; break; case CREATURE_SIZE_HUGE: nSpeedIncrease = 20; break; default: if (DEBUG) WriteTimestampedLogEntry("Invalid size value for an Astral Construct encountered when processing Celerity"); nSpeedIncrease = 0; break; } effect eSpeed = EffectMovementSpeedIncrease(nSpeedIncrease); DoEffect(oConstruct, eSpeed); if (DEBUG) WriteTimestampedLogEntry(" Added celerity (" + IntToString(nSpeedIncrease) + "% increase)"); } // Add one attack if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_ATTACK) { effect eAttack = EffectModifyAttacks(1); DoEffect(oConstruct, eAttack); if (DEBUG) WriteTimestampedLogEntry(" Added extra attack"); } // Applies 50% concealement and a normal invisibility effect to the construct to // simulate permanent invisibility if(nFlags & ASTRAL_CONSTRUCT_OPTION_NATURAL_INVIS) { effect eConceal = EffectConcealment(50); effect eInvis = EffectInvisibility(INVISIBILITY_TYPE_NORMAL); DoEffect(oConstruct, eConceal); DoEffect(oConstruct, eInvis); if (DEBUG) WriteTimestampedLogEntry(" Added natural invisibility"); } /* Start handling the creature weapon stuff */ // First, actually create the weapon and equip it object oWeapon = CreateItemOnObject(sWeaponResRef, oConstruct); AssignCommand(oConstruct, ActionEquipItem(oWeapon, INVENTORY_SLOT_CWEAPON_B)); /// Handle the damage that it will deal // Get the base damage int nAttackBaseDamage = GetBaseDamageFromSize(nSize); if (DEBUG) WriteTimestampedLogEntry(" Set base damage to " + IntToString(nAttackBaseDamage)); // Check if the damage needs to be increased if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMPROVED_SLAM) { nAttackBaseDamage = GetNextBaseDamage(nAttackBaseDamage); if (DEBUG) WriteTimestampedLogEntry(" Added feat: Improved Slam"); } // Apply the monster damage iprop to the weapon itemproperty ipDam = ItemPropertyMonsterDamage(nAttackBaseDamage); AddItemProperty(DURATION_TYPE_PERMANENT, ipDam, oWeapon); // Add damage bonus equal to 0.5 times the creature's strength modifier. // The Astral Constructs should have it since they only have one natural attack (and no other attacks) int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oConstruct) / 2; nStrBon = nStrBon < 0 ? 0 : nStrBon; nStrBon = nStrBon > 10 ? 10: nStrBon; // The Extra Attacks modifier cancels 1.5x STR bonus, so check for it's presence if(nStrBon && !(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_ATTACK) && nSize < CREATURE_SIZE_LARGE) { // The following is a *wrong* way of doing this and will break the moment BW decides to modify // the actual values of the IP_CONST_DAMAGEBONUS_* constants, but I'm lazy :p int nDamBon = nStrBon > 5 ? nStrBon + 10 : nStrBon; int nDamageType = sWeaponResRef == ASTRAL_CONSTRUCT_SLAM ? IP_CONST_DAMAGETYPE_BLUDGEONING : IP_CONST_DAMAGETYPE_SLASHING; itemproperty ipDamBon = ItemPropertyDamageBonus(nDamageType, nDamBon); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamBon, oWeapon); } // Apply OnHitCast power if necessary if(nFlags & ASTRAL_CONSTRUCT_OPTION_POISON_TOUCH || nFlags & ASTRAL_CONSTRUCT_OPTION_REND) { itemproperty ipOnHitCast = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1); AddItemProperty(DURATION_TYPE_PERMANENT, ipOnHitCast, oWeapon); } // Handle Energy Touch option if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_TOUCH) { DoEnergyTouch(oWeapon, GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS), GetMaster(oConstruct)); } /// Handle options that set local ints // Add OnHit: CastSpell - Unique Power to the slam if(nFlags & ASTRAL_CONSTRUCT_OPTION_POISON_TOUCH) { SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_POISON_TOUCH, TRUE); } // Make the construct capable of using Concussion Blast power as a free action every round // Will probably be implemented via heartbeat if(nFlags & ASTRAL_CONSTRUCT_OPTION_CONCUSSION) { SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_CONCUSSION, TRUE); } /********************* UNFINISHED POWERS *********************/ // Allow the construct to use Dimension Slide as a move action every round // COMPLETELY UNIMPLEMENTED AS OF YET if(nFlags & ASTRAL_CONSTRUCT_OPTION_DIMENSION_SLIDE) { WriteTimestampedLogEntry("Astral Construct created with flag ASTRAL_CONSTRUCT_OPTION_DIMENSION_SLIDE. This option shouldn't be available yet."); } // Allow the construct to use Energy Bolt as a standard action every round // COMPLETELY UNIMPLEMENTED AS OF YET if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_BOLT) { WriteTimestampedLogEntry("Astral Construct created with flag ASTRAL_CONSTRUCT_OPTION_ENERGY_BOLT. This option shouldn't be available yet."); } } // Extraordinaries and applies the given effect void DoEffect(object oConstruct, effect eEffect) { eEffect = ExtraordinaryEffect(eEffect); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oConstruct); } // Adds the given feat to the AC's hide. Nothing special, just // a convenience so I needn't typ(e/o) it several times void DoFeat(object oHide, int nIPFeatID) { itemproperty ipFeat = PRCItemPropertyBonusFeat(nIPFeatID); AddItemProperty(DURATION_TYPE_PERMANENT, ipFeat, oHide); } // Does the work of the Deflect series of effects void DoDeflect(object oConstruct, int nACVal) { effect eAC = EffectACIncrease(nACVal, AC_DEFLECTION_BONUS); DoEffect(oConstruct, eAC); } // Adds the given resistances to the construct's hide void DoResistance(object oHide, int nElementFlags) { itemproperty ipResist; if(nElementFlags & ELEMENT_ACID) { ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID, IP_CONST_DAMAGERESIST_5); AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); } if(nElementFlags & ELEMENT_COLD) { ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_COLD, IP_CONST_DAMAGERESIST_5); AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); } if(nElementFlags & ELEMENT_ELECTRICITY) { ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ELECTRICAL, IP_CONST_DAMAGERESIST_5); AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); } if(nElementFlags & ELEMENT_FIRE) { ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE, IP_CONST_DAMAGERESIST_5); AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); } if(nElementFlags & ELEMENT_SONIC) { ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC, IP_CONST_DAMAGERESIST_5); AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); } } // Adds 1d4 (or 1d6 if oCreator is a kineticist) of the chosen types of elemental damage // to the construct's weapon void DoEnergyTouch(object oWeapon, int nElementFlags, object oCreator) { itemproperty ipDamage; int nDamage = GetHasFeat(FEAT_PSION_DIS_KINETICIST, oCreator) ? IP_CONST_DAMAGEBONUS_1d6 : IP_CONST_DAMAGEBONUS_1d4; if(nElementFlags & ELEMENT_ACID) { ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID, nDamage); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); } if(nElementFlags & ELEMENT_COLD) { ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_COLD, nDamage); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); } if(nElementFlags & ELEMENT_ELECTRICITY) { ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ELECTRICAL, nDamage); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); } if(nElementFlags & ELEMENT_FIRE) { ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_FIRE, nDamage); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); } if(nElementFlags & ELEMENT_SONIC) { ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_SONIC, nDamage); AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); } } /* Returns the base damage an AC's attacks should deal based on it's size * Small - 1d4 * Medium - 1d6 * Large - 1d8 * Huge - 2d6 */ int GetBaseDamageFromSize(int nSize) { switch(nSize) { case CREATURE_SIZE_SMALL: return IP_CONST_MONSTERDAMAGE_1d4; case CREATURE_SIZE_MEDIUM: return IP_CONST_MONSTERDAMAGE_1d6; case CREATURE_SIZE_LARGE: return IP_CONST_MONSTERDAMAGE_1d8; case CREATURE_SIZE_HUGE: return IP_CONST_MONSTERDAMAGE_2d6; default: WriteTimestampedLogEntry("Invalid size value for an Astral Construct passed to GetBaseDamageFromSize: " + IntToString(nSize)); } return IP_CONST_MONSTERDAMAGE_1d4; } // Returns the next bigger damage value int GetNextBaseDamage(int nDamVal) { switch(nDamVal) { case IP_CONST_MONSTERDAMAGE_1d4: return IP_CONST_MONSTERDAMAGE_1d6; case IP_CONST_MONSTERDAMAGE_1d6: return IP_CONST_MONSTERDAMAGE_1d8; case IP_CONST_MONSTERDAMAGE_1d8: return IP_CONST_MONSTERDAMAGE_2d6; case IP_CONST_MONSTERDAMAGE_2d6: return IP_CONST_MONSTERDAMAGE_3d6; default: WriteTimestampedLogEntry("Invalid monster damage value passed to GetNextBaseDamage: " + IntToString(nDamVal)); } return IP_CONST_MONSTERDAMAGE_1d4; } // Returns the size of the construct based on its level int GetConstructSizeFromTag(object oConstruct) { string sTag = GetTag(oConstruct); int nLevel = StringToInt(GetSubString(sTag, 14, 1)); switch(nLevel) { case 1: return CREATURE_SIZE_SMALL; case 2: case 3: case 4: return CREATURE_SIZE_MEDIUM; case 5: case 6: case 7: case 8: return CREATURE_SIZE_LARGE; case 9: return CREATURE_SIZE_HUGE; default: WriteTimestampedLogEntry("Erroneous value for nLevel in GetDamageReduction: " + IntToString(nLevel) + ", tag: " + sTag); } return 0; } // Returns the damage reduction that the level of the construct entitles it to int GetDamageReduction(object oConstruct) { string sTag = GetTag(oConstruct); int nLevel = StringToInt(GetSubString(sTag, 14, 1)); switch(nLevel) { case 1: case 2: case 3: case 4: return 0; case 5: return 5; case 6: case 7: return 10; case 8: case 9: return 15; default: WriteTimestampedLogEntry("Erroneous value for nLevel in GetDamageReduction: " + IntToString(nLevel) + ", tag: " + sTag); } return 0; } // Returns the IP_CONST_DAMAGESOAK_*_HP constant that does the given // amount of damage reduction int GetDamageReductionConstantFromAmount(int nDamRed) { switch(nDamRed) { case 3: return IP_CONST_DAMAGESOAK_3_HP; case 5: return IP_CONST_DAMAGESOAK_5_HP; case 6: return IP_CONST_DAMAGESOAK_6_HP; case 8: return IP_CONST_DAMAGESOAK_8_HP; case 9: return IP_CONST_DAMAGESOAK_9_HP; case 10: return IP_CONST_DAMAGESOAK_10_HP; case 11: return IP_CONST_DAMAGESOAK_11_HP; case 13: return IP_CONST_DAMAGESOAK_13_HP; case 14: return IP_CONST_DAMAGESOAK_14_HP; case 15: return IP_CONST_DAMAGESOAK_15_HP; case 16: return IP_CONST_DAMAGESOAK_16_HP; case 19: return IP_CONST_DAMAGESOAK_19_HP; case 18: return IP_CONST_DAMAGESOAK_18_HP; case 21: return IP_CONST_DAMAGESOAK_21_HP; case 24: return IP_CONST_DAMAGESOAK_24_HP; default: WriteTimestampedLogEntry("Erroneous value for nDamRed in GetDamageReductionAmountToConstant: " + IntToString(nDamRed)); } return -1; }