//:://///////////////////////////////////////////// //:: Astral Construct manifestation include //:: psi_inc_ac_manif //::////////////////////////////////////////////// //::////////////////////////////////////////////// //:: Created By: Ornedan //:: Created On: 23.01.2005 //::////////////////////////////////////////////// #include "prc_inc_spells" #include "psi_inc_psifunc" #include "psi_inc_ac_const" #include "inc_utility" ////////////////////////////////////////////////// /* Constant defintions */ ////////////////////////////////////////////////// const int TEMP_HENCH_COUNT = 150; ////////////////////////////////////////////////// /* Structure definitions */ ////////////////////////////////////////////////// // A structure containing appearance references struct ac_forms{ int Appearance1, Appearance1Alt; int Appearance2, Appearance2Alt; int Appearance3, Appearance3Alt; int Appearance4, Appearance4Alt; }; ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// void DoAstralConstructCreation(struct manifestation manif, location locTarget, int nACLevel, int nOptionFlags, int nResElemFlags, int nETchElemFlags, int nEBltElemFlags); void DoDespawn(object oConstruct, int bDoVFX = TRUE); void DoDespawnAux(object oManifester, float fDur); void DoSummonVFX(location locTarget, int nACLevel); void DoUnsummonVFX(location locTarget, int nACLevel); struct ac_forms GetAppearancessForLevel(int nLevel); int GetAppearanceForConstruct(int nACLevel, int nOptionFlags, int nCheck); int GetUseAltAppearances(int nOptionFlags); string GetResRefForConstruct(int nACLevel, int nOptionFlags); int GetHighestCraftSkillValue(object oCreature); ////////////////////////////////////////////////// /* Function defintions */ ////////////////////////////////////////////////// // Summons the specified Astral Construct at the given location // Handling of the flags (other than the Buff series) is done // in the creature's OnSpawn eventscript void DoAstralConstructCreation(struct manifestation manif, location locTarget, int nACLevel, int nOptionFlags, int nResElemFlags, int nETchElemFlags, int nEBltElemFlags) { // Get the resref for the AC string sResRef = GetResRefForConstruct(nACLevel, nOptionFlags); // Get the constructs duration. 1 round / level. Metapsionic Extend can be applied. float fDur = 6.0 * GetManifesterLevel(manif.oManifester); if(GetPRCSwitch(PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD) > 0) fDur *= GetPRCSwitch(PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD); if(manif.bExtend) fDur *= 2; // Add in 1 round of duration to account for AI disorientation in the beginning fDur += 6.0f; /* Until Bioware "fixes" Jasperre's multisummon trick, AC are added as genuine summons instead of henchies // We need to make sure that we can add the new construct as henchman int nMaxHenchmen = GetMaxHenchmen(); SetMaxHenchmen(TEMP_HENCH_COUNT); // Add the AC as henchman object oConstruct = CreateObject(OBJECT_TYPE_CREATURE, sResRef, locTarget); AddHenchman(oManifester, oConstruct); // And set the max henchmen count back to original, so we won't mess up the module SetMaxHenchmen(nMaxHenchmen); */ // Do multisummon trick int bMultisummon = GetPRCSwitch(PRC_MULTISUMMON); int i = 1; object oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, manif.oManifester, i); while(GetIsObjectValid(oCheck)) { //DoDebug("DoAstralConstructCreation: Handling associate " + DebugObject2Str(oCheck)); // If multisummon is active, make all summons indestructible. If not, only make astral constructs if(bMultisummon || GetStringLeft(GetTag(oCheck), 14) == "psi_astral_con") { AssignCommand(oCheck, SetIsDestroyable(FALSE, FALSE, FALSE)); AssignCommand(oCheck, DelayCommand(1.0, SetIsDestroyable(TRUE, FALSE, FALSE))); oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, manif.oManifester, ++i); } } // Do actual summon effect ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectSummonCreature(sResRef), locTarget, fDur + 0.5); /* For use if need to return to henchman setup // Add the locals to the construct SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_LEVEL, nACLevel); SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_OPTION_FLAGS, nOptionFlags); SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_RESISTANCE_FLAGS, nResElemFlags); SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS, nETchElemFlags); SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_ENERGY_BOLT_FLAGS, nEBltElemFlags); // Do appearance switching int nCraft = GetHighestCraftSkillValue(oManifester); int nCheck = d20() + nCraft; int nAppearance = GetAppearanceForConstruct(nACLevel, nOptionFlags, nCheck); SetCreatureAppearanceType(oConstruct, nAppearance); */ // Do VFX DoSummonVFX(locTarget, nACLevel); // Schedule unsummoning. No need to hurry this one, so give it a larger delay // in order to avoid hogging too much resources over a short span of time. DelayCommand(2.0, DoDespawnAux(manif.oManifester, fDur)); } // A function to handle the AC's duration running out // Some paranoia present to make sure nothing could accidentally // make it permanent void DoDespawn(object oConstruct, int bDoVFX = TRUE) { if(GetIsObjectValid(oConstruct)){ DestroyObject(oConstruct); DelayCommand(0.15f, MyDestroyObject(oConstruct)); // The paranoia bit :D if(bDoVFX) DoUnsummonVFX(GetLocation(oConstruct), GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_LEVEL)); } } // An auxiliary to be delayed so that a reference to the just created AC can be found void DoDespawnAux(object oManifester, float fDur){ // Find the newly added construct object oConstruct = OBJECT_INVALID; int i = 1; object oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oManifester, i); while(GetIsObjectValid(oCheck)) { if(!GetLocalInt(oCheck, "UnsummonScheduled") && GetStringLeft(GetTag(oCheck), 14) == "psi_astral_con") { oConstruct = oCheck; break; } i++; oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oManifester, i); } SetLocalInt(oConstruct, "UnsummonScheduled", TRUE); if(DEBUG) DoDebug("Found the just added astral construct: " + (GetIsObjectValid(oConstruct) ? "true":"false") + "\nSummon name: " + GetName(oConstruct) + "\nTotal summons: " + IntToString(i), oManifester); // Schedule unsummoning. Done this way to skip the default unsummoning VFX. DelayCommand(fDur - 2.0, DoDespawn(oConstruct)); } // Does a visual choreography that depends on the level of the construct being created. // Higher level constructs get neater VFX :D void DoSummonVFX(location locTarget, int nACLevel){ DrawSpiral(0, 263, locTarget, IntToFloat(nACLevel) * 0.75 + 0.5, 0.4, 1.0, 60, 16.899999619, 3.0, 4.0); } void DoUnsummonVFX(location locTarget, int nACLevel){ DrawSpiral(0, 263, locTarget, 0.4, IntToFloat(nACLevel) * 0.75 + 0.5, 1.0, 60, 16.899999619, 3.0, 4.0); } struct ac_forms GetAppearancesForLevel(int nLevel) { struct ac_forms toReturn; switch(nLevel) { case 1: toReturn.Appearance1 = APPEARANCE_TYPE_RAT; toReturn.Appearance1Alt = 387; //Dire Rat toReturn.Appearance2 = APPEARANCE_TYPE_INTELLECT_DEVOURER; toReturn.Appearance2Alt = APPEARANCE_TYPE_WAR_DEVOURER; toReturn.Appearance3 = APPEARANCE_TYPE_PSEUDODRAGON; toReturn.Appearance3Alt = APPEARANCE_TYPE_PSEUDODRAGON; toReturn.Appearance4 = APPEARANCE_TYPE_FAERIE_DRAGON; toReturn.Appearance4Alt = APPEARANCE_TYPE_FAERIE_DRAGON; return toReturn; case 2: toReturn.Appearance1 = APPEARANCE_TYPE_GARGOYLE; toReturn.Appearance1Alt = APPEARANCE_TYPE_GARGOYLE; toReturn.Appearance2 = APPEARANCE_TYPE_BAT_HORROR; toReturn.Appearance2Alt = APPEARANCE_TYPE_HELMED_HORROR; toReturn.Appearance3 = APPEARANCE_TYPE_ASABI_WARRIOR; toReturn.Appearance3Alt = APPEARANCE_TYPE_LIZARDFOLK_WARRIOR_B; toReturn.Appearance4 = APPEARANCE_TYPE_WERECAT; toReturn.Appearance4Alt = APPEARANCE_TYPE_WERECAT; return toReturn; case 3: toReturn.Appearance1 = APPEARANCE_TYPE_FORMIAN_WORKER; toReturn.Appearance1Alt = APPEARANCE_TYPE_FORMIAN_WORKER; toReturn.Appearance2 = APPEARANCE_TYPE_FORMIAN_WARRIOR; toReturn.Appearance2Alt = APPEARANCE_TYPE_FORMIAN_WARRIOR; toReturn.Appearance3 = APPEARANCE_TYPE_FORMIAN_MYRMARCH; toReturn.Appearance3Alt = APPEARANCE_TYPE_FORMIAN_MYRMARCH; toReturn.Appearance4 = APPEARANCE_TYPE_FORMIAN_QUEEN; toReturn.Appearance4Alt = APPEARANCE_TYPE_FORMIAN_QUEEN; return toReturn; case 4: toReturn.Appearance1 = 416; // Deep Rothe toReturn.Appearance1Alt = 416; toReturn.Appearance2 = APPEARANCE_TYPE_MANTICORE; toReturn.Appearance2Alt = APPEARANCE_TYPE_MANTICORE; toReturn.Appearance3 = APPEARANCE_TYPE_BASILISK; toReturn.Appearance3Alt = APPEARANCE_TYPE_GORGON; toReturn.Appearance4 = APPEARANCE_TYPE_DEVIL; toReturn.Appearance4Alt = 468; // Golem, Demonflesh return toReturn; case 5: toReturn.Appearance1 = APPEARANCE_TYPE_GOLEM_FLESH; toReturn.Appearance1Alt = APPEARANCE_TYPE_GOLEM_FLESH; toReturn.Appearance2 = APPEARANCE_TYPE_GOLEM_STONE; toReturn.Appearance2Alt = APPEARANCE_TYPE_GOLEM_STONE; toReturn.Appearance3 = APPEARANCE_TYPE_GOLEM_CLAY; toReturn.Appearance3Alt = APPEARANCE_TYPE_GOLEM_CLAY; toReturn.Appearance4 = 420; // Golem, Mithril toReturn.Appearance4Alt = 420; return toReturn; case 6: toReturn.Appearance1 = APPEARANCE_TYPE_TROLL; toReturn.Appearance1Alt = APPEARANCE_TYPE_TROLL; toReturn.Appearance2 = APPEARANCE_TYPE_ETTERCAP; toReturn.Appearance2Alt = APPEARANCE_TYPE_ETTERCAP; toReturn.Appearance3 = APPEARANCE_TYPE_UMBERHULK; toReturn.Appearance3Alt = APPEARANCE_TYPE_UMBERHULK; toReturn.Appearance4 = APPEARANCE_TYPE_MINOTAUR_SHAMAN; toReturn.Appearance4Alt = APPEARANCE_TYPE_MINOGON; return toReturn; case 7: toReturn.Appearance1 = APPEARANCE_TYPE_SPIDER_DIRE; toReturn.Appearance1Alt = APPEARANCE_TYPE_SPIDER_DIRE; toReturn.Appearance2 = APPEARANCE_TYPE_SPIDER_SWORD; toReturn.Appearance2Alt = APPEARANCE_TYPE_SPIDER_SWORD; toReturn.Appearance3 = 446; // Drider, Female toReturn.Appearance3Alt = 446; toReturn.Appearance4 = 407; // Drider, Chief toReturn.Appearance4Alt = 407; return toReturn; case 8: toReturn.Appearance1 = APPEARANCE_TYPE_HOOK_HORROR; toReturn.Appearance1Alt = APPEARANCE_TYPE_VROCK; toReturn.Appearance2 = 427; // Slaad, White toReturn.Appearance2Alt = 427; toReturn.Appearance3 = APPEARANCE_TYPE_GREY_RENDER; toReturn.Appearance3Alt = APPEARANCE_TYPE_GREY_RENDER; toReturn.Appearance4 = APPEARANCE_TYPE_GREY_RENDER; toReturn.Appearance4Alt = APPEARANCE_TYPE_GREY_RENDER; return toReturn; case 9: toReturn.Appearance1 = APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER; toReturn.Appearance1Alt = APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER; toReturn.Appearance2 = APPEARANCE_TYPE_GIANT_FROST_FEMALE; toReturn.Appearance2Alt = APPEARANCE_TYPE_GIANT_FROST_FEMALE; toReturn.Appearance3 = 418; // Dragon, Shadow toReturn.Appearance3Alt = 418; toReturn.Appearance4 = 471; // Mephisto, Normal toReturn.Appearance4Alt = 471; return toReturn; default:{ string sErr = "psi_inc_ac_manif: GetAppearancesForLevel(): ERROR: Erroneous value for nLevel: " + IntToString(nLevel); if(DEBUG) DoDebug(sErr); else WriteTimestampedLogEntry(sErr); } } return toReturn; } int GetAppearanceForConstruct(int nACLevel, int nOptionFlags, int nCheck) { int bUse2da = GetPRCSwitch(PRC_PSI_ASTRAL_CONSTRUCT_USE_2DA); int bUseAlt = GetUseAltAppearances(nOptionFlags); int nNum = nCheck < AC_APPEARANCE_CHECK_HIGH ? nCheck < AC_APPEARANCE_CHECK_MEDIUM ? nCheck < AC_APPEARANCE_CHECK_LOW ? 1 : 2 : 3 : 4; // If we use 2da, get the data from there if(bUse2da) { nNum += (nACLevel - 1) * 4 - 1; return StringToInt(Get2DACache("ac_appearances", bUseAlt ? "AltAppearance" : "NormalAppearance", nNum)); } // We don't so get it from GetAppearancesForLevel struct ac_forms appearancelist = GetAppearancesForLevel(nACLevel); switch(nNum) { case 1: return bUseAlt ? appearancelist.Appearance1Alt : appearancelist.Appearance1; case 2: return bUseAlt ? appearancelist.Appearance2Alt : appearancelist.Appearance2; case 3: return bUseAlt ? appearancelist.Appearance3Alt : appearancelist.Appearance3; case 4: return bUseAlt ? appearancelist.Appearance4Alt : appearancelist.Appearance4; default:{ string sErr = "psi_inc_ac_manif: GetAppearanceForConstruct(): ERROR: Erroneous value for nNum: " + IntToString(nNum); if(DEBUG) DoDebug(sErr); else WriteTimestampedLogEntry(sErr); } } return -1; } int GetUseAltAppearances(int nOptionFlags) { // Buff series return nOptionFlags & ASTRAL_CONSTRUCT_OPTION_BUFF || nOptionFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF || nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF || // Deflection series nOptionFlags & ASTRAL_CONSTRUCT_OPTION_DEFLECTION || nOptionFlags & ASTRAL_CONSTRUCT_OPTION_HEAVY_DEFLECT || nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DEFLECT || // Damage Reduction Series nOptionFlags & ASTRAL_CONSTRUCT_OPTION_IMP_DAM_RED || nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DAM_RED; } string GetResRefForConstruct(int nACLevel, int nOptionFlags) { string sResRef = "psi_astral_con" + IntToString(nACLevel); string sSuffix; // Check whether we need a resref with buff applied if(nOptionFlags & ASTRAL_CONSTRUCT_OPTION_BUFF) sSuffix += "a"; else if(nOptionFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF) sSuffix += "b"; else if(nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF) sSuffix += "c"; return sResRef + sSuffix; } int GetHighestCraftSkillValue(object oCreature) { int nArmor = GetSkillRank(SKILL_CRAFT_ARMOR, oCreature); int nTrap = GetSkillRank(SKILL_CRAFT_TRAP, oCreature); int nWeapon = GetSkillRank(SKILL_CRAFT_WEAPON, oCreature); return nArmor > nTrap ? nArmor > nWeapon ? nArmor : nWeapon : nTrap > nWeapon ? nTrap : nWeapon; }