//////////////////////////////////////////////////////////////////////////////// // Common Functions Library // -TheEngine Jan 5/2003 // www.zeromassengine.com //////////////////////////////////////////////////////////////////////////////// // NOTES for future revisions // //////////////////////////////////////////////////////////////////////////////// // Food System v1.00 // -TheEngine Nov 9/2002 // www.zeromassengine.com // NOTES // Standard usage is 120 units for 1 day of rations (5 units per game-hour) // Therefore, 6 units of supplies is deducted every 24 seconds under the STANDARD conditions // // *** DEC 19 - Cut the food consumption rate by 40% - it was just eating resources too fast. // Default Heartbeat for system is now 60 seconds instead of 24 seconds. // - Added code to FoodSys_HeartBeat() so if a PC is dead (0 HP), he will not // use food. // *** JAN 5, 2003 - Integrated Foodsystem scripts into Common scripts library file // // STANDARD CONDITIONS // o 120 Supply units = 1 day of rations // o 6 food units for every 4 rounds (24 seconds), totalling 120 units for 1 game day // Since there are 480 rounds in 1 game day, there are 20 x 24-second periods in a game day. // 20 x 6 = 120 supply units. // o SEASON = Fall; Time of Day = Day; CON-mod = 0; STR-mod = 0; Terrain = GRASSLAND; Modifier = NORMAL; // Ambient Temp = 25 C; No Clothing. // // TIME Notes // 1 round = 6 seconds, 1 turn = 60 seconds // 2 real minutes = 1 game hour // 48 rm's = 1 game day // // CONSIDERATIONS // - Season // - Time of Day // - PC Constitution // - PC Strength // - Terrain Type // - Ambient Temperature // - PC Clothing/Equipment/ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Includes //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// #include "NW_O2_CONINCLUDE" #include "NW_I0_GENERIC" //////////////////////////////////////////////////////////////////////////////// // Constants /////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// int SEASON_SUMMER = 1; int SEASON_FALL = 2; // JUN - AUG then SEP - NOV int SEASON_WINTER = 3; int SEASON_SPRING = 4; // DEC - FEB then MAR - MAY // Time of Day Constants int TOD_DUSK = 1; int TOD_DAWN = 2; int TOD_DAY = 3; int TOD_NIGHT = 4; // Healing int HEAL_EFFECTS_NONE = 0; // Healing Function Constants int HEAL_EFFECTS_BASIC = 1; // Effects to be removed will be AC, Attack, and Damage Decrease int HEAL_EFFECTS_SENSORY = 2; // Saving throws, Curse, Blindness and Deafness int HEAL_EFFECTS_CONSTITUTION = 4; // Constitution impairments. Damage Immunity Decrease, Disease, Poison and Paralysis int HEAL_EFFECTS_ENERGY_DRAIN = 8; // Negative Levels are lifted. int HEAL_EFFECTS_ADVANCED = 16; // Ability decrease, Skill decrease and Spell Resistance Decrease int HEAL_EFFECTS_ALL = 31; // Remove ALL effects // Blessing int HEAL_BLESS_DURATION_LOW = 16; // 1 minutes blessing duration int HEAL_BLESS_DURATION_MEDIUM = 32; // 3 minutes int HEAL_BLESS_DURATION_HIGH = 64; // 6 minutes int HEAL_BLESS_NONE = 0; // Blessing 'packages' int HEAL_BLESS_BASIC = 1; // +1 AC/Attack/Saves int HEAL_BLESS_AVERAGE = 2; // +2 AC/Attack/Saves int HEAL_BLESS_HIGH = 4; // +3 AC/Attack/Saves int HEAL_BLESS_EXTRA_DAMAGE = 128; // Optional Blessing Effects int HEAL_BLESS_DAMAGE_REDUCTION = 256; int HEAL_BLESS_DAMAGE_RESIST_ACID = 512; int HEAL_BLESS_DAMAGE_RESIST_COLD = 1024; int HEAL_BLESS_DAMAGE_RESIST_FIRE = 2048; int HEAL_BLESS_HASTE = 4096; int HEAL_BLESS_HIT_POINTS = 8192; int HEAL_BLESS_REGENERATE = 16384; int HEAL_BLESS_SPELL_ABSORPTION_ONE = 32768; int HEAL_BLESS_SPELL_ABSORPTION_TWO = 65536; int HEAL_BLESS_SPELL_ABSORPTION_THREE = 131072; // Experience Values int XP_ATTO = 1; int XP_FEMTO = 2; int XP_PICO = 3; int XP_NANO = 4; int XP_MICRO = 5; int XP_MILLI = 6; int XP_CENTI = 7; int XP_DECI = 9; int XP_DECA = 10; int XP_HECTO = 12; int XP_KILO = 13; int XP_MEGA = 15; int XP_GIGA = 17; int XP_TERA = 20; int XP_PETA = 25; int XP_EXA = 30; int XP_ULTRA = 40; // Item Category Values int MAX_AMMO_LOW = 12; int MAX_AMMO_MEDIUM = 16; int MAX_AMMO_HIGH = 22; int MAX_ARMOR_LOW = 87; int MAX_ARMOR_MEDIUM = 4; int MAX_ARMOR_HIGH = 5; int MAX_BOOKS_LOW = 7; int MAX_BOOKS_MEDIUM = 6; int MAX_BOOKS_HIGH = 1; int MAX_CLOTHING_LOW = 4; int MAX_CLOTHING_MEDIUM = 5; int MAX_CLOTHING_HIGH = 9; int MAX_JEWELRY_LOW = 5; int MAX_JEWELRY_MEDIUM = 6; int MAX_JEWELRY_HIGH = 11; int MAX_MISCELLANEOUS = 10; int MAX_ALCHEM_NOTES = 14; int MAX_POISONS = 53; int MAX_POTIONS_LOW = 28; int MAX_POTIONS_MEDIUM = 10; int MAX_POTIONS_HIGH = 18; int MAX_NWN_POTIONS = 23; int MAX_REAGENTS_LOW = 30; int MAX_REAGENTS_MEDIUM = 12; int MAX_REAGENTS_HIGH = 28; int MAX_RECIPES_LOW = 30; int MAX_RECIPES_MEDIUM = 12; int MAX_RECIPES_HIGH = 28; int MAX_SCROLLS_LOW = 11; int MAX_SCROLLS_MEDIUM = 2; int MAX_SCROLLS_HIGH = 1; int MAX_WEAPONS_LOW = 99; int MAX_WEAPONS_MEDIUM = 34; int MAX_WEAPONS_HIGH = 14; // Object Quality Identifiers float QUALITY_HIGH = 37.0; float QUALITY_LOW = 19.0; float QUALITY_MEDIUM = 26.0; // Object Types int OBJECT_TYPE_BARREL = 1; int OBJECT_TYPE_BOOKS = 2; int OBJECT_TYPE_CHEST = 3; int OBJECT_TYPE_CRATE = 4; int OBJECT_TYPE_GOLD_PILE = 5; int OBJECT_TYPE_LOOTBAG = 6; int OBJECT_TYPE_MISCELLANEOUS = 7; int OBJECT_TYPE_WIZARD_CABINET = 8; // Exchange Rates float EXCHANGE_RATE_COPPER = 100.0; float EXCHANGE_RATE_SILVER = 10.0; float EXCHANGE_RATE_ELECTRUM = 2.0; float EXCHANGE_RATE_GOLD = 1.0; float EXCHANGE_RATE_PLATINUM = 0.2; // Food System int DEBUG_STATE = FALSE; // Switch to FALSE if Debug information is not wanted int TERRAIN_MODIFIER_INFRA = 0; int TERRAIN_MODIFIER_EASY = 1; int TERRAIN_MODIFIER_NORMAL = 2; int TERRAIN_MODIFIER_HARD = 3; int TERRAIN_MODIFIER_ULTRA = 4; int TERRAIN_TYPE_FOREST = 1; int TERRAIN_TYPE_GRASSLAND = 2; int TERRAIN_TYPE_CITY = 3; int TERRAIN_TYPE_CAVERN = 4; int TERRAIN_TYPE_HILLS = 5; int TERRAIN_TYPE_DUNGEON = 6; //////////////////////////////////////////////////////////////////////////////// // Structures ////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Functions List ////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// void ActionForceMoveToRandomLocation(object oSubject, float fXDist, float fYDist, int iRun=TRUE); void AI_Movement_FlockTypeA(int iFlockRoll, float fDist=1.0, object oObject=OBJECT_SELF); void AI_Movement_FlockTypeB(float fMinDist=1.5, float fMaxDist=2.2, int iMoveToGroup=TRUE, object oObject=OBJECT_SELF); void Area_CheckForPlayers(object oArea, float fFrequency=18.0); string Array_AddElement(string strElement, string strArray); string Array_GetElement(int iElement, string strArray); int Array_GetTotalElements(string strArray); void Array_ObjectAddElement(object oObject, string strArrayName, string strElement); string Array_RemoveElement(int iElement, string strArray); int BlockCodeExecution(object oTarget, string strBlockFlag, int iReset=FALSE); int BlockMultiActivation(string strBlockFlag, object oTarget, float fBlockPeriod, string strMessage="Nothing happened!"); void CreateItem(object oPC, string strItemTag, int iGiveBack); object CreateObjectAtDistance(int nObjectType, object oTarget, string strResRef, float fDist); void Debug_Report(string strMessage, object oSelf=OBJECT_SELF); void Doors_ActionCloseDoor(object oDoor); void Doors_ActionSealDoor(object oDoor, int iNoPC=TRUE); void Doors_BashOpenCheck(object oObject=OBJECT_SELF); void Doors_DelayClose(object oDoor, float fDelay); void Doors_DelaySeal(object oDoor, float fDelay); int Doors_DoBashDamage(object oDoor); void Doors_FakeDoorSpeak(object oDoor=OBJECT_SELF); string Effect_AbsorbAbility(object oAbsorber, object oVictim, int iAbility); string Effect_AbsorbHitPoints(object oAbsorber, object oVictim); string Effect_AbsorbRandomAbility(object oAbsorber, object oVictim); string Effect_AbsorbRandomSkill(object oAbsorber, object oVictim); string Effect_AbsorbSpellMemory(object oAbsorber, object oVictim); void Effect_AbsorptionVFX(object oAbsorber, object oVictim); void Effect_DischargeAbsorbedSpell(object oCaster, int iAbortOnUnknown=FALSE); void Effect_DischargeMemorizedSpell(object oCaster, int iSpellID); effect Effect_SetAbilityScore(int iValue, object oTarget, int iAbility); void Encounter_NightEncounterOnEnter(object oEncounter=OBJECT_SELF); void Encounter_NightEncounterOnExit(object oEncounter=OBJECT_SELF); int Experience_GetXPForDC(int iDC); int Experience_RewardByLevel(int iLvls, int iBaseXP); void Experience_RewardNearestByDC(int iDC); void Fire_BurnCreature(object oVictim, int iDamage, int iDamageType=DAMAGE_TYPE_FIRE, int iDC=21); void Fire_DoBurnCheck(int iDamage, int iDamageType=DAMAGE_TYPE_FIRE, int iDC=21, object oFire=OBJECT_SELF, float fDelay=1.0); void Fire_DoRandomMotion(int iHungry=FALSE, object oFire=OBJECT_SELF, float fMaxXRange=4.0, float fMaxYRange=4.0, float fXRange=0.15, float fYRange=0.15); void Fire_Explosion(location lCenter, int iMaxDamage=30, int iMinDamage=6, float fRadius=10.0); void FoodSys_ClearMessages(object oPC); void FoodSys_CommentOnWeather(object oPC, float fApparentTemp); void FoodSys_Drunk_CheckVomit(object oPC, int iMOD, int iBloodAlcohol); void FoodSys_Drunk_EffectDrunk(object oTarget); void FoodSys_Drunk_LowerBloodAlcohol(object oTarget); void FoodSys_Drunk_PlaySoundFemale(object oPC); void FoodSys_Drunk_PlaySoundMale(object oPC); void FoodSys_Drunk_SpeakWarning(object oPC); void FoodSys_Drunk_VomitEffect(object oPC, int iMOD); int FoodSys_GetActiveState(object oArea); int FoodSys_GetAmbientTemperature(object oArea); int FoodSys_GetConAdjPCClothing(object oPC); int FoodSys_GetConAdjSeason(); // Consumption Adjustment for Season int FoodSys_GetConAdjTerrainModifier(object oArea); int FoodSys_GetConAdjTerrainType(object oArea); int FoodSys_GetConAdjTOD(); int FoodSys_GetFoodUse(); int FoodSys_GetHeatRetension(object oPC); int FoodSys_GetMinimumFoodRate(object oArea); int FoodSys_GetStarvationLimit(object oPC); int FoodSys_GetStarveCounter(object oPC); int FoodSys_GetTemperatureFlux(object oArea); int FoodSys_GetTerrainModifier(object oArea); int FoodSys_GetTerrainType(object oArea); void FoodSys_HeartBeat(object oPC, float fHeartBeat); void FoodSys_Initialize(object oTarget, float fHeartBeat=60.0, int iBaseFoodUse=6); void FoodSys_SendMessage(object oPC, string strMessageID, string strMessage); void FoodSys_SetFoodUse(int iBaseFoodUse); void FoodSys_SetStarveCounter(object oPC, int iValue=0); void FoodSys_SetTerrainType(int iTerrainType=3, int iAmbientTemperature=25, int iTerrainModifier=2, int iTemperatureFlux=10, int iMinFoodRate=1, int iActive=TRUE); void FoodSys_ShowTerrainVars(object oPC); int FoodSys_TakeSupplies(object oPC, int iQuantity); int GetHighestLevel(object oSubject); int GetLevel(object oSubject); int GetLowestLevel(object oSubject); int GetMiddleLevel(object oSubject); int GetNumberOfNegativeEffects(object oTarget); int GetSeason(); string GetSeasonString(); int GetTOD(); string GetTODString(); void Heal_BeginHealing(object oPC, int iRemoveEffects = 31, int iAddBlessings = 0, int iGiveHenchPotions = TRUE, string strPotionTag = "NW_IT_MPOTION003", int iHealHench = TRUE, int iHealFamil = TRUE, int iHealOthers = TRUE, int iMinHP = 0, int iMaxHP = 0); void Heal_BlessingEffect(object oPC, int iAddBlessings); void Heal_RestoreEffect(object oTarget, int iRemoveEffects, int iMinHP, int iMaxHP, int iDoHeal=TRUE); int Inventory_CountItem(object oTarget, string strItemTag); void Inventory_DestroyAllItems(object oTarget=OBJECT_SELF); int Inventory_ItemCount(object oTarget); void Inventory_RemoveItem(object oTarget, string strItemTag); int Inventory_RemoveItemNumber(object oTarget, string strItemTag, int iTake=1); void Inventory_RemoveStackedItemQuantity(object oTarget, string strItemTag, int iRemoveNum); void Lightning_DamageCreatures(location lStrike, int iDifficulty=15, float fRange=10.0, int iMaxDamage=26, int iMinDamage=6); void Lightning_DivineFury(object oObject, float fRadius, int iTotalStrikes, int iDoesDamage=FALSE, float fDelayWindow=14.0); void Lightning_DoStrike(int iLightning, object oSelf); void Lightning_Initialize(object oSelf, int iXSize, int iYSize, int iLightningDC=15, float fLightningRange=6.0, int iLightningRandFreq=20, int iLightningConstFreq=9); location Location_AddVectors(vector vCurrentPosition, float fVectorX, float fVectorY, float fVectorZ, object oObject=OBJECT_SELF); location Location_GetLocationNearObject(object oObject, float fXRange=1.0, float fYRange=1.0); location Location_GetLocationNearWaypoint(string strObjectTag, float fXRange=1.0, float fYRange=1.0); location Location_GetRandomLocation(object oArea, int iXSize, int iYSize, int iXMargin=0, int iYMargin=0, int iHeight=0); float Math_FitFloatToBoundaries(float fValue, float fPoint, float fRange); float Math_RandomFloat(float fMaxValue); float Math_RandomFloatSign(); int Math_RandomIntSign(); void Object_AddProperty(int iEffect, int iParamA=1, int iParamB=4, int iParamC=1, object oObject=OBJECT_SELF); void Object_CheckDamageVsResistances(int iDown, int iSpecDam, int iDReduction, float fDur, int iResistances, object oSelf=OBJECT_SELF); int Object_CheckInventoryForItem(string strResource, object oObject); int Object_CountSameAtLocation(object oObject, location lCenter, float fSize=5.0, int iShape=SHAPE_SPHERE); void Object_CreateObject(int nObjectType, string sTemplate, location lLocation, int bUseAppearAnimation=FALSE); void Object_EnableRegenerators(object oObject=OBJECT_SELF); void Object_ForceEndCombat(object oAttacker); void Object_InitializeInventory(string strItemTag, int iStack=4, object oObject=OBJECT_SELF); int Object_IsClassAndRace(int iClassType, object oObject=OBJECT_SELF, int iExclusive=FALSE, string strName=""); int Object_IsPCNear(object oObject=OBJECT_SELF, float fDist=15.0); int Object_IsPrey(object oObject=OBJECT_SELF); object Object_GetClosestSameInLocationShape(location lCenter, object oObject=OBJECT_SELF, float fSize=5.0, int iShape=SHAPE_SPHERE); vector Object_GetDensityCenter(location lCenter, object oObject=OBJECT_SELF, float fSize=5.0, int iShape=SHAPE_SPHERE); int Object_GetDifficulty(object oObject, int iReturnRaw=FALSE); object Object_GetNearestSame(object oObject=OBJECT_SELF, float fDistance=15.0); int Object_GetType(object oObject); void Object_GiveUniqueAbilities(object oObject=OBJECT_SELF); void Object_MakeAnimalsCommon(object oObject=OBJECT_SELF); void Object_RespawnContents(string strResource, int iStack=1, object oObject=OBJECT_SELF); void Object_RespawnSpecialInventory(object oObject, string strInventoryArray); int Object_SelectMemorizedSpell(object oCaster); void Object_SpawnMonsterFromPC(object oPlayer); string Object_StoreSpecialInventory(object oObject=OBJECT_SELF); void Placeable_Altar_ShrineOfPtah(object oCloser, object oObject=OBJECT_SELF); int Placeable_Lever_GetState(object oLever); int Placeable_Lever_SwitchState(object oLever); void Player_HeartBeat_Raise(object oPC, float fHeartBeat=6.0, int iReviveDC = 25); void Player_HeartBeat_Revive(object oPC, float fHeartBeat=6.0, int iReviveDC = 25); void Player_RestoreToLife(object oPC); string Recall_GetDefaultPortalTag(); string Recall_GetPortalTag(object oArea=OBJECT_SELF); void Recall_InitializeArea(string strRecallPortal, string strDefaultPortal="NULL", object oArea=OBJECT_SELF); int Recall_IsDefaultPortalActive(); int Recall_IsDefaultPortalBlocked(object oArea=OBJECT_SELF); int Recall_IsAreaActive(object oArea=OBJECT_SELF); int Recall_IsRecall(); void Recall_SetAreaBlocked(int iBlockValue=TRUE, object oArea=OBJECT_SELF); void Recall_SetDefaultPortal(string strDefaultPortal); void Respawn_ByResrefWithDelay(object oObject, float fDelay, string strInventoryArray=""); void Respawn_DoRespawn(int iType, string strResRef, location lLoc, string strInventoryArray=""); int RollChance(int iPercent); int Spell_IsOffensive(int iSpellID); string String_AddDigits(string strItemTag, int iValue, int iDigits=2); string String_GetAbilityText(int iAbility); int String_IsSubString(string strOriginal, string strFindThis); void Sunlight_CheckForSunriseInArea(object oArea, float fHeartBeat=18.0); void Sunlight_CheckForSunlightOnUndead(object oUndead); void Sunlight_DamageUndead(object oUndead, int iMaxDamage=12, int iMinDamage=6, float fFrequency=2.0, int iRepeats=20); void Sunlight_DestroyUndead(object oUndead); void Sunlight_DestroyUndeadInArea(object oArea); void TorchLight_Heartbeat(object oPC, float fHeartBeat, float fBurnLimit, float fBurnRate); void TorchLight_Initialize(object oPC, float fHeartBeat=6.0, float fBurnLimit=2880.0, float fBurnRate=6.0); int TorchLight_IsHoldingTorch(object oPC); int Treasure_Death_CreateMeat(int iProb=45, int iMax=3); int Treasure_Death_DecideOnBodyParts(int nPercentChance, int nQuant, string strTemplate, object oMonster); void Treasure_Death_DecideOnMeat(object oMonster); void Treasure_Death_PlaceBodyParts(object oMonster=OBJECT_SELF); int Treasure_ExchangeFundsForGold(object oPC, object oItem, float fExchangeRate); void Treasure_GenerateBook(object oContainer, object oLastOpener, int iQuality); void Treasure_GenerateCustomTreasure(object oContainer, int iWealth=0); object Treasure_GenerateMiscItem(object oContainer); void Treasure_GenerateNWNBook(object oContainer, object oLastOpener); object Treasure_GeneratePoisonPotion(object oContainer); void Treasure_GeneratePotion(object oContainer, object oAdventurer, int iQuality); void Treasure_GenerateReagent(object oContainer, int iQuality); void Treasure_GenerateTreasure(object oObject, object oLastOpener, int iQuality, int iType); void Treasure_GenerateValuable(object oContainer, object oAdventurer, int iQuality); void Treasure_GenerateWizardSupplies(object oContainer, object oAdventurer, int iQuality); void Treasure_GoldForClassAdjust(object oTarget); int Treasure_RollCoins(object oPC, object oItem, float fExchangeRate); void Treasure_StockContainerByDCAndRespawn(object oObject); void Weapon_FireBallista(object oWeapon, object oPC, int iPower=6); //////////////////////////////////////////////////////////////////////////////// // Function Definitions //////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Forces the the action subject to run to a random location, the distance of which is // specified by the fXDist and fYDist parameters. The object will not move if the location // generated is invalid or can't be reached. void ActionForceMoveToRandomLocation(object oSubject, float fXDist, float fYDist, int iRun=TRUE) { vector vCurrentPosition = GetPosition(oSubject); int iDMOD = GetAbilityModifier(ABILITY_DEXTERITY, oSubject); int iCMOD = GetAbilityModifier(ABILITY_CONSTITUTION, oSubject); float fRunDistanceX = fXDist + IntToFloat(Random(iDMOD)) + IntToFloat(Random(iCMOD)); float fRunDistanceY = fYDist + IntToFloat(Random(iDMOD)) + IntToFloat(Random(iCMOD)); float fDirectionX = 1.0; float fDirectionY = 1.0; if (Random(100) < 50) fDirectionX = -1.0; if (Random(100) < 50) fDirectionY = -1.0; float fVX = vCurrentPosition.x + (fDirectionX * fRunDistanceX); float fVY = vCurrentPosition.y + (fDirectionY * fRunDistanceY); vector vNewPosition = Vector(fVX, fVY, vCurrentPosition.z); location lNewLocation = Location(GetArea(oSubject), vNewPosition, IntToFloat(Random(180))); AssignCommand(oSubject, ClearAllActions()); AssignCommand(oSubject, ActionForceMoveToLocation(lNewLocation, iRun)); } // Flocking algorithm that clusters creatures of the same species. Cohesion of the cluster is // determined by the Charisma and Wisdom of the animals involved. void AI_Movement_FlockTypeA(int iFlockRoll, float fDist=1.0, object oObject=OBJECT_SELF) { // see if we flock to others object oSame = Object_GetNearestSame(); if (GetIsObjectValid(oSame)) { float fDist = GetDistanceToObject(oSame); int iABILS = GetAbilityScore(OBJECT_SELF, ABILITY_CHARISMA) + GetAbilityScore(OBJECT_SELF, ABILITY_WISDOM); if (iFlockRoll <= iABILS) { ActionForceMoveToObject(oSame, FALSE, fDist); } } } // Keeps creatures a minimum of fMinDist apart while moving them to the highest concentration of nearby // similair creatures void AI_Movement_FlockTypeB(float fMinDist=1.5, float fMaxDist=2.2, int iMoveToGroup=TRUE, object oObject=OBJECT_SELF) { // Keep a minimum distance from similair objects object oNearest = Object_GetNearestSame(); if (GetDistanceBetween(oNearest, oObject) < fMinDist) { ActionMoveAwayFromObject(oNearest, FALSE, fMaxDist); return; } // Move toward the highest concentration of similair objects vector vPosition = Object_GetDensityCenter(GetLocation(oObject), oObject, 15.0); location lNewLocation = Location(GetArea(oObject), vPosition, IntToFloat(Random(180))); ActionForceMoveToLocation(lNewLocation, Random(2)); return; } // This function is called from the Master OnEnter file for a given area. It schedules itself // to run every 18 seconds (by default) and examines the area for PC's who are NOT DM's - and // sets a variable ("iPlayerPresent") on the area to TRUE if PC's were found. Once a PC is found, // the function aborts searching through objects to save some effort. void Area_CheckForPlayers(object oArea, float fFrequency=18.0) { int iPlayerPresent = FALSE; object oObject = GetFirstObjectInArea(oArea); // Search for a PC, exclude the DM while(GetIsObjectValid(oObject)) { if (GetIsPC(oObject) && !GetIsDM(oObject)) { iPlayerPresent = TRUE; break; } oObject = GetNextObjectInArea(oArea); } // Set a variable on the area indicating if a Player was found SetLocalInt(oArea, "iPlayerPresent", iPlayerPresent); // Continue checking AssignCommand(oArea, DelayCommand(fFrequency, Area_CheckForPlayers(oArea, fFrequency))); } // Add an element! string Array_AddElement(string strElement, string strArray) { string strWork = strArray + strElement + "|"; return strWork; } // Access the element number passed string Array_GetElement(int iElement, string strArray) { int iDelimeter; int iLength; string strWork; int iIndex; // Locate element for (iIndex=0; iIndex 0) { iCount++; iDelimeter = FindSubString(strArray, "|"); iLength = GetStringLength(strArray); strWork = GetSubString(strArray, iDelimeter+1, iLength-iDelimeter+1); strArray = strWork; } return iCount; } void Array_ObjectAddElement(object oObject, string strArrayName, string strElement) { string strArray = GetLocalString(oObject, strArrayName); strArray = Array_AddElement(strElement, strArray); SetLocalString(oObject, strArrayName, strArray); } string Array_RemoveElement(int iElement, string strArray) { int iIndex; string strFinished; int iTotalElements = Array_GetTotalElements(strArray); // Copy all elements up to the element we want for (iIndex=0; iIndex= 360.0) fFacing = 720.0 - fFacing; // Correct for the GetFacing bug if (fFacing < 0.0) fFacing += (360.0); vector vCreate = AngleToVector(fFacing); // Create a vector from the angle the PC is facing vCreate = VectorNormalize(vCreate); // Normalize the vector to a unit vector vector vPCPos = GetPosition(oTarget); // Get the vector position of the PC vCreate.x = (vCreate.x * fDist) + vPCPos.x; // Create the vector position for the new object vCreate.y = (vCreate.y * fDist) + vPCPos.y; vCreate.z = vPCPos.z; location lCreate = Location(GetArea(oTarget), vCreate, fFacing); // Create a location for the new object object oReturn = CreateObject(nObjectType, strResRef, lCreate); // Create the object return oReturn; } // Output a string to the nearest PC of the object executing the function void Debug_Report(string strMessage, object oSelf=OBJECT_SELF) { // NOT sure if it works!!! /* string strReport = GetTag(oSelf); strReport += ": " + strMessage; object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); SendMessageToPC(oPC, strReport); */ } // Performs the close door action, but only if no PC is nearby. void Doors_ActionCloseDoor(object oDoor) { if (!Object_IsPCNear(oDoor)) ActionCloseDoor(oDoor); else AssignCommand(oDoor, DelayCommand(20.0, Doors_ActionCloseDoor(oDoor))); } // Performs the seal door action, but only if no PC is nearby (when iNoPC is TRUE). If iNoPC is // FALSE, then the door will close automatically. void Doors_ActionSealDoor(object oDoor, int iNoPC=TRUE) { if (iNoPC) { if (!Object_IsPCNear(oDoor)) { ActionCloseDoor(oDoor); SetLocked(oDoor, TRUE); } else AssignCommand(oDoor, DelayCommand(30.0, Doors_ActionCloseDoor(oDoor))); } else { ActionCloseDoor(oDoor); SetLocked(oDoor, TRUE); } } // Checks too see if damage done to the door causes it too open. Uses the objects // will save to determine the number of 'Hitpoints' of damage that can be taken before // opening. void Doors_BashOpenCheck(object oDoor=OBJECT_SELF) { int iFortitude = GetFortitudeSavingThrow(oDoor); // It gets a save for half damage int iHardness = GetReflexSavingThrow(oDoor); // Use the Reflex Save as the 'Hardness' of the door! int iOpenDamage = GetWillSavingThrow(oDoor); // Max damage door can take before opening int iDoorDamage = GetLocalInt(oDoor, "iDoorDamage"); int iBash = Doors_DoBashDamage(oDoor) - iHardness; if (iBash < 0) iBash = 0; object oAttacker = GetLastAttacker(oDoor); // Check if the door only takes half damage int iDC = GetHighestLevel(oAttacker) + GetAbilityScore(oAttacker, ABILITY_STRENGTH) + GetAbilityModifier(ABILITY_STRENGTH, oAttacker); int iRoll = d20() + iFortitude; if (iRoll >= iDC) iBash /= 2; // Success int iTotalDamage = iDoorDamage + iBash; // Float the damage on the PC string strBashText = "I did " + IntToString(iBash) + " damage!"; FloatingTextStringOnCreature(strBashText, oAttacker); // Check if the door actually opens if (iTotalDamage >= iOpenDamage) { // Effects effect eFX2 = EffectVisualEffect(VFX_FNF_SCREEN_BUMP); effect eFX3 = EffectVisualEffect(VFX_COM_SPARKS_PARRY); int iSound = Random(3) + 1; string strSound = "as_cv_woodbreak" + IntToString(iSound); PlaySound(strSound); DelayCommand(0.3, ApplyEffectToObject(DURATION_TYPE_INSTANT, eFX3, oDoor)); DelayCommand(0.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, eFX2, oDoor)); // Stop the PC from attacking Object_ForceEndCombat(oAttacker); // Open the door ActionUnlockObject(oDoor); ActionOpenDoor(oDoor); iTotalDamage = 0; // Reset damage counter // Set the door to close and lock again after a delay Doors_DelaySeal(oDoor, 600.0 + IntToFloat(Random(300))); // 10.0 } SetLocalInt(oDoor, "iDoorDamage", iTotalDamage); } // Closes a door object on a delay void Doors_DelayClose(object oDoor, float fDelay) { DelayCommand(fDelay, Doors_ActionCloseDoor(oDoor)); } // Closes and Locks a door object on a delay void Doors_DelaySeal(object oDoor, float fDelay) { DelayCommand(fDelay, Doors_ActionSealDoor(oDoor)); } // Calculate an amount of damage that the Attack of the Door is likely to do int Doors_DoBashDamage(object oDoor){ object oAttacker = GetLastAttacker(); object oWeapon = GetLastWeaponUsed(oAttacker); int iLVL = GetLevel(oAttacker); int iSTR = GetAbilityScore(oAttacker, ABILITY_STRENGTH); int iSTRMod = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); int iBaseItem = GetBaseItemType(oWeapon); int iDamage; int iDamageModifier; if (iLVL < 9) iDamageModifier = 1; else if (iLVL < 16) iDamageModifier = 2; else if (iLVL < 21) iDamageModifier = 3; if (iBaseItem == BASE_ITEM_BASTARDSWORD) iDamage = d12(iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_BATTLEAXE) iDamage = d10(iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_DOUBLEAXE) iDamage = d6(2+iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_GREATAXE) iDamage = d6(2+iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_GREATSWORD) iDamage = d6(2+iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_HALBERD) iDamage = d12(iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_HEAVYFLAIL) iDamage = d10(iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_WARHAMMER) iDamage = d10(iDamageModifier) + Random(iSTR); else if (iBaseItem == BASE_ITEM_LONGBOW) iDamage = d6(iDamageModifier); else if (iBaseItem == BASE_ITEM_LIGHTCROSSBOW) iDamage = d4(iDamageModifier); else if (iBaseItem == BASE_ITEM_SHORTBOW) iDamage = d4(iDamageModifier); else if (iBaseItem == BASE_ITEM_HEAVYCROSSBOW) iDamage = d6(iDamageModifier); else if (iBaseItem == BASE_ITEM_SHURIKEN) iDamage = d3(iDamageModifier); else if (iBaseItem == BASE_ITEM_INVALID || iBaseItem == BASE_ITEM_GLOVES || iBaseItem == BASE_ITEM_BRACER) iDamage = d4(); else iDamage = d4(iDamageModifier) + Random(iSTR); // Calculate final damage iDamage += iSTRMod; return(iDamage); } // This function void Doors_FakeDoorSpeak(object oDoor=OBJECT_SELF){ int iRand = Random(7); string strFakeSpeak; if (iRand == 0) strFakeSpeak = "The door appears jammed - perhaps there's another way in."; if (iRand == 1) strFakeSpeak = "The door is stuck."; if (iRand == 2) strFakeSpeak = "The door seems blocked from the other side."; if (iRand == 3) strFakeSpeak = "It just will not budge!"; if (iRand == 4) strFakeSpeak = "Something tightly blocks the doorway from the other side."; if (iRand == 5) strFakeSpeak = "The door appears barred from within."; if (iRand == 6) strFakeSpeak = "The door is heavily damaged and will not open."; if (iRand == 7) strFakeSpeak = "A baracade has been constructed on the inside. There doesn't appear to be anyway in..."; AssignCommand(oDoor, SpeakString(strFakeSpeak)); } string Effect_AbsorbAbility(object oAbsorber, object oVictim, int iAbility) { string strAbility = String_GetAbilityText(iAbility); string strReturn; // How much? int iDrain = d3(); // Only do the drain if the ability selected can support it... int iScore = GetAbilityScore(oVictim, iAbility); int iDiff = iScore - 3; if (iDiff == 0) return ""; if (iDrain > iDiff) iDrain = iDiff; // Absorb the Ability effect eDrain = SupernaturalEffect(EffectAbilityDecrease(iAbility, iDrain)); effect eGain = SupernaturalEffect(EffectAbilityIncrease(iAbility, iDrain)); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDrain, oVictim); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGain, oAbsorber); Effect_AbsorptionVFX(oAbsorber, oVictim); // Return string indicating result strReturn = "absorbed " + IntToString(iDrain) + " points of " + strAbility + " from " + GetName(oVictim); return strReturn; } string Effect_AbsorbHitPoints(object oAbsorber, object oVictim) { // How many hitpoints? int iCHP = GetCurrentHitPoints(oVictim); int iHP = d3(GetHitDice(oVictim)); if (iHP >= iCHP) iHP = iCHP / 2; // Only take MAX 50% of the PC's HP!!! if (iHP < 1) return ""; // Absorb the Hitpoints effect eDrain = EffectDamage(iHP, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_PLUS_FIVE); effect eGain = EffectTemporaryHitpoints(iHP); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDrain, oVictim); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGain, oAbsorber); Effect_AbsorptionVFX(oAbsorber, oVictim); // Return string indicating result string strReturn = "absorbed " + IntToString(iHP) + " points of life from " + GetName(oVictim); return strReturn; } string Effect_AbsorbRandomAbility(object oAbsorber, object oVictim) { // What ability? int iAbility; int iRand = Random(6); string strAbility; string strReturn; if (iRand == 0) strReturn = Effect_AbsorbAbility(oAbsorber, oVictim, ABILITY_CHARISMA); if (iRand == 1) strReturn = Effect_AbsorbAbility(oAbsorber, oVictim, ABILITY_CONSTITUTION); if (iRand == 2) strReturn = Effect_AbsorbAbility(oAbsorber, oVictim, ABILITY_DEXTERITY); if (iRand == 3) strReturn = Effect_AbsorbAbility(oAbsorber, oVictim, ABILITY_INTELLIGENCE); if (iRand == 4) strReturn = Effect_AbsorbAbility(oAbsorber, oVictim, ABILITY_STRENGTH); if (iRand == 5) strReturn = Effect_AbsorbAbility(oAbsorber, oVictim, ABILITY_WISDOM); return strReturn; } string Effect_AbsorbRandomSavingThrow(object oAbsorber, object oVictim) { // What saving throw? int iRand = Random(19); int iSaveType; string strSaveType; if (iRand == 0) { strSaveType = "save vs acids"; iSaveType = SAVING_THROW_TYPE_ACID; } if (iRand == 1) { strSaveType = "save vs chaos"; iSaveType = SAVING_THROW_TYPE_CHAOS; } if (iRand == 2) { strSaveType = "save vs cold"; iSaveType = SAVING_THROW_TYPE_COLD; } if (iRand == 3) { strSaveType = "save vs death"; iSaveType = SAVING_THROW_TYPE_DEATH; } if (iRand == 4) { strSaveType = "save vs diseases"; iSaveType = SAVING_THROW_TYPE_DISEASE; } if (iRand == 5) { strSaveType = "save vs divine"; iSaveType = SAVING_THROW_TYPE_DIVINE; } if (iRand == 6) { strSaveType = "save vs electricity"; iSaveType = SAVING_THROW_TYPE_ELECTRICITY; } if (iRand == 7) { strSaveType = "save vs evils"; iSaveType = SAVING_THROW_TYPE_EVIL; } if (iRand == 8) { strSaveType = "save vs fear"; iSaveType = SAVING_THROW_TYPE_FEAR; } if (iRand == 9) { strSaveType = "save vs fire"; iSaveType = SAVING_THROW_TYPE_FIRE; } if (iRand == 10) { strSaveType = "save vs good"; iSaveType = SAVING_THROW_TYPE_GOOD; } if (iRand == 11) { strSaveType = "save vs law"; iSaveType = SAVING_THROW_TYPE_LAW; } if (iRand == 12) { strSaveType = "save vs mind spells"; iSaveType = SAVING_THROW_TYPE_MIND_SPELLS; } if (iRand == 13) { strSaveType = "save vs negative"; iSaveType = SAVING_THROW_TYPE_NEGATIVE; } if (iRand == 14) { strSaveType = "save vs poisons"; iSaveType = SAVING_THROW_TYPE_POISON; } if (iRand == 15) { strSaveType = "save vs positive"; iSaveType = SAVING_THROW_TYPE_POSITIVE; } if (iRand == 16) { strSaveType = "save vs sonics"; iSaveType = SAVING_THROW_TYPE_SONIC; } if (iRand == 17) { strSaveType = "save vs spells"; iSaveType = SAVING_THROW_TYPE_SPELL; } if (iRand == 18) { strSaveType = "save vs traps"; iSaveType = SAVING_THROW_TYPE_TRAP; } // Absorb the save int iDrain = d3(); effect eDrain = SupernaturalEffect(EffectSavingThrowDecrease(SAVING_THROW_ALL, iDrain, iSaveType)); effect eGain = SupernaturalEffect(EffectSavingThrowIncrease(SAVING_THROW_ALL, iDrain, iSaveType)); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDrain, oVictim); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGain, oAbsorber); Effect_AbsorptionVFX(oAbsorber, oVictim); // Return string indicating result string strReturn = "absorbed " + IntToString(iDrain) + " points of " + strSaveType + " from " + GetName(oVictim); return strReturn; } string Effect_AbsorbRandomSkill(object oAbsorber, object oVictim) { // What Skill? (Attempt the drain whether the PC has the skill or not) int iSkill; int iRand = Random(20); string strSkill; if (iRand == 0) { iSkill = SKILL_ANIMAL_EMPATHY; strSkill = "Animal Empathy"; } if (iRand == 1) { iSkill = SKILL_CONCENTRATION; strSkill = "Concentration"; } if (iRand == 2) { iSkill = SKILL_DISABLE_TRAP; strSkill = "Disabling Traps"; } if (iRand == 3) { iSkill = SKILL_DISCIPLINE; strSkill = "Discipline"; } if (iRand == 4) { iSkill = SKILL_HEAL; strSkill = "Healing"; } if (iRand == 5) { iSkill = SKILL_HIDE; strSkill = "Hiding"; } if (iRand == 6) { iSkill = SKILL_LISTEN; strSkill = "Listening"; } if (iRand == 7) { iSkill = SKILL_LORE; strSkill = "Lore"; } if (iRand == 8) { iSkill = SKILL_MOVE_SILENTLY; strSkill = "Moving Silently"; } if (iRand == 9) { iSkill = SKILL_OPEN_LOCK; strSkill = "Opening Locks"; } if (iRand == 10) { iSkill = SKILL_PARRY; strSkill = "Parry"; } if (iRand == 11) { iSkill = SKILL_PERFORM; strSkill = "Performing"; } if (iRand == 12) { iSkill = SKILL_PERSUADE; strSkill = "Persuading"; } if (iRand == 13) { iSkill = SKILL_PICK_POCKET; strSkill = "Picking Pockets"; } if (iRand == 14) { iSkill = SKILL_SEARCH; strSkill = "Searching"; } if (iRand == 15) { iSkill = SKILL_SET_TRAP; strSkill = "Setting Traps"; } if (iRand == 16) { iSkill = SKILL_SPELLCRAFT; strSkill = "Spellcrafting"; } if (iRand == 17) { iSkill = SKILL_SPOT; strSkill = "Spotting"; } if (iRand == 18) { iSkill = SKILL_TAUNT; strSkill = "Taunting"; } if (iRand == 19) { iSkill = SKILL_USE_MAGIC_DEVICE; strSkill = "Using Magical Devices"; } // Abort if the PC doesn't have the selected skill if (GetSkillRank(iSkill, oVictim) < 1) return ""; // How much? int iDrain = d2(3); // Proceed with the absorption!!!!! effect eDrain = SupernaturalEffect(EffectSkillDecrease(iSkill, iDrain)); effect eGain = SupernaturalEffect(EffectSkillIncrease(iSkill, iDrain)); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDrain, oVictim); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGain, oAbsorber); Effect_AbsorptionVFX(oAbsorber, oVictim); // Return string indicating result string strReturn = "absorbed " + IntToString(iDrain) + " points of " + strSkill + " from " + GetName(oVictim); return strReturn; } // Allows the creature to 'take' memorized spells from PC's. Places the IDs for stolen spells // into an array for later access. string Effect_AbsorbSpellMemory(object oAbsorber, object oVictim) { // Select a spell that the PC has int iSpellID = Object_SelectMemorizedSpell(oVictim); // Cause the PC to discharge it Effect_DischargeMemorizedSpell(oVictim, iSpellID); // Add the Spell ID to memory array for later access Array_ObjectAddElement(oAbsorber, "strAbsorbedSpells", IntToString(iSpellID)); // Do the FX Effect_AbsorptionVFX(oAbsorber, oVictim); // Return string indicating result string strReturn = "absorbed spell memories from " + GetName(oVictim); return strReturn; } void Effect_AbsorptionVFX(object oAbsorber, object oVictim) { effect eVFX1 = EffectVisualEffect(VFX_IMP_HEAD_ELECTRICITY); effect eVFX2 = EffectVisualEffect(VFX_COM_HIT_ELECTRICAL); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX1, oAbsorber); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX2, oVictim); } // Causes the target object to randomly select and cast a spell that they've absorbed. void Effect_DischargeAbsorbedSpell(object oCaster, int iAbortOnUnknown=FALSE) { // Select a spell to cast string strAbsorbedSpells = GetLocalString(oCaster, "strAbsorbedSpells"); int iTotalAbsorbed = Array_GetTotalElements(strAbsorbedSpells); int iElement = Random(iTotalAbsorbed); int iSpellID = StringToInt(Array_GetElement(iElement, strAbsorbedSpells)); strAbsorbedSpells = Array_RemoveElement(iElement, strAbsorbedSpells); // Remove the element SetLocalString(oCaster, "strAbsorbedSpells", strAbsorbedSpells); // Determine if the spell is defensive or offensive int iSpellType = Spell_IsOffensive(iSpellID); // Returns TRUE if the spell is used to attack with if ((iSpellType == -1) && iAbortOnUnknown) { return; // Could not identify the spell, just skip it and return. } // If spell was offensive then get last attack target and FIRE! Otherwise, cast at self. if (iSpellType) { // Offensive object oLastAttacker = GetLastAttacker(oCaster); AssignCommand(oCaster, ClearAllActions(TRUE)); AssignCommand(oCaster, ActionCastSpellAtLocation(iSpellID, GetLocation(oLastAttacker), METAMAGIC_ANY, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); } else { // Defensive AssignCommand(oCaster, ClearAllActions(TRUE)); AssignCommand(oCaster, ActionCastSpellAtLocation(iSpellID, GetLocation(oCaster), METAMAGIC_ANY, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); } } // Causes the target of the function to cast the spell specified, effectively discharging it. void Effect_DischargeMemorizedSpell(object oCaster, int iSpellID) { // Get a location away from the caster... location lLoc = GetLocation(oCaster); vector vPos = GetPositionFromLocation(lLoc); float fX = vPos.x; float fY = vPos.y; fX += Math_RandomFloatSign() * (5.0 + Math_RandomFloat(8.0)); fY += Math_RandomFloatSign() * (5.0 + Math_RandomFloat(8.0)); vPos.x = fX; vPos.y = fY; lLoc = Location(GetArea(oCaster), vPos, 0.0); // Cast the spell off AssignCommand(oCaster, ActionCastSpellAtLocation(iSpellID, lLoc, METAMAGIC_ANY, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); } // Used to set an ability score to a particular value, rather than boosting it up X points. effect Effect_SetAbilityScore(int iValue, object oTarget, int iAbility) { int iMOD = iValue - GetAbilityScore(oTarget, iAbility); if (iMOD < 0) { iMOD = 0; FloatingTextStringOnCreature("Nothing happened!!!", oTarget); } effect eReal = EffectAbilityIncrease(iAbility, iMOD); return(eReal); } // Placed in the master encounters OnEnter script for an encounter if that encounter // is meant to be active only at night. Be sure the encounter tag is UNIQUE and that // the counter is initially set to inactive (advanced tab) and Continuous. void Encounter_NightEncounterOnEnter(object oEncounter=OBJECT_SELF) { object oPC = GetEnteringObject(); int nEntries = GetLocalInt(oEncounter, "nEntries"); int nResetValue = GetLocalInt(oEncounter, "nResetValue"); if (GetIsPC(oPC) && GetIsNight()) { if (nEntries == 0) { SetEncounterActive(TRUE); nResetValue = 2 + Random(2); SetLocalInt(oEncounter, "nResetValue", nResetValue); } if (nEntries == nResetValue) nEntries = -1; SetLocalInt(oEncounter, "nEntries", nEntries); } } // Placed in the master encounters OnExit script for an encounter if that encounter // is meant to be active only at night. Be sure the encounter tag is UNIQUE and that // the counter is initially set to inactive (advanced tab) and Continuous. void Encounter_NightEncounterOnExit(object oEncounter=OBJECT_SELF) { object oPC = GetExitingObject(); int nEntries = GetLocalInt(oEncounter, "nEntries"); if (GetIsPC(oPC)) { nEntries++; SetLocalInt(oEncounter, "nEntries", nEntries); SetEncounterActive(FALSE); } } // Returns an XP_* constant based on the magnitude of the DC value that was passed in int Experience_GetXPForDC(int iDC) { if (iDC >= 0 && iDC < 4) return (XP_ATTO); if (iDC >= 4 && iDC < 6) return (XP_FEMTO); if (iDC >= 6 && iDC < 8) return (XP_PICO); if (iDC >= 8 && iDC < 11) return (XP_NANO); if (iDC >= 11 && iDC < 13) return (XP_MICRO); if (iDC >= 13 && iDC < 15) return (XP_MILLI); if (iDC >= 15 && iDC < 16) return (XP_CENTI); if (iDC >= 16 && iDC < 17) return (XP_DECI); if (iDC >= 18 && iDC < 20) return (XP_DECA); if (iDC >= 20 && iDC < 22) return (XP_HECTO); if (iDC >= 22 && iDC < 25) return (XP_KILO); if (iDC >= 25 && iDC < 28) return (XP_MEGA); if (iDC >= 28 && iDC < 32) return (XP_GIGA); if (iDC >= 32) return (XP_TERA); return 1; } // Lower level characters get more XP; returns the XP value for a PC of a certain // level trying to disarm a trap of a certain difficulty int Experience_RewardByLevel(int iLvls, int iBaseXP) { int iReward = 15 - iLvls; if (iReward <= 0) return iBaseXP; else iReward *= iBaseXP; if (iReward >= (iBaseXP * 3)) iReward = iBaseXP * 3; return (iReward); } // Calls Experience_RewardByLevel to determine the amount of XP (from the iXPValue) to award void Experience_RewardNearestByDC(int iDC) { object oTarget = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); int iDCXPValue = Experience_GetXPForDC(iDC); int iLevels = GetLevel(oTarget); GiveXPToCreature(oTarget, Experience_RewardByLevel(iLevels, iDC)); } // Burns the creature specified with the damage & damage type passed. Applies a saving throw for half damage. void Fire_BurnCreature(object oVictim, int iDamage, int iDamageType=DAMAGE_TYPE_FIRE, int iDC=21) { // Do a saving throw int iFortSave = GetFortitudeSavingThrow(oVictim) + d20(); if (iFortSave > iDC) { SendMessageToPC(oVictim, "Save (roll: " + IntToString(iFortSave) + ") vs. Flame (" + IntToString(iDC) + ") *success*"); iDamage /= 2; // Half Damage } else SendMessageToPC(oVictim, "Save (roll: " + IntToString(iFortSave) + ") vs. Flame (" + IntToString(iDC) + ") *failure*"); // Play audio if (GetIsPC(oVictim)) { if (!BlockMultiActivation("iBurnScream", oVictim, 7.0, "")) { if (GENDER_FEMALE == GetGender(oVictim)) AssignCommand(oVictim, PlaySound("as_pl_screamf1")); if (GENDER_MALE == GetGender(oVictim)) AssignCommand(oVictim, PlaySound("as_pl_screamm1")); } } // Apply Damage effect eFX = EffectDamage(iDamage, iDamageType, DAMAGE_POWER_ENERGY); ApplyEffectToObject(DURATION_TYPE_INSTANT, eFX, oVictim); // Output text FloatingTextStringOnCreature("Ouch!!", oVictim); } // Checks too see if any creatures are too close and applies fire damage if so. Recurses into itself // to continue checking. void Fire_DoBurnCheck(int iDamage, int iDamageType=DAMAGE_TYPE_FIRE, int iDC=21, object oFire=OBJECT_SELF, float fDelay=1.0) { // Check for nearby creatures object oCreature = GetFirstObjectInShape(SHAPE_SPHERE, 0.50, GetLocation(oFire)); while (GetIsObjectValid(oCreature)) { // Burn the creature Fire_BurnCreature(oCreature, iDamage, iDamageType, iDC); // Get next creature oCreature = GetNextObjectInShape(SHAPE_SPHERE, 0.50, GetLocation(oFire)); } // Recurse AssignCommand(oFire, DelayCommand(fDelay, Fire_DoBurnCheck(iDamage, iDamageType, iDC, oFire))); } // Moves the flame. Don't move too far from the closest "FireBowl" audio object. If iHungry is // true, the flame will chase after nearby creatures. void Fire_DoRandomMotion(int iHungry=FALSE, object oFire=OBJECT_SELF, float fMaxXRange=4.0, float fMaxYRange=4.0, float fXRange=0.20, float fYRange=0.20) { float fXPos; float fYPos; object oAudio = GetNearestObjectByTag("FireBowl"); vector vLimit = GetPosition(oAudio); vector vPosition = GetPosition(oFire); if (!GetIsObjectValid(oAudio)) vLimit = vPosition; // Is the flame hungry? if (iHungry) { // Calculate new fire position that's closer to creature float fSiteRange = sqrt(pow(fMaxXRange, 2.0) + pow(fMaxYRange, 2.0)); object oCreature = GetFirstObjectInShape(SHAPE_SPHERE, fSiteRange, GetLocation(oFire)); vector vCreature = GetPosition(oCreature); if (vCreature.x >= vPosition.x) fXPos = vPosition.x + Math_RandomFloat(fXRange); else if (vCreature.x <= vPosition.x) fXPos = vPosition.x - Math_RandomFloat(fXRange); if (vCreature.y >= vPosition.y) fYPos = vPosition.y + Math_RandomFloat(fYRange); else if (vCreature.y <= vPosition.y) fYPos = vPosition.y - Math_RandomFloat(fYRange); } else { // Calculate new fire position fXPos = vPosition.x + (Math_RandomFloatSign() * Math_RandomFloat(fXRange)); fYPos = vPosition.y + (Math_RandomFloatSign() * Math_RandomFloat(fYRange)); } // Make sure fXPos is within bounds fXPos = Math_FitFloatToBoundaries(fXPos, vLimit.x, fMaxXRange); fYPos = Math_FitFloatToBoundaries(fYPos, vLimit.y, fMaxYRange); // Create another flame string strResRef = GetResRef(oFire); vector vNewPosition = Vector(fXPos, fYPos, vPosition.z); location lNewLocation = Location(GetArea(oFire), vNewPosition, Math_RandomFloat(180.0)); CreateObject(OBJECT_TYPE_PLACEABLE, strResRef, lNewLocation); DestroyObject(oFire, Math_RandomFloat(3.0)); } // Creates an area with explosive damage in it - all objects inside the sphere of effect take fire damage, // prorated from the center. void Fire_Explosion(location lCenter, int iMaxDamage=30, int iMinDamage=6, float fRadius=10.0) { if (fRadius <= 0.0) fRadius = 1.0; iMaxDamage -= iMinDamage; if (iMaxDamage <= 1) iMaxDamage=1; object oObject = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lCenter); effect eFX1 = EffectVisualEffect(VFX_IMP_FLAME_M); effect eFX2 = EffectVisualEffect(VFX_FNF_FIREBALL); ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eFX2, lCenter); while (GetIsObjectValid(oObject)) { location lLoc = GetLocation(oObject); float fDistance = GetDistanceBetweenLocations(lCenter, lLoc); int iTotalDamage = iMinDamage + FloatToInt((IntToFloat(Random(iMaxDamage)) * (fRadius - fDistance)/fRadius)); effect eDamage = EffectDamage(iTotalDamage, DAMAGE_TYPE_FIRE); ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oObject); ApplyEffectToObject(DURATION_TYPE_INSTANT, eFX1, oObject); oObject = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lCenter); } } // Clears off the state of all messages that have been sent (PC has eaten some food) void FoodSys_ClearMessages(object oPC) { SetLocalInt(oPC, "iStarveMsg01", FALSE); SetLocalInt(oPC, "iStarveMsg02", FALSE); SetLocalInt(oPC, "iStarveMsg03", FALSE); SetLocalInt(oPC, "iStarveMsg04", FALSE); } // Outputs some floating text above the PC that indicates what the PC thinks of the current weather. // If PC has been standing in the same spot for a while, output the info then block until he's standing at a new spot. // If PC has been moving about, have a small chance for output. void FoodSys_CommentOnWeather(object oPC, float fApparentTemp) { } // Called from FoodSys_Drunk_EffectDrunk() to determine if the booze-hound can hold // their liqour... void FoodSys_Drunk_CheckVomit(object oActivator, int iMOD, int iBloodAlcohol) { // Calculate whether oActivator's Blood Alcohol is too high int bVomit = FALSE; int iVomit; int iAdj; if (iMOD >= 0) { // For + CON iAdj = iMOD * iMOD; iVomit = Random(iBloodAlcohol) - (iMOD * Random(iMOD)); if (iVomit > (20 + (iMOD * (iMOD-1)) + (FloatToInt(IntToFloat(iMOD) * 3.35)))) bVomit = TRUE; } else { // For - CON iAdj = -1 * iMOD * iMOD; iVomit = Random(iBloodAlcohol) + (iMOD * Random(iMOD)); if (iVomit > (20 - (iMOD * (iMOD)) + (FloatToInt(IntToFloat(iMOD) * 3.35)))) bVomit = TRUE; } // oActivator can't hold their booze! if (bVomit) { // Say something pre-event? if (Random(100) > 49) FoodSys_Drunk_SpeakWarning(oActivator); // Vomit effects AssignCommand(oActivator, ClearAllActions()); AssignCommand(oActivator, FoodSys_Drunk_VomitEffect(oActivator, iMOD)); // Remove some supplies (they barfed them up, afterall!) Inventory_RemoveStackedItemQuantity(oActivator, "supplies", Random(40 - iAdj) + d2(10)); } } // Entry function for the FoodSys_Drunk_* functions. Called from the activator function(s) // for alcohol (such as Ale, Wine, etc.) void FoodSys_Drunk_EffectDrunk(object oTarget) { // Check for immunity if (GetLocalInt(oTarget, "iAlcohol_Immune")) return; // Proceed normally int iMOD = GetAbilityModifier(ABILITY_CONSTITUTION, oTarget); int iAdj = iMOD * iMOD; if (iMOD < 0) iAdj *= -1; int iBloodAlcohol = GetLocalInt(oTarget, "iBloodAlcohol"); if (iBloodAlcohol <= 0) AssignCommand(oTarget, DelayCommand(12.0, FoodSys_Drunk_LowerBloodAlcohol(oTarget))); int iInc = d12() + 5 - iAdj; if (iInc < 4) iInc = 4; iBloodAlcohol += iInc; SetLocalInt(oTarget, "iBloodAlcohol", iBloodAlcohol); // Do effects effect eFXA; effect eFXB; effect eFXC; if (iBloodAlcohol > (40 + iAdj)) { // CON/WIS/INT -1 eFXA = EffectAbilityDecrease(ABILITY_CONSTITUTION, 1); eFXB = EffectAbilityDecrease(ABILITY_WISDOM, 1); eFXC = EffectAbilityDecrease(ABILITY_INTELLIGENCE, 1); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXA, oTarget, IntToFloat(d20(2)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXB, oTarget, IntToFloat(d20(2)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXC, oTarget, IntToFloat(d20(2)-iMOD)); } if (iBloodAlcohol > (45 + iAdj)) { // WIS-1 / CHA-2 / CONC - 1 eFXA = EffectSkillDecrease(SKILL_CONCENTRATION, 1); eFXB = EffectAbilityDecrease(ABILITY_WISDOM, 1); eFXC = EffectAbilityDecrease(ABILITY_CHARISMA, 2); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXA, oTarget, IntToFloat(d20(3)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXB, oTarget, IntToFloat(d20(3)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXC, oTarget, IntToFloat(d20(3)-iMOD)); } if (iBloodAlcohol > (55 + iAdj)) { // DEX -1 / WIS -2/ CHA -2 eFXA = EffectAbilityDecrease(ABILITY_DEXTERITY, 1); eFXB = EffectAbilityDecrease(ABILITY_WISDOM, 2); eFXC = EffectAbilityDecrease(ABILITY_CHARISMA, 2); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXA, oTarget, IntToFloat(d20(3)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXB, oTarget, IntToFloat(d20(3)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXC, oTarget, IntToFloat(d20(3)-iMOD)); } if (iBloodAlcohol > (60 + iAdj)) { // Blindness eFXB = EffectSkillDecrease(SKILL_CONCENTRATION, 2); eFXC = EffectSkillDecrease(SKILL_DISCIPLINE, 2); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXB, oTarget, IntToFloat(d20(4)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXC, oTarget, IntToFloat(d20(4)-iMOD)); } if (iBloodAlcohol > (67 + iAdj)) { eFXB = EffectSkillDecrease(SKILL_CONCENTRATION, 4); eFXC = EffectSkillDecrease(SKILL_DISCIPLINE, 4); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXB, oTarget, IntToFloat(d20(4)-iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFXC, oTarget, IntToFloat(d20(4)-iMOD)); } // Check to see if PC is gonna POP FoodSys_Drunk_CheckVomit(oTarget, iMOD, iBloodAlcohol); } // Once the target object begins drinking alcohol, his Blood Alcohol level is raised. This function // is called and then recurses back into itself on a delay (determined by the CON of the target object) // until the Blood Alcohol level is 0 or less. void FoodSys_Drunk_LowerBloodAlcohol(object oTarget) { int iBloodAlcohol = GetLocalInt(oTarget, "iBloodAlcohol"); int iMOD = GetAbilityModifier(ABILITY_CONSTITUTION, oTarget); if (iMOD <= 0) iMOD = 1; if (iMOD > 14) iMOD = 14; iBloodAlcohol -= iMOD; SetLocalInt(oTarget, "iBloodAlcohol", iBloodAlcohol); if (iBloodAlcohol > 0) AssignCommand(oTarget, DelayCommand(14.0 - IntToFloat(iMOD), FoodSys_Drunk_LowerBloodAlcohol(oTarget))); } // Randomly plays back one of the basic vomit sounds for females. void FoodSys_Drunk_PlaySoundFemale(object oPC) { int iRandom = Random(3); string strSFX; if (iRandom == 0) strSFX = "f_vomit000"; if (iRandom == 1) strSFX = "f_vomit001"; if (iRandom == 2) strSFX = "f_vomit002"; AssignCommand(oPC, PlaySound(strSFX)); } // Randomly plays back one of the basic vomit sounds for males. void FoodSys_Drunk_PlaySoundMale(object oPC) { int iRandom = Random(3); string strSFX; if (iRandom == 0) strSFX = "m_vomit000"; if (iRandom == 1) strSFX = "m_vomit001"; if (iRandom == 2) strSFX = "m_vomit002"; AssignCommand(oPC, PlaySound(strSFX)); } // If the FoodSystem determines that the target object is going to vomit, it can call this function // to have the target say something prior to vomitting. void FoodSys_Drunk_SpeakWarning(object oTarget) { int iRandom = Random(4); string strSay; if (iRandom == 0) strSay = "Don't feel so good..."; if (iRandom == 1) strSay = "*BRACK*"; if (iRandom == 2) strSay = "AWE CRIPES!"; if (iRandom == 3) strSay = "Gonna PUKE!!!"; FloatingTextStringOnCreature(strSay, oTarget); } // This function is responsible for creating the actual vomit FX (including audio based on sex). void FoodSys_Drunk_VomitEffect(object oPC, int iMOD) { // Create some barf location lLoc = GetLocation(oPC); effect eVFXA = EffectVisualEffect(VFX_COM_BLOOD_REG_GREEN); effect eVFXB = EffectVisualEffect(VFX_COM_CHUNK_YELLOW_SMALL); effect eFX; eFX = EffectKnockdown(); ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eVFXA, lLoc, 16.0); ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eVFXB, lLoc, 16.0); float fDrunkAnimDur = 8.0 - IntToFloat(iMOD); AssignCommand(oPC, DelayCommand(1.8, PlayAnimation(ANIMATION_LOOPING_PAUSE_DRUNK, 1.0, fDrunkAnimDur))); AssignCommand(oPC, DelayCommand((1.8+fDrunkAnimDur), ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX, oPC, IntToFloat(d12() - iMOD)))); // Play audio if (GetGender(oPC) == GENDER_FEMALE) FoodSys_Drunk_PlaySoundFemale(oPC); else FoodSys_Drunk_PlaySoundMale(oPC); // Apply effects from puking! if (iMOD >=0 ) { int iPenalty = 8 - iMOD; if (iPenalty < 0) iPenalty = 1; eFX = EffectAbilityDecrease(ABILITY_CONSTITUTION, iPenalty); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX, oPC, IntToFloat(d100(5))); } else { eFX = EffectAbilityDecrease(ABILITY_CONSTITUTION, (-1 * iMOD)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX, oPC, IntToFloat(d100(8))); } } // Accessor function for the Active state of the area int FoodSys_GetActiveState(object oArea) { return(GetLocalInt(oArea, "ACTIVE_STATE")); } // Accessor function for ambient temperature variable int FoodSys_GetAmbientTemperature(object oArea) { return(GetLocalInt(oArea, "AMBIENT_TEMP")); } // Takes into account the amount of good/harm the PC's equipment is doing. Although Season and Time of Day // were incorporated previously, they are also considered here in relation to the clothing worn as 'influences'. int FoodSys_GetConAdjPCClothing(object oPC) { // Get Area Ambient Temperature; Modify with Season and TOD info. int iSeason = GetSeason(); int iTOD = GetTOD(); object oArea = GetArea(oPC); int iTempFlux = FoodSys_GetTemperatureFlux(oArea); int iAmbTemp = FoodSys_GetAmbientTemperature(oArea); // Modify the Temperature with the Season if (iSeason == SEASON_WINTER) iAmbTemp *= -1; // Invert the ambient temperature for winter if ((iSeason == SEASON_SPRING) || (iSeason == SEASON_FALL)) iAmbTemp -= FloatToInt(IntToFloat(iAmbTemp) / IntToFloat(iTempFlux)); // Decrease the ambient temperature for Spring and Fall // Modify the Temperature with the Flux int iFlux = Random(iTempFlux); if ((iSeason == SEASON_FALL) || (iSeason == SEASON_SPRING)) { iFlux /= 2; if (Random(100) < 50) iFlux *= -1; // Decrease the temperature from the seasonal ambient iAmbTemp += iFlux; } // Modify the Temperature with the Windshield // Not yet implimented // Modify the Temperature with the TOD if (iTOD == TOD_NIGHT) iAmbTemp -= Random(iFlux/2); if (iTOD == TOD_DAY) iAmbTemp += Random(iFlux/2); if ((iTOD == TOD_DUSK) || (iTOD == TOD_DAWN)) { if (Random(100) < 50) iAmbTemp -= Random(iFlux/3); else iAmbTemp += Random(iFlux/3); } // Generate a Heat Retension percentage for the PC based on his clothing float fHeatRetension = IntToFloat(FoodSys_GetHeatRetension(oPC)); // What is the apparent temperature for the PC? float fAmbTemp = IntToFloat(iAmbTemp); float fApparentTemp = fAmbTemp; float fAdjust; float fSign = 1.0; if (iAmbTemp < 0) fSign = -1.0; float fHRFactor = fHeatRetension - 50; // The PC's Heat Retension factor (0 heat loss and heat gain is at 50%, tan(0) = 0 which is why I correct the value with -50, to get a ZERO heat gain/loss) float fWarmingFactor = fAmbTemp - 25; // Represents the players ability to heat themselves as temperature drops (becomes less and less with lower temps). ZERO point for this is 25C. if (fWarmingFactor >= 90.0) fWarmingFactor = 89.0; // Ensure the values are not illegal for the TAN function if (fWarmingFactor <= -90.0) fWarmingFactor = -89.0; if (fHRFactor >= 90.0) fHRFactor = 89.0; if (fHRFactor <= -90.0) fHRFactor = -89.0; fApparentTemp += fSign * fAmbTemp * (tan(fWarmingFactor) + tan(fHRFactor)); // Return an adjustment for the PC's food consumption rate int iExtraFoodNeeded = 0; if (fApparentTemp <= -25.0) iExtraFoodNeeded = 7; if (fApparentTemp >= -25.0 && fApparentTemp <= -12.0) iExtraFoodNeeded = 4; if (fApparentTemp >= -11.0 && fApparentTemp <= -5.0) iExtraFoodNeeded = 2; if (fApparentTemp >= -4.0 && fApparentTemp <= 7.0) iExtraFoodNeeded = -1; if (fApparentTemp >= 8.0 && fApparentTemp <= 25.0) iExtraFoodNeeded = -2; if (fApparentTemp >= 25.0 && fApparentTemp <= 32.0) iExtraFoodNeeded = 1; if (fApparentTemp >= 33.0) iExtraFoodNeeded = 2; // Let the PC see what his character thinks of the current temperature (low random chance if moving // about, high random chance if PC has been standing still for some time) FoodSys_CommentOnWeather(oPC, fApparentTemp); // Finish up if (DEBUG_STATE) SendMessageToPC(oPC, "Current Temperature is at: " + FloatToString(fAmbTemp) + " C, Apparent temperature is at: " + FloatToString(fApparentTemp) + " C"); return(iExtraFoodNeeded); } // Returns the ration consumption adjustment factor for the current season int FoodSys_GetConAdjSeason() { int iMonth = GetCalendarMonth(); if ((iMonth >= 1 && iMonth <= 2) || iMonth==12) return 3; // December - February (WINTER) if (iMonth >= 3 && iMonth <= 5) return 1; // March - May (SPRING) if (iMonth >= 6 && iMonth <= 8) return 2; // June - August (SUMMER) if (iMonth >= 9 && iMonth <= 11) return 0; // September - November (FALL) return 0; } // Returns the ration consumption adjustment factor for the difficult modifier of the current terrain int FoodSys_GetConAdjTerrainModifier(object oArea) { int iTerrainModifier = FoodSys_GetTerrainModifier(oArea); if (iTerrainModifier == TERRAIN_MODIFIER_INFRA) return -3; if (iTerrainModifier == TERRAIN_MODIFIER_EASY) return -2; if (iTerrainModifier == TERRAIN_MODIFIER_NORMAL) return 0; if (iTerrainModifier == TERRAIN_MODIFIER_HARD) return 1; if (iTerrainModifier == TERRAIN_MODIFIER_ULTRA) return 2; return 0; } // Returns the ration consumption adjustment factor for the current type of terrain int FoodSys_GetConAdjTerrainType(object oArea) { int iTerrainType = FoodSys_GetTerrainType(oArea); if (iTerrainType == TERRAIN_TYPE_DUNGEON) return 2; if (iTerrainType == TERRAIN_TYPE_FOREST) return 3; if (iTerrainType == TERRAIN_TYPE_GRASSLAND) return 0; if (iTerrainType == TERRAIN_TYPE_CITY) return 1; if (iTerrainType == TERRAIN_TYPE_CAVERN) return 2; if (iTerrainType == TERRAIN_TYPE_HILLS) return 3; return 0; } // Returns the ration consumption adjustment factor for the current time of day int FoodSys_GetConAdjTOD() { if (GetIsDawn()) return 1; if (GetIsDay()) return 0; if (GetIsDusk()) return 1; if (GetIsNight()) return 2; return 0; } // Accessor function for the basic food usage value (food used every 24 seconds, or in a HB) int FoodSys_GetFoodUse() { return(GetLocalInt(GetModule(), "BASE_FOOD_USAGE")); } // Examines the PC to determine what articles of clothing he's wearing and how well he will therefore // retain heat. Can be a good thing, can be a bad thing! A return value of 50% is the ideal level for // maintaining a constant temperature. int FoodSys_GetHeatRetension(object oPC) { int iHeatRetension = 0; // Look for Summer-specific Heat dissipation items (have no effect during winter) // Is it Summer? if (SEASON_SUMMER == GetSeason()) { // Not implimented yet } // Look for Winter-specific Heat Retension items (large shield, etc.) (have no effect during summer) // Is it winter? if (SEASON_WINTER == GetSeason()) { // Not implimented yet } // Assess the remaining items for year round heat retention (HR = 65% if all slots are full) if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_ARMS, oPC))) iHeatRetension += 7; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_BELT, oPC))) iHeatRetension += 1; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC))) iHeatRetension += 7; if ((GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC))) || (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC)))) iHeatRetension += 25; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC))) iHeatRetension += 14; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_HEAD, oPC))) iHeatRetension += 10; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC))) iHeatRetension += 4; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_NECK, oPC))) iHeatRetension += 7; if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC))) iHeatRetension += 4; return(iHeatRetension); } // Accessor function for the minimum food rate consumption variable int FoodSys_GetMinimumFoodRate(object oArea) { return(GetLocalInt(oArea, "MIN_FOOD_RATE")); } // Calculates how long the PC can last without food int FoodSys_GetStarvationLimit(object oPC) { // Based on PC's CON and STR int iCON = GetAbilityModifier(ABILITY_CONSTITUTION, oPC); int iSTR = GetAbilityModifier(ABILITY_STRENGTH, oPC); // Calculate the limit return(iCON + (iSTR / 2)); } // Get the PC's Current starvation level int FoodSys_GetStarveCounter(object oPC) { return(GetLocalInt(oPC, "iStarveCounter")); } // Accessor function for the temperature flux variable int FoodSys_GetTemperatureFlux(object oArea) { return(GetLocalInt(oArea, "TEMP_FLUX")); } // Accessor function for terrain modifier variable int FoodSys_GetTerrainModifier(object oArea) { return(GetLocalInt(oArea, "TERRAIN_MOD")); } // Accessor function for terrain type variable int FoodSys_GetTerrainType(object oArea) { return(GetLocalInt(oArea, "TERRAIN_TYPE")); } // Main function for maintaining the FoodSystem_* on all PC's - launched via FoodSystem_Initialize(). void FoodSys_HeartBeat(object oPC, float fHeartBeat) { // Exempt from Hunger? Immune to Food Use, Dead, or Close to Dead... if ((GetLocalInt(oPC, "iFoodSys_Immune") == TRUE) || (GetCurrentHitPoints(oPC) <= 3)) { if (DEBUG_STATE) SendMessageToPC(oPC, "PC is immune to hunger..."); AssignCommand(oPC, DelayCommand(fHeartBeat, FoodSys_HeartBeat(oPC, fHeartBeat))); // Make sure the Food System Heart Beat continues to fire return; } // Is the weather system in the current area active? object oArea = GetArea(oPC); if (!FoodSys_GetActiveState(oArea)) { if (DEBUG_STATE) SendMessageToPC(oPC, "Food system not active in this area."); AssignCommand(oPC, DelayCommand(fHeartBeat, FoodSys_HeartBeat(oPC, fHeartBeat))); // Make sure the Food System Heart Beat continues to fire return; } // Get Consumption Adjustment factor for Season int iConsumpAdj = FoodSys_GetFoodUse(); iConsumpAdj += FoodSys_GetConAdjSeason(); // Get Consumption Adjustment factor for TOD iConsumpAdj += FoodSys_GetConAdjTOD(); // Get Consumption Adjustment factor for Area Terrain Type iConsumpAdj += FoodSys_GetConAdjTerrainType(oArea); // Get Consumption Adjustment factor for Area Terrain Modifier iConsumpAdj += FoodSys_GetConAdjTerrainModifier(oArea); // Consumption Adjustment factor for PC's clothing iConsumpAdj += FoodSys_GetConAdjPCClothing(oPC); // Constitution adjustment factor iConsumpAdj -= GetAbilityModifier(ABILITY_CONSTITUTION, oPC); // Strength adjustment factor iConsumpAdj -= (GetAbilityModifier(ABILITY_STRENGTH, oPC) / 2); // Make sure food consumed is NOT less than the Minimum Food Rate consumption for every HeartBeat period // (This is the least amount of food that MUST be consumed each food_sys heartbeat, regardless of bonusses) int iMinFoodRate = FoodSys_GetMinimumFoodRate(oArea); if (iConsumpAdj < iMinFoodRate) iConsumpAdj = iMinFoodRate; if (DEBUG_STATE) SendMessageToPC(oPC, "Final Food Usage: " + IntToString(iConsumpAdj)); // Check starvation counter. int iStarveCounter = FoodSys_GetStarveCounter(oPC); int iStarveLimit = FoodSys_GetStarvationLimit(oPC); // Has PC reached starvation levels of hunger yet? if (iStarveCounter > iStarveLimit) { // YES! U GONNA DIE NOW SENOR! // How high is the counter? Output a message indicating PC's severity! effect eFX1; effect eFX2; effect eFX3; effect eFX4; effect eFX5; int iPenalty = iStarveCounter - iStarveLimit; int iSeason = GetSeason(); if (iStarveCounter >= iStarveLimit+1) { // -(STR/CON/DEX - 1; CONC & DISC - 5;) FoodSys_SendMessage(oPC, "iStarveMsg01", "I need food... badly!"); eFX1 = EffectAbilityDecrease(ABILITY_STRENGTH, iPenalty); // eFX2 = EffectAbilityDecrease(ABILITY_CONSTITUTION, iPenalty); eFX3 = EffectAbilityDecrease(ABILITY_DEXTERITY, iPenalty); eFX4 = EffectSkillDecrease(SKILL_CONCENTRATION, iPenalty); eFX5 = EffectSkillDecrease(SKILL_DISCIPLINE, iPenalty); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, fHeartBeat); // ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX2, oPC, fHeartBeat); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX3, oPC, fHeartBeat); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX4, oPC, fHeartBeat); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX5, oPC, fHeartBeat); } if (iStarveCounter >= iStarveLimit+2) { // - Slow FoodSys_SendMessage(oPC, "iStarveMsg02", "I'm starving!! Getting weaker... must find food soon..."); eFX1 = EffectSlow(); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, fHeartBeat); } if (iStarveCounter >= iStarveLimit+3) { // - Reduced Damage; Reduced AC; both increase on how far over the Starve limit we are FoodSys_SendMessage(oPC, "iStarveMsg03", "I'm starving to death!! ...must find food or shelter soon..."); eFX1 = EffectDamageDecrease(iPenalty); eFX2 = EffectACDecrease(iPenalty); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, fHeartBeat); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX2, oPC, fHeartBeat); } if (iStarveCounter >= iStarveLimit+4) { // - Hit points reduced based on how far over Starve limit we are if (18 > d20()+GetFortitudeSavingThrow(oPC)) { SendMessageToPC(oPC, "Saving throw vs. Primary effects of starvation failed!"); FoodSys_SendMessage(oPC, "iStarveMsg04", "...can't keep going..."); eFX1 = EffectSavingThrowDecrease(SAVING_THROW_ALL, 1); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, IntToFloat(Random(FloatToInt(fHeartBeat)))); } } if (iStarveCounter >= iStarveLimit+5) { // - %Chance Dazed; %Chance Knockdown; %Chance Stunned; if (iPenalty+10 > d20()+GetFortitudeSavingThrow(oPC)) { // PC becomes diseased SendMessageToPC(oPC, "Saving throw vs. Primary effects of starvation failed!"); int iRandom = Random(3); string strFXMsg; if (iRandom == 0) { eFX1 = EffectDazed(); strFXMsg = "...can't think straight..."; } if (iRandom == 1) { eFX1 = EffectKnockdown(); strFXMsg = "...dizzy... loosing my balance..."; } if (iRandom == 2) { eFX1 = EffectStunned(); strFXMsg = "... who... where am i???..."; } FloatingTextStringOnCreature(strFXMsg, oPC); SendMessageToPC(oPC, strFXMsg); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, IntToFloat(Random(FloatToInt(fHeartBeat)))); } } if (iStarveCounter >= iStarveLimit+8) { // - %Chance Blindness; %Chance Deafness; %Chance Confused if (iPenalty+10 > d20()+GetFortitudeSavingThrow(oPC)) { // PC becomes diseased SendMessageToPC(oPC, "Saving throw vs. Tertiary effects of starvation failed!"); int iRandom = Random(3); string strFXMsg; if (iRandom == 0) { eFX1 = EffectBlindness(); strFXMsg = "...my vision has blacked out from hunger!"; } if (iRandom == 1) { eFX1 = EffectDeaf(); strFXMsg = "...i can't hear anything but a buzzing sound from the hunger!"; } if (iRandom == 2) { eFX1 = EffectConfused(); strFXMsg = "...where? where am i?? so... hungry..."; } FloatingTextStringOnCreature(strFXMsg, oPC); SendMessageToPC(oPC, strFXMsg); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, IntToFloat(Random(FloatToInt(fHeartBeat))+iPenalty)); } } if (iStarveCounter >= iStarveLimit+10) { // - %Chance of Disease if (d100()-GetAbilityModifier(ABILITY_CONSTITUTION, oPC) >= 95-iPenalty) { if (iPenalty+12 > d20()+GetFortitudeSavingThrow(oPC)) { // PC becomes diseased SendMessageToPC(oPC, "Saving throw vs. Disease Vulnerability effects of starvation failed!"); string strFXMsg; int iRandom = Random(9); if (iRandom == 0) { eFX1 = EffectDisease(DISEASE_BURROW_MAGGOTS); strFXMsg = "... i'm so weak i've contracted burrowing maggots..."; } if (iRandom == 1) { eFX1 = EffectDisease(DISEASE_CACKLE_FEVER); strFXMsg = "... i'm so weak i've contracted cackle fever..."; } if (iRandom == 2) { eFX1 = EffectDisease(DISEASE_DEMON_FEVER); strFXMsg = "... i'm so weak i've contracted demon fever..."; } if (iRandom == 3) { eFX1 = EffectDisease(DISEASE_DEVIL_CHILLS); strFXMsg = "... i'm so weak i've contracted devil chills..."; } if (iRandom == 4) { eFX1 = EffectDisease(DISEASE_DREAD_BLISTERS); strFXMsg = "... i'm so weak i've contracted dread blisters..."; } if (iRandom == 5) { eFX1 = EffectDisease(DISEASE_FILTH_FEVER); strFXMsg = "... i'm so weak i've contracted filth fever..."; } if (iRandom == 6) { eFX1 = EffectDisease(DISEASE_MINDFIRE); strFXMsg = "... i'm so weak i've contracted mindfire..."; } if (iRandom == 7) { eFX1 = EffectDisease(DISEASE_RED_ACHE); strFXMsg = "... i'm so weak i've contracted the red ache..."; } if (iRandom == 8) { eFX1 = EffectDisease(DISEASE_SHAKES); strFXMsg = "... i'm so weak i've contracted the shakes..."; } FloatingTextStringOnCreature(strFXMsg, oPC); SendMessageToPC(oPC, strFXMsg); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX1, oPC, IntToFloat(Random(FloatToInt(fHeartBeat)) * (1+Random(FloatToInt(fHeartBeat))))); } } } } // How many supply rations are needed? Does the PC have them? if (FoodSys_TakeSupplies(oPC, iConsumpAdj)) { // Yes, do a debit. Clear starvation counter to ZERO. SetLocalInt(oPC, "iStarveMsg00", FALSE); FoodSys_ClearMessages(oPC); FoodSys_SetStarveCounter(oPC); // Reset the starvation counter to ZERO } else { // No, indicate PC has run out of food. Start counter. FoodSys_SetStarveCounter(oPC, ++iStarveCounter); // Increment starvation level int iStarveMsg00 = GetLocalInt(oPC, "iStarveMsg00"); if (!iStarveMsg00) { FloatingTextStringOnCreature("I've run out of food!!", oPC); SetLocalInt(oPC, "iStarveMsg00", TRUE); } } // Make sure the Food System Heart Beat continues to fire AssignCommand(oPC, DelayCommand(fHeartBeat, FoodSys_HeartBeat(oPC, fHeartBeat))); // Output info for debugging FoodSys_ShowTerrainVars(oPC); } // Placed in the modules OnClientEnter and used on PC's entering the game. Used to // activate the food system for all PC's entering the world. void FoodSys_Initialize(object oTarget, float fHeartBeat=60.0, int iBaseFoodUse=6) { FoodSys_SetFoodUse(iBaseFoodUse); AssignCommand(oTarget, FoodSys_HeartBeat(oTarget, fHeartBeat)); } // Sends the message but ensures it is only seend ONCE by the PC until he has eaten again... void FoodSys_SendMessage(object oPC, string strMessageID, string strMessage) { if (GetLocalInt(oPC, strMessageID)) return; SetLocalInt(oPC, strMessageID, TRUE); FloatingTextStringOnCreature(strMessage, oPC); } // Sets the minimum food requirement per heartbeat of the system void FoodSys_SetFoodUse(int iBaseFoodUse) { SetLocalInt(GetModule(), "BASE_FOOD_USAGE", iBaseFoodUse); } // Set the value contained within the PC's starvation counter void FoodSys_SetStarveCounter(object oPC, int iValue=0) { SetLocalInt(oPC, "iStarveCounter", iValue); } // Used in an Area's OnEnter callback event to set how much food the area requires from travellers. // Can also be called in an areas OnHeartBeat - though not recommended. Will only fire once for each // area that calls it. // *Default params are TERRAIN_TYPE_CITY, TEMP=25C, TERRAIN_MODIFIER_NORMAL, MIN_FOOD_RATE=1 void FoodSys_SetTerrainType(int iTerrainType=3, int iAmbientTemperature=25, int iTerrainModifier=2, int iTemperatureFlux=10, int iMinFoodRate=1, int iActive=TRUE) { // Be sure Terrain Type is valid if ((iTerrainType != TERRAIN_TYPE_DUNGEON) && (iTerrainType != TERRAIN_TYPE_FOREST) && (iTerrainType != TERRAIN_TYPE_GRASSLAND) && (iTerrainType != TERRAIN_TYPE_CITY) && (iTerrainType != TERRAIN_TYPE_CAVERN) && (iTerrainType != TERRAIN_TYPE_HILLS)) iTerrainType = TERRAIN_TYPE_CITY; // Be sure Terrain Difficulty Modifier is valid if ((iTerrainModifier != TERRAIN_MODIFIER_INFRA) && (iTerrainModifier != TERRAIN_MODIFIER_EASY) && (iTerrainModifier != TERRAIN_MODIFIER_NORMAL) && (iTerrainModifier != TERRAIN_MODIFIER_HARD) && (iTerrainModifier != TERRAIN_MODIFIER_ULTRA)) iTerrainModifier = TERRAIN_MODIFIER_NORMAL; // Set the variables SetLocalInt(OBJECT_SELF, "TERRAIN_TYPE", iTerrainType); SetLocalInt(OBJECT_SELF, "TERRAIN_MOD", iTerrainModifier); SetLocalInt(OBJECT_SELF, "AMBIENT_TEMP", iAmbientTemperature); SetLocalInt(OBJECT_SELF, "MIN_FOOD_RATE", iMinFoodRate); // The least amount of food that MUST be consumed each food_sys heartbeat, regardless of bonusses SetLocalInt(OBJECT_SELF, "TEMP_FLUX", iTemperatureFlux); SetLocalInt(OBJECT_SELF, "ACTIVE_STATE", iActive); } // Sends the current terrain variables to a PC void FoodSys_ShowTerrainVars(object oPC) { int iTerrainType = FoodSys_GetTerrainType(GetArea(oPC)); int iTerrainModifier = FoodSys_GetTerrainModifier(GetArea(oPC)); int iAmbientTemperature = FoodSys_GetAmbientTemperature(GetArea(oPC)); // Terrain Type string strTerrainType; if (iTerrainType == TERRAIN_TYPE_DUNGEON) strTerrainType = "Dungeon"; if (iTerrainType == TERRAIN_TYPE_FOREST) strTerrainType = "Forest"; if (iTerrainType == TERRAIN_TYPE_GRASSLAND) strTerrainType = "Grassland"; if (iTerrainType == TERRAIN_TYPE_CITY) strTerrainType = "City"; if (iTerrainType == TERRAIN_TYPE_CAVERN) strTerrainType = "Cavern/Mine"; if (iTerrainType == TERRAIN_TYPE_HILLS) strTerrainType = "Hills"; string strTerrainModifier; // Terrain Modifier if (iTerrainModifier == TERRAIN_MODIFIER_INFRA) strTerrainModifier = "Infra"; if (iTerrainModifier == TERRAIN_MODIFIER_EASY) strTerrainModifier = "Easy"; if (iTerrainModifier == TERRAIN_MODIFIER_NORMAL) strTerrainModifier = "Normal"; if (iTerrainModifier == TERRAIN_MODIFIER_HARD) strTerrainModifier = "Hard"; if (iTerrainModifier == TERRAIN_MODIFIER_ULTRA) strTerrainModifier = "Ultra"; // Season string strSeason = GetSeasonString(); string strTOD = GetTODString(); string strTempFlux = IntToString(FoodSys_GetTemperatureFlux(GetArea(oPC))); // Active state int iActiveState = FoodSys_GetActiveState(GetArea(oPC)); // Output the values if (DEBUG_STATE) SendMessageToPC(oPC, strTerrainType + ", " + strTerrainModifier + ", " + IntToString(iAmbientTemperature) + " C " + "+/- " + strTempFlux + ", " + strTOD + ", " + strSeason + ", active state is " + IntToString(iActiveState)); } // Debit the PC for the supplies he consumes this 'HeartBeat' int FoodSys_TakeSupplies(object oPC, int iQuantity) { int iPCHasEnough = FALSE; // Count the PC's supplies object oItem = GetFirstItemInInventory(oPC); int iTotalSupplies = 0; while (GetIsObjectValid(oItem)) { if (GetTag(oItem) == "supplies") iTotalSupplies += GetNumStackedItems(oItem); oItem = GetNextItemInInventory(oPC); } // Does he have enough? if (iTotalSupplies >= iQuantity) { iPCHasEnough = TRUE; // Yes! Inventory_RemoveStackedItemQuantity(oPC, "supplies", iQuantity); } else Inventory_RemoveStackedItemQuantity(oPC, "supplies", iTotalSupplies); return iPCHasEnough; } // Returns the subjects highest class level int GetHighestLevel(object oSubject) { int iIndex; int iHighest = 0; int iTemp; for (iIndex=1; iIndex<=3; iIndex++) { iTemp = GetLevelByPosition(iIndex, oSubject); if (iTemp > iHighest) iHighest = iTemp; } return iHighest; } // Returns the PC's combined level from all classes (MAX 3 classes) int GetLevel(object oSubject) { int iIndex; int iLevels = 0; for (iIndex=1; iIndex<=3; iIndex++) iLevels += GetLevelByPosition(iIndex, oSubject); return(iLevels); } // Returns the subjects lowest class level int GetLowestLevel(object oSubject) { int iIndex; int iLowest = 500; int iTemp; for (iIndex=1; iIndex<=3; iIndex++) { iTemp = GetLevelByPosition(iIndex, oSubject); if (iTemp < iLowest) iLowest = iTemp; } return iLowest; } // Returns the subjects middle class level. Only for PC's with 3 classes - otherwise returns a 0. int GetMiddleLevel(object oSubject) { return 0; } // Returns the total number of negative effects that the target object has on it int GetNumberOfNegativeEffects(object oTarget) { int iTotalEffects = 0; effect eBad = GetFirstEffect(oTarget); while(GetIsEffectValid(eBad)) { if ((GetEffectType(eBad) == EFFECT_TYPE_AC_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_ATTACK_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_DAMAGE_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_SAVING_THROW_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_CURSE || GetEffectType(eBad) == EFFECT_TYPE_BLINDNESS || GetEffectType(eBad) == EFFECT_TYPE_DEAF || GetEffectType(eBad) == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_DISEASE || GetEffectType(eBad) == EFFECT_TYPE_POISON || GetEffectType(eBad) == EFFECT_TYPE_PARALYZE || GetEffectType(eBad) == EFFECT_TYPE_NEGATIVELEVEL || GetEffectType(eBad) == EFFECT_TYPE_ABILITY_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_SPELL_RESISTANCE_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_SKILL_DECREASE)) iTotalEffects++; eBad = GetNextEffect(oTarget); } return iTotalEffects; } // Accessor function the current season int GetSeason() { int iMonth = GetCalendarMonth(); if ((iMonth >= 1 && iMonth <= 2) || iMonth==12) return SEASON_WINTER; if (iMonth >= 3 && iMonth <= 5) return SEASON_SPRING; if (iMonth >= 6 && iMonth <= 8) return SEASON_SUMMER; if (iMonth >= 9 && iMonth <= 11) return SEASON_FALL; return(0); } // Accessor function for the current season text string GetSeasonString() { int iMonth = GetCalendarMonth(); if ((iMonth >= 1 && iMonth <= 2) || iMonth==12) return "Winter"; if (iMonth >= 3 && iMonth <= 5) return "Spring"; if (iMonth >= 6 && iMonth <= 8) return "Summer"; if (iMonth >= 9 && iMonth <= 11) return "Fall"; return("Unknown"); } // Accessor function for the Time of Day int GetTOD() { if (GetIsDawn()) return TOD_DAWN; if (GetIsDay()) return TOD_DAY; if (GetIsDusk()) return TOD_DUSK; if (GetIsNight()) return TOD_NIGHT; return(0); } // Accessor function for the current TOD string string GetTODString() { if (GetIsDawn()) return "Dawn"; if (GetIsDay()) return "Day"; if (GetIsDusk()) return "Dusk"; if (GetIsNight()) return "Night"; return("Unknown"); } // Called by an object that is capable of Healing another object (a Priest healing a PC and all his minions). // iRemoveEffects dictates what Negative Effects will be removed from the targets. // iAddBlessings allows the caller to add a set of blessings to the targets. void Heal_BeginHealing(object oPC, int iRemoveEffects = 31, int iAddBlessings = FALSE, int iGiveHenchPotions = TRUE, string strPotionTag = "NW_IT_MPOTION003", int iHealHench = TRUE, int iHealFamil = TRUE, int iHealOthers = TRUE, int iMinHP = 0, int iMaxHP = 0) { object oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC); object oAnimal = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oPC); object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oPC); object oDominated = GetAssociate(ASSOCIATE_TYPE_DOMINATED,oPC); object oSummoned = GetAssociate(ASSOCIATE_TYPE_SUMMONED,oPC); ActionPauseConversation(); // Bless PC if needed if (iAddBlessings != 0) Heal_BlessingEffect(oPC, iAddBlessings); // Do restorations ActionCastFakeSpellAtObject(SPELL_GREATER_RESTORATION, OBJECT_SELF); ActionDoCommand(Heal_RestoreEffect(oPC, iRemoveEffects, iMinHP, iMaxHP)); if (!GetIsObjectValid(GetItemPossessedBy(oHenchman, strPotionTag)) && iGiveHenchPotions) CreateItemOnObject(strPotionTag, oHenchman, 3); if(GetIsObjectValid(oHenchman) && iHealHench) ActionDoCommand(Heal_RestoreEffect(oHenchman, iRemoveEffects, iMinHP, iMaxHP)); if(GetIsObjectValid(oAnimal) && iHealOthers) ActionDoCommand(Heal_RestoreEffect(oAnimal, iRemoveEffects, iMinHP, iMaxHP)); if(GetIsObjectValid(oFamiliar) && iHealFamil) ActionDoCommand(Heal_RestoreEffect(oFamiliar, iRemoveEffects, iMinHP, iMaxHP)); if(GetIsObjectValid(oDominated) && iHealOthers) ActionDoCommand(Heal_RestoreEffect(oDominated, iRemoveEffects, iMinHP, iMaxHP)); if(GetIsObjectValid(oSummoned) && iHealOthers) ActionDoCommand(Heal_RestoreEffect(oSummoned, iRemoveEffects, iMinHP, iMaxHP)); ActionResumeConversation(); } // Only the PC may have these (as oppossed to Henchmen, etc.)!!! // Blocks PC from obtaining multiple blessings while previous ones are still in effect!! // iAddBlessings *must* contain ONE of the HEAL_BLESS_DURATION_* values, and should have // one of the 'packages' and perhaps one (or more) of the Blessing Options available. Made available // through a conversation, these effects can be charged for or specified with a good degree of variety. void Heal_BlessingEffect(object oPC, int iAddBlessings) { if (GetLocalInt(oPC, "iBlessingActive") == 1) { FloatingTextStringOnCreature("Blessing fizzled!", oPC); return; // PC has already been blessed! } float fDuration; effect eACBonus; effect eATBonus; effect eSTBonus; effect eOptional; // Get duration if ((iAddBlessings & 16) == 16) fDuration = 60.0; // 1 minute if ((iAddBlessings & 32) == 32) fDuration = 180.0; // 3 minutes if ((iAddBlessings & 64) == 64) fDuration = 360.0; // 6 minutes // Basic blessing if ((iAddBlessings & 1) == 1) { eACBonus = EffectACIncrease(1); eATBonus = EffectAttackIncrease(1); eSTBonus = EffectSavingThrowIncrease(SAVING_THROW_TYPE_ALL, 1); } // Average blessing if ((iAddBlessings & 2) == 2) { eACBonus = EffectACIncrease(2); eATBonus = EffectAttackIncrease(2); eSTBonus = EffectSavingThrowIncrease(SAVING_THROW_TYPE_ALL, 2); } // High blessing if ((iAddBlessings & 4) == 4) { eACBonus = EffectACIncrease(3); eATBonus = EffectAttackIncrease(3); eSTBonus = EffectSavingThrowIncrease(SAVING_THROW_TYPE_ALL, 3); } ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eACBonus, oPC, fDuration); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eATBonus, oPC, fDuration); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eSTBonus, oPC, fDuration); // 128 = Optional Damage Increase effect (random amount) if ((iAddBlessings & 128) == 128) { eOptional = EffectDamageIncrease(d12()); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 256 = Optional Damage Reduction effect (random) if ((iAddBlessings & 256) == 256) { eOptional = EffectDamageReduction(d10(), DAMAGE_POWER_PLUS_ONE, d20(10)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 512 = Optional Damage Resistance to ACID effect if ((iAddBlessings & 512) == 512) { eOptional = EffectDamageResistance(DAMAGE_TYPE_ACID, d10()); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 1024 = Optional Damage Resistance to COLD effect if ((iAddBlessings & 1024) == 1024) { eOptional = EffectDamageResistance(DAMAGE_TYPE_COLD, d10()); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 2048 = Optional Damage Resistance to FIRE effect if ((iAddBlessings & 2048) == 2048) { eOptional = EffectDamageResistance(DAMAGE_TYPE_FIRE, d10()); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 4096 = Optional Haste Effect if ((iAddBlessings & 4096) == 4096) { eOptional = EffectHaste(); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 8192 = Optional Temporary Hit Points (random percent of Target's max HP's) if ((iAddBlessings & 8192) == 8192) { eOptional = EffectTemporaryHitpoints(Random(GetMaxHitPoints(oPC))); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 16384 = Optional Regeneration effect if ((iAddBlessings & 16384) == 16384) { eOptional = EffectRegenerate(d4(), 3.0); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 32768 = Optional Level 1 Spell Absorption Effect if ((iAddBlessings & 32768) == 32768) { eOptional = EffectSpellLevelAbsorption(1, d6(3)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 65536 = Optional Level 2 Spell Absorption Effect if ((iAddBlessings & 65536) == 65536) { eOptional = EffectSpellLevelAbsorption(2, d8(4)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // 131072 = Optional Level 3 Spell Absorption Effect if ((iAddBlessings & 131072) == 131072) { eOptional = EffectSpellLevelAbsorption(3, d8(5)); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eOptional, oPC, fDuration); } // Finish up SetLocalInt(oPC, "iBlessingActive", 1); AssignCommand(oPC, DelayCommand(fDuration, SetLocalInt(oPC, "iBlessingActive", 0))); effect eVisual = EffectVisualEffect(VFX_IMP_RESTORATION_GREATER); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, oPC); } // Called from Heal_BeginHealing() function. Does the actual 'healing' involved on the PC and his // If all HP's should be healed, make iMaxHP = 0. If NO HP's should be healed, make iDoHeal = FALSE void Heal_RestoreEffect(object oTarget, int iRemoveEffects, int iMinHP, int iMaxHP, int iDoHeal=TRUE) { // Search for negative effects effect eBad; // Remove basic effects if ((iRemoveEffects & 1) == 1) { eBad = GetFirstEffect(oTarget); while(GetIsEffectValid(eBad)) { if ((GetEffectType(eBad) == EFFECT_TYPE_AC_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_ATTACK_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_DAMAGE_DECREASE)) RemoveEffect(oTarget, eBad); eBad = GetNextEffect(oTarget); } } // Remove sensory impairments if ((iRemoveEffects & 2) == 2) { eBad = GetFirstEffect(oTarget); while(GetIsEffectValid(eBad)) { if ((GetEffectType(eBad) == EFFECT_TYPE_SAVING_THROW_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_CURSE || GetEffectType(eBad) == EFFECT_TYPE_BLINDNESS || GetEffectType(eBad) == EFFECT_TYPE_DEAF)) RemoveEffect(oTarget, eBad); eBad = GetNextEffect(oTarget); } } // Remove constitution impairments if ((iRemoveEffects & 4) == 4) { eBad = GetFirstEffect(oTarget); while(GetIsEffectValid(eBad)) { if ((GetEffectType(eBad) == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_DISEASE || GetEffectType(eBad) == EFFECT_TYPE_POISON || GetEffectType(eBad) == EFFECT_TYPE_PARALYZE)) RemoveEffect(oTarget, eBad); eBad = GetNextEffect(oTarget); } } // Remove Level Drains if ((iRemoveEffects & 8) == 8) { eBad = GetFirstEffect(oTarget); while(GetIsEffectValid(eBad)) { if ((GetEffectType(eBad) == EFFECT_TYPE_NEGATIVELEVEL)) RemoveEffect(oTarget, eBad); eBad = GetNextEffect(oTarget); } } // Remove Advanced impairments if ((iRemoveEffects & 16) == 16) { eBad = GetFirstEffect(oTarget); while(GetIsEffectValid(eBad)) { if ((GetEffectType(eBad) == EFFECT_TYPE_ABILITY_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_SPELL_RESISTANCE_DECREASE || GetEffectType(eBad) == EFFECT_TYPE_SKILL_DECREASE)) RemoveEffect(oTarget, eBad); eBad = GetNextEffect(oTarget); } } // Heal the target object if (iDoHeal) { if(GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) { // Determine amount to heal int nHeal; if (iMaxHP == 0) nHeal = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); else nHeal = iMinHP + Random(iMaxHP); // Apply the Healing effect effect eHeal = EffectHeal(nHeal); if (nHeal > 0) ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget); } } effect eVisual = EffectVisualEffect(VFX_IMP_RESTORATION_GREATER); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, oTarget); } // Searches the targets inventory for the item with the passed tag and returns the total count for that // item (including stacked items). int Inventory_CountItem(object oTarget, string strItemTag) { int iCount = 0; object oItem = GetFirstItemInInventory(oTarget); while (GetIsObjectValid(oItem)) { if (strItemTag == GetTag(oItem)) iCount += GetNumStackedItems(oItem); oItem = GetNextItemInInventory(oTarget); } return iCount; } // Deletes all items in the inventory of the container void Inventory_DestroyAllItems(object oTarget=OBJECT_SELF) { object oItem = GetFirstItemInInventory(oTarget); while (GetIsObjectValid(oItem)) { DestroyObject(oItem); oItem = GetNextItemInInventory(oTarget); } } // Returns the total number of items in the containers inventory int Inventory_ItemCount(object oTarget) { int iCount = 0; object oItem = GetFirstItemInInventory(oTarget); while (GetIsObjectValid(oItem)) { iCount++; oItem = GetNextItemInInventory(oTarget); } return iCount; } // Removes ALL instances of strItemTag from the Targets inventory!!! void Inventory_RemoveItem(object oTarget, string strItemTag) { object oItem = GetFirstItemInInventory(oTarget); while (GetIsObjectValid(oItem)) { if (strItemTag == GetTag(oItem)) DestroyObject(oItem); oItem = GetNextItemInInventory(oTarget); } } // Removes iTake instances of strItemTag from the Targets inventory. Should only be used on // unstackable items as it will take the entire stack!! int Inventory_RemoveItemNumber(object oTarget, string strItemTag, int iTake=1) { int iCount=0; object oItem = GetFirstItemInInventory(oTarget); while (GetIsObjectValid(oItem)) { if (strItemTag == GetTag(oItem)) { iCount++; DestroyObject(oItem); } if (iCount >= iTake) return(iCount); oItem = GetNextItemInInventory(oTarget); } return(iCount); } // Used on items that are stacked to take -some- items from the stack, but not // all. void Inventory_RemoveStackedItemQuantity(object oTarget, string strItemTag, int iRemoveNum) { object oItem = GetFirstItemInInventory(oTarget); int iCount = 0; int iTotalEmpties = 0; while ((iCount <= iRemoveNum) && GetIsObjectValid(oItem)) { if (GetTag(oItem) == strItemTag) { int iStackSize = GetNumStackedItems(oItem); iCount += iStackSize; if (iCount <= iRemoveNum) DestroyObject(oItem); else { int iGiveBack = iCount - iRemoveNum; AssignCommand(oTarget, DelayCommand(1.0, CreateItem(oTarget, strItemTag, iGiveBack))); DestroyObject(oItem); } } oItem = GetNextItemInInventory(oTarget); } } // Hurls down a flurry of lightning strikes! void Lightning_DivineFury(object oObject, float fRadius, int iTotalStrikes, int iDoesDamage=FALSE, float fDelayWindow=14.0) { effect eStrike = EffectVisualEffect(VFX_IMP_LIGHTNING_M); int iIndex=0; for (iIndex=0; iIndex < iTotalStrikes; iIndex++) { // Get a location near the effect center location lLoc = Location_GetLocationNearObject(oObject, fRadius, fRadius); float fDelay = 1.0 + Math_RandomFloat(fDelayWindow); AssignCommand(GetArea(oObject), DelayCommand(fDelay, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eStrike, lLoc))); if (iDoesDamage) Lightning_DamageCreatures(lLoc, 12, 3.5); } } // Damage all creatures within range of the strike (if they fail a save) void Lightning_DamageCreatures(location lStrike, int iDifficulty=15, float fRange=10.0, int iMaxDamage=26, int iMinDamage=6) { // Get the effects int iDamage = iMinDamage + Random(iMaxDamage - iMinDamage); effect eREALA = EffectDamage(iDamage, DAMAGE_TYPE_ELECTRICAL); effect eREALB = EffectKnockdown(); effect eREALC = EffectDeaf(); effect eREALD = EffectACDecrease(12); // Begin searching for nearby objects that can take damage object oObject = GetFirstObjectInShape(SHAPE_SPHERE, fRange, lStrike); while (oObject != OBJECT_INVALID) { // Only PC's get saving throws... because I like them =) if (GetIsPC(oObject)) { int iFortSave = GetFortitudeSavingThrow(oObject) + d20(); if (iFortSave >= iDifficulty) { SendMessageToPC(oObject, "Lightning Strike! Save (roll: " + IntToString(iFortSave) + ") vs. Fortitude (" + IntToString(iDifficulty) + ") *success*"); // If the PC was too close, takes partial damage anyway if (GetDistanceBetweenLocations(lStrike, GetLocation(oObject)) <= 1.0) { SendMessageToPC(oObject, "Too close to the lightning bolt!!!"); eREALA = EffectDamage(d6(3), DAMAGE_TYPE_ELECTRICAL); ApplyEffectToObject(DURATION_TYPE_INSTANT, eREALA, oObject); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eREALB, oObject, IntToFloat(d6())); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eREALC, oObject, IntToFloat(d20())); if (!GetIsPC(oObject)) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eREALD, oObject, IntToFloat(d10())); } oObject = GetNextObjectInShape(SHAPE_SPHERE, fRange, lStrike); continue; } SendMessageToPC(oObject, "Save (roll: " + IntToString(iFortSave) + ") vs. Fortitude (" + IntToString(iDifficulty) + ") *failure*"); } // Apply effects and damage to creatures ApplyEffectToObject(DURATION_TYPE_INSTANT, eREALA, oObject); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eREALB, oObject, IntToFloat(d10()+d4())); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eREALC, oObject, IntToFloat(d10()+d4())); oObject = GetNextObjectInShape(SHAPE_SPHERE, fRange, lStrike); } } // Creates a lightning strike in the target area at a random location. Recurses back into itself! // If any creatures are nearby, they must make a saving throw or suffer effects. void Lightning_DoStrike(int iLightning, object oSelf) { // Get the effect effect eVFX = EffectVisualEffect(iLightning); float fRange = GetLocalFloat(oSelf, "fLightningRange"); int iDifficulty = GetLocalInt(oSelf, "iLightningDC"); int iAreaSizeX = GetLocalInt(oSelf, "XSIZE"); // X-Size of the area int iAreaSizeY = GetLocalInt(oSelf, "YSIZE"); // Y-Size of the area int iRandmFrequency = GetLocalInt(oSelf, "iLightningRandFreq"); // Interval between strikes, randomized. int iConstFrequency = GetLocalInt(oSelf, "iLightningConstFreq"); // Interval between strikes - this is the minimum period. // Generate a random location to do a lightning strike location lStrike = Location_GetRandomLocation(GetArea(OBJECT_SELF), iAreaSizeX, iAreaSizeY); // Do a lightning strike visual effect and prepare additional effects if Saving Throws for nearby creatures are failed ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVFX, lStrike); // Check for nearby creatures Lightning_DamageCreatures(lStrike); // Do another lightning strike DelayCommand(IntToFloat(Random(iRandmFrequency) + iConstFrequency), Lightning_DoStrike(iLightning, oSelf)); // Strikes } // Entry function for the perpetual Lightning Strike system. The X and Y parameters must be the // size of the area the lightning will be striking inside of - that information can be found // in the Toolset. Place in the OnEnter of an Area with a Code-Block command. void Lightning_Initialize(object oSelf, int iXSize, int iYSize, int iLightningDC=15, float fLightningRange=6.0, int iLightningRandFreq=20, int iLightningConstFreq=9) { SetLocalInt(oSelf, "XSIZE", iXSize); SetLocalInt(oSelf, "YSIZE", iYSize); SetLocalInt(oSelf, "iLightningDC", iLightningDC); SetLocalFloat(oSelf, "fLightningRange", fLightningRange); SetLocalInt(oSelf, "iLightningRandFreq", iLightningRandFreq); SetLocalInt(oSelf, "iLightningConstFreq", iLightningRandFreq); Lightning_DoStrike(VFX_IMP_LIGHTNING_M, oSelf); } // Adds the vector data passed to the position passed and returns a location location Location_AddVectors(vector vCurrentPosition, float fVectorX, float fVectorY, float fVectorZ, object oObject=OBJECT_SELF) { float fVX = vCurrentPosition.x + fVectorX; float fVY = vCurrentPosition.y + fVectorY; float fVZ = vCurrentPosition.z + fVectorZ; vector vNewPosition = Vector(fVX, fVY, fVZ); return(Location(GetArea(oObject), vNewPosition, IntToFloat(Random(180)))); } // Uses the X and Y ranges passed to determine a random point near the waypoint supplied. location Location_GetLocationNearObject(object oObject, float fXRange=1.0, float fYRange=1.0) { // Get the position of the Waypoint vector vCenter = GetPosition(oObject); // Generate a new location nearby float fNewX = vCenter.x + (Math_RandomFloatSign() * Math_RandomFloat(fXRange)); float fNewY = vCenter.y + (Math_RandomFloatSign() * Math_RandomFloat(fYRange)); vector vNewPosition = Vector(fNewX, fNewY, vCenter.z); return(Location(GetArea(oObject), vNewPosition, IntToFloat(Random(180)))); } // Uses the X and Y ranges passed to determine a random point near the waypoint supplied. location Location_GetLocationNearWaypoint(string strWaypointTag, float fXRange=1.0, float fYRange=1.0) { // Get the position of the Waypoint object oWaypoint = GetObjectByTag(strWaypointTag); vector vCenter = GetPosition(oWaypoint); // Generate a new location nearby float fNewX = vCenter.x + (Math_RandomFloatSign() * Math_RandomFloat(fXRange)); float fNewY = vCenter.y + (Math_RandomFloatSign() * Math_RandomFloat(fYRange)); vector vNewPosition = Vector(fNewX, fNewY, vCenter.z); return(Location(GetArea(oWaypoint), vNewPosition, IntToFloat(Random(180)))); } // Returns a random location within the range of 0->iXSize and 0->iYSize location Location_GetRandomLocation(object oArea, int iXSize, int iYSize, int iXMargin=0, int iYMargin=0, int iHeight=0) { float fX = IntToFloat(Random(iXSize - iXMargin) + iXMargin); float fY = IntToFloat(Random(iYSize - iYMargin) + iYMargin); float fZ = IntToFloat(iHeight); vector vStrike = Vector(fX, fY, fZ); location lStrike = Location(oArea, vStrike, IntToFloat(Random(180))); return lStrike; } // Ensures that fValue is within range of (fPoint +/- fRange); if not, it will return a corrected // value that is. float Math_FitFloatToBoundaries(float fValue, float fPoint, float fRange) { // Check the value is in bounds if (fValue > fPoint + fRange) fValue = (fPoint + fRange) - Math_RandomFloat(0.5); if (fValue < fPoint - fRange) fValue = (fPoint - fRange) + Math_RandomFloat(0.5); // Return final value return fValue; } // Computes a random float value, including the decimal portion (2-digits only) float Math_RandomFloat(float fMaxValue) { float fWhole = fabs(fMaxValue); int iWhole = Random(FloatToInt(fWhole)); int iDecimal = FloatToInt(100.0 * (fMaxValue - fWhole)); if (iDecimal == 0) iDecimal = 100; float fDecimal = IntToFloat(Random(iDecimal)) / 100.0; float fFinal = IntToFloat(iWhole) + fDecimal; return fFinal; } // Returns a -1.0 or 1.0 which can be multiplied to another value to randomly determine the sign of the result. float Math_RandomFloatSign() { if (d100() > 50) return 1.0; return -1.0; } // Returns a -1 or 1 which can be multiplied to another value to randomly determine the sign of the result. int Math_RandomIntSign() { if (d100() > 50) return 1; return -1; } // Use this (called typically from OnSpawn) function to add a regular effect to an object permanently (a // minor distinction I make between an effect and a property is that properties are perm.) void Object_AddProperty(int iEffect, int iParamA=1, int iParamB=4, int iParamC=1, object oObject=OBJECT_SELF) { effect eFX; if (iEffect == EFFECT_TYPE_REGENERATE) eFX = EffectRegenerate(iParamA, IntToFloat(iParamB)); if (iEffect == EFFECT_TYPE_TEMPORARY_HITPOINTS) eFX = EffectTemporaryHitpoints(iParamA); if ((iEffect == EFFECT_TYPE_ABILITY_INCREASE || iEffect == EFFECT_TYPE_ABILITY_DECREASE) && iParamB > 0) eFX = EffectAbilityIncrease(iParamA, iParamB); if ((iEffect == EFFECT_TYPE_ABILITY_INCREASE || iEffect == EFFECT_TYPE_ABILITY_DECREASE) && iParamB < 0) eFX = EffectAbilityDecrease(iParamA, abs(iParamB)); if (iEffect == EFFECT_TYPE_TRUESEEING) eFX = EffectTrueSeeing(); ApplyEffectToObject(DURATION_TYPE_PERMANENT, eFX, oObject); } // Object_CheckDamageVsResistances() - Called from the obects 'On Damaged' slot. This function processes the results of a successful attack on a monster versus // the creatures resistances, which are passed in the params. Determines if the // (and how much) damage the creature takes and if it can be destroyed by the attack. // 1) Minimum HP's to allow going down // 2) Amount of Special Damage needed to effect creature. Will only count if creature is 'down'. // 3) When creature is down, only massive damage will effect it. Damage done by PC's is REDUCED by this amount. // 4) Period that creature will be down for // 5) Bitwise AND specifying ABSENT resistances as follows: // 1 DAMAGE_TYPE_ACID // 2 DAMAGE_TYPE_BLUDGEONING // 4 DAMAGE_TYPE_COLD // 8 DAMAGE_TYPE_DIVINE // 16 DAMAGE_TYPE_ELECTRICAL // 32 DAMAGE_TYPE_FIRE // 64 DAMAGE_TYPE_MAGICAL // 128 DAMAGE_TYPE_NEGATIVE // 256 DAMAGE_TYPE_PIERCING // 512 DAMAGE_TYPE_POSITIVE // 1024 DAMAGE_TYPE_SLASHING // 2048 DAMAGE_TYPE_SONIC // Specifying '41' indicates the creature IS NOT IMMUNE to Acid, Divine, or Fire damage, but is resistant // to all other types of damage. // 6) The final parameter passed is the object ID of the calling object. // void Object_CheckDamageVsResistances(int iDown, int iSpecDam, int iDReduction, float fDur, int iResistances, object oSelf=OBJECT_SELF) { // Test object for a knock-down if ((GetCurrentHitPoints(oSelf) <= iDown) && (!GetLocalInt(oSelf, "iDown"))) { // The creature is near death, put him down ClearAllActions(); SetLocalInt(oSelf , "iDown", TRUE); // Set flag on creature to avoid this code again until object gets up // Apply resistances if ((iResistances & 1) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_ACID, iDReduction), oSelf, fDur); if ((iResistances & 2) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_BLUDGEONING, iDReduction), oSelf, fDur); if ((iResistances & 4) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_COLD, iDReduction), oSelf, fDur); if ((iResistances & 8) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_DIVINE, iDReduction), oSelf, fDur); if ((iResistances & 16) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, iDReduction), oSelf, fDur); if ((iResistances & 32) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_FIRE, iDReduction), oSelf, fDur); if ((iResistances & 64) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_MAGICAL, iDReduction), oSelf, fDur); if ((iResistances & 128) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_NEGATIVE, iDReduction), oSelf, fDur); if ((iResistances & 256) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_PIERCING, iDReduction), oSelf, fDur); if ((iResistances & 512) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_POSITIVE, iDReduction), oSelf, fDur); if ((iResistances & 1024) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_SLASHING, iDReduction), oSelf, fDur); if ((iResistances & 2048) == FALSE) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDamageResistance(DAMAGE_TYPE_SONIC, iDReduction), oSelf, fDur); // Apply Knockdown FX ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oSelf, fDur); // Set delay for Knockdown to be lifted DelayCommand(fDur, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(GetMaxHitPoints()), oSelf, fDur)); DelayCommand(fDur, SetLocalInt(oSelf, "knee", 0)); } // If knocked down, we can apply special damages (those creature is NOT resistant too) and kill the critter if ((GetLocalInt(oSelf, "iDown") == 1)) { location lLoc = GetLocation(oSelf); // ON ACID DAMAGE Spew the object if (GetDamageDealtByType(DAMAGE_TYPE_ACID) >= iSpecDam) { ClearAllActions(); DelayCommand(0.3, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_CHUNK_GREEN_SMALL), lLoc,5.0)); DelayCommand(0.4, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_BLOOD_LRG_GREEN), lLoc,5.0)); DelayCommand(1.0, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_HIT_ACID), lLoc,5.0)); DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON BLUDGEON DAMAGE Smash the object if (GetDamageDealtByType(DAMAGE_TYPE_BLUDGEONING) >= iSpecDam) { ClearAllActions(); DelayCommand(0.3, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_BLOOD_LRG_RED), lLoc,5.0)); DelayCommand(0.4, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_CHUNK_BONE_MEDIUM), lLoc,5.0)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON COLD DAMAGE Freeze the object if (GetDamageDealtByType(DAMAGE_TYPE_COLD) >= iSpecDam) { ClearAllActions(); DelayCommand(0.3, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL), lLoc,5.0)); DelayCommand(0.4, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FROST_L), lLoc)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON DIVINE DAMAGE Strike the object if (GetDamageDealtByType(DAMAGE_TYPE_DIVINE) >= iSpecDam) { ClearAllActions(); DelayCommand(0.3, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DIVINE_STRIKE_FIRE), lLoc)); DelayCommand(0.4, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_BLOOD_LRG_RED), lLoc, 8.0)); DelayCommand(0.8, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DIVINE_STRIKE_HOLY), lLoc)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON ELECTRICAL DAMAGE Fry the object if (GetDamageDealtByType(DAMAGE_TYPE_ELECTRICAL) >= iSpecDam) { ClearAllActions(); DelayCommand(0.3, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_HIT_ELECTRICAL), lLoc)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON FIRE DAMAGE Immolate the object if (GetDamageDealtByType(DAMAGE_TYPE_FIRE) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_IMP_FLAME_M), lLoc, 5.5)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON MAGICAL DAMAGE Fireworks on the object if (GetDamageDealtByType(DAMAGE_TYPE_MAGICAL) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_BLOOD_REG_GREEN), lLoc, 4.5)); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_BLOOD_CRT_RED), lLoc, 5.5)); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_CHUNK_BONE_MEDIUM), lLoc, 6.0)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON NEGATIVE DAMAGE Neg effect if (GetDamageDealtByType(DAMAGE_TYPE_NEGATIVE) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_HIT_NEGATIVE), lLoc)); DelayCommand(1.8, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY), lLoc)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON PIERCE DAMAGE Do some gore if (GetDamageDealtByType(DAMAGE_TYPE_PIERCING) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL), lLoc, 5.5)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON POSITIVE DAMAGE Pos effect if (GetDamageDealtByType(DAMAGE_TYPE_POSITIVE) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_HIT_NEGATIVE), lLoc)); DelayCommand(1.8, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY), lLoc)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON SLASH DAMAGE Slash it up if (GetDamageDealtByType(DAMAGE_TYPE_PIERCING) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL), lLoc, 5.5)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } // ON SONIC if (GetDamageDealtByType(DAMAGE_TYPE_PIERCING) >= iSpecDam) { ClearAllActions(); DelayCommand(1.5, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HEAD_SONIC), lLoc)); DelayCommand(2.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(TRUE), oSelf)); } } } // Search the object's inventory and return true if the item passed was found inside. False otherwise... int Object_CheckInventoryForItem(string strResource, object oObject) { object oItem = GetFirstItemInInventory(); string strItemTag; while (GetIsObjectValid(oItem)) { strItemTag = GetTag(oItem); if (strItemTag == strResource) return (TRUE); oItem = GetNextItemInInventory(); } return (FALSE); } // Counts the number of objects that are of the same type as the caller from a specified center point int Object_CountSameAtLocation(object oObject, location lCenter, float fSize=5.0, int iShape=SHAPE_SPHERE) { string strName = GetName(oObject); int iCount = 0; // Search for similair objects object oFound = GetFirstObjectInShape(iShape, fSize, lCenter, TRUE, OBJECT_TYPE_CREATURE); while (GetIsObjectValid(oFound)) { if (strName == GetName(oFound)) iCount++; oFound = GetNextObjectInShape(iShape, fSize, lCenter, TRUE, OBJECT_TYPE_CREATURE); } // Return count return iCount; } // Create an object (similair to CreateObject()) - useful for creating objects with DelayCommand(). void Object_CreateObject(int nObjectType, string sTemplate, location lLocation, int bUseAppearAnimation=FALSE) { CreateObject(nObjectType, sTemplate, lLocation, bUseAppearAnimation); } // Called (usually from an OnSpawn) to determine if a creature is a regenerating monster void Object_EnableRegenerators(object oObject=OBJECT_SELF) { float fCR = GetChallengeRating(oObject); // Check for undead if (Object_IsClassAndRace(CLASS_TYPE_UNDEAD, oObject)) { if (fCR < 4.0) Object_AddProperty(EFFECT_TYPE_REGENERATE); if (fCR >= 4.0 && fCR < 6.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 1, 3); if (fCR == 6.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 1, 2); if (fCR >= 7.0 && fCR < 10.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 2, 3); if (fCR >= 10.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 2, 2); } // Check for trolls if (Object_IsClassAndRace(CLASS_TYPE_GIANT, OBJECT_SELF, FALSE, "troll")) { if (fCR <= 7.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 1, 2); else if (fCR < 9.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 2, 3); else if (fCR >= 9.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 3, 3); } // Check for lycanthropes if (Object_IsClassAndRace(CLASS_TYPE_SHAPECHANGER, oObject, FALSE, "were")) { if (fCR < 8.0) Object_AddProperty(EFFECT_TYPE_REGENERATE); if (fCR >= 8.0 && fCR < 14.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 1, 3); if (fCR >= 14.0) Object_AddProperty(EFFECT_TYPE_REGENERATE, 2, 4); } // Check for regular animals if (Object_IsClassAndRace(CLASS_TYPE_ANIMAL, oObject, TRUE)) Object_AddProperty(EFFECT_TYPE_REGENERATE, 1, 8); } // Causes the object calling the function to stop combat void Object_ForceEndCombat(object oAttacker) { AssignCommand(oAttacker, ClearAllActions()); AssignCommand(oAttacker, SurrenderToEnemies()); ClearPersonalReputation(oAttacker); } // Places iStack instances of strItemTag onto the object if the function has NOT already been called for // the object void Object_InitializeInventory(string strItemTag, int iStack=4, object oObject=OBJECT_SELF) { if (GetLocalInt(oObject, "iInventoryInit")) return; SetLocalInt(oObject, "iInventoryInit", TRUE); int i; for (i=0; i 0) { // If no Race is needed, return the level if (GetStringLength(strName) == 0) return iLevel; else { // Race is needed, so try and get a match from the objects given name. string strLName = GetStringLowerCase(strName); string strOName = GetStringLowerCase(GetName(oObject)); if (String_IsSubString(strOName, strLName)) return iLevel; } } } else { // iExclusive was TRUE, so creature can have ONLY levels in iClassType int iAllLevels = GetLevel(oObject); if (iAllLevels == iLevel) { // All Levels are in iClassType // If no Race is needed, return the level if (GetStringLength(strName) == 0) return iLevel; else { // Race is needed, so try and get a match from the objects given name. string strLName = GetStringLowerCase(strName); string strOName = GetStringLowerCase(GetName(oObject)); if (String_IsSubString(strOName, strLName)) return iLevel; } } } return FALSE; } // Checks to see if a PC is near the calling object, returns TRUE if a PC is within fDist. int Object_IsPCNear(object oObject=OBJECT_SELF, float fDist=20.0) { object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); float fDistance = GetDistanceBetween(oObject, oPC); if (fDistance < fDist) return TRUE; return FALSE; } // Returns the object inside the shape centered at lCenter that is the same species as the caller and is // closest than any other candidates. object Object_GetClosestSameInLocationShape(location lCenter, object oObject=OBJECT_SELF, float fSize=5.0, int iShape=SHAPE_SPHERE) { string strName = GetName(oObject); object oTemp; float fTemp; float fDistance = 1000.0; // Arbitrarily high (no meaning) // Search for similair objects object oFound = GetFirstObjectInShape(iShape, fSize, lCenter, TRUE, OBJECT_TYPE_CREATURE); while (GetIsObjectValid(oFound)) { if (strName == GetName(oFound)) { fTemp = GetDistanceBetween(oObject, oFound); if (fTemp < fDistance) { fDistance = fTemp; oTemp = oFound; } } oFound = GetNextObjectInShape(iShape, fSize, lCenter, TRUE, OBJECT_TYPE_CREATURE); } // Return nearest object return oTemp; } vector Object_GetDensityCenter(location lCenter, object oObject=OBJECT_SELF, float fSize=5.0, int iShape=SHAPE_SPHERE) { vector vAverage; vector vFinal; vector vFound; float fCount = 0.0; // Search for similair objects object oFound = GetFirstObjectInShape(iShape, fSize, lCenter, TRUE, OBJECT_TYPE_CREATURE); string strName = GetName(oObject); while (GetIsObjectValid(oFound)) { if (strName == GetName(oFound)) { fCount += 1.0; vFound = GetPosition(oFound); vAverage.x += vFound.x; vAverage.y += vFound.y; vAverage.z += vFound.z; } oFound = GetNextObjectInShape(iShape, fSize, lCenter, TRUE, OBJECT_TYPE_CREATURE); } vFinal.x = vAverage.x / fCount; vFinal.y = vAverage.y / fCount; vFinal.z = vAverage.z / fCount; return vFinal; } // Examines all the DC's for the object and returns a difficulty (a.k.a 'quality') rating. // Pass with iReturnRaw as TRUE and get the sum of all the object's DC's instead of a QUALITY_* constant. int Object_GetDifficulty(object oObject, int iReturnRaw=FALSE) { int iTotal = GetLockUnlockDC(oObject); iTotal += GetTrapDetectDC(oObject); iTotal += GetTrapDisarmDC(oObject); if (iReturnRaw) return iTotal; if (iTotal < 19) return FloatToInt(QUALITY_LOW); if (iTotal >= 19 && iTotal < 28) return FloatToInt(QUALITY_MEDIUM); return FloatToInt(QUALITY_HIGH); } // Returns the first object found, inside a circle of fDistance meters that is // of the same species as the object passed in. object Object_GetNearestSame(object oObject=OBJECT_SELF, float fDistance=5.0) { location lCenter = GetLocation(oObject); object oFound; object oNearest = GetFirstObjectInShape(SHAPE_SPHERE, fDistance, lCenter, TRUE, OBJECT_TYPE_CREATURE); while (GetIsObjectValid(oNearest)) { if ((GetName(oObject) == GetName(oNearest)) && (oObject != oNearest)) { oFound = oNearest; break; } oNearest = GetNextObjectInShape(SHAPE_SPHERE, fDistance, lCenter, TRUE, OBJECT_TYPE_CREATURE); } return oFound; } // Returns one of the OBJECT_TYPE_* constants for the object that was passed int Object_GetType(object oObject) { string strTag = GetStringLowerCase(GetName(oObject)); if (String_IsSubString(strTag, "barrel")) return OBJECT_TYPE_BARREL; if (String_IsSubString(strTag, "book")) return OBJECT_TYPE_BOOKS; if (String_IsSubString(strTag, "chest")) return OBJECT_TYPE_CHEST; if (String_IsSubString(strTag, "crate") || String_IsSubString(strTag, "box")) return OBJECT_TYPE_CRATE; if (String_IsSubString(strTag, "treasure")) return OBJECT_TYPE_GOLD_PILE; if (String_IsSubString(strTag, "loot")) return OBJECT_TYPE_LOOTBAG; if (String_IsSubString(strTag, "supply")) return OBJECT_TYPE_WIZARD_CABINET; return(OBJECT_TYPE_MISCELLANEOUS); } // Changes the creatures stats so that they are unique to the individual; changes are not large // enough to constitute differentiation to a 'new species'. void Object_GiveUniqueAbilities(object oObject=OBJECT_SELF) { // All creatures receive a MOD from their basic stats Object_AddProperty(EFFECT_TYPE_ABILITY_INCREASE, ABILITY_STRENGTH, Math_RandomIntSign() * d2()); Object_AddProperty(EFFECT_TYPE_ABILITY_INCREASE, ABILITY_CONSTITUTION, Math_RandomIntSign() * d2()); Object_AddProperty(EFFECT_TYPE_ABILITY_INCREASE, ABILITY_INTELLIGENCE, Math_RandomIntSign() * d2()); Object_AddProperty(EFFECT_TYPE_ABILITY_INCREASE, ABILITY_CHARISMA, Math_RandomIntSign() * d2()); Object_AddProperty(EFFECT_TYPE_ABILITY_INCREASE, ABILITY_WISDOM, Math_RandomIntSign() * d2()); Object_AddProperty(EFFECT_TYPE_ABILITY_INCREASE, ABILITY_DEXTERITY, Math_RandomIntSign() * d2()); Object_AddProperty(EFFECT_TYPE_TEMPORARY_HITPOINTS, d8(GetHitDice(OBJECT_SELF))); // Give critters unique hit points } // Usually called from the OnSpawn of a creature in order to change its faction to common (if it's an // animal) void Object_MakeAnimalsCommon(object oObject=OBJECT_SELF) { // Check to be sure the creature passed in was an animal if (!Object_IsClassAndRace(CLASS_TYPE_ANIMAL, oObject, TRUE)) return; //////////////////////////////////////////////////////////////////////////// // Creatures that are exempt from faction changes (remain hostile) ///////// //////////////////////////////////////////////////////////////////////////// // All dire creatures are hostile string strCritterName = GetStringLowerCase(GetName(oObject)); if (String_IsSubString(strCritterName, "dire") || String_IsSubString(strCritterName, "rat") || String_IsSubString(strCritterName, "badger")) return; // Check if the animal was a non-prey item. If so, change to Defender. string strAnimalName = GetName(oObject); if (!Object_IsPrey(oObject)) ChangeToStandardFaction(oObject, STANDARD_FACTION_DEFENDER); // No need to worry about 'prey' item factions since they're common by default } // Respawns the passed item onto the passed object but only if the object's inventory does not already // have an instance of the item. void Object_RespawnContents(string strResource, int iStack=1, object oObject=OBJECT_SELF) { if (Object_CheckInventoryForItem(strResource, oObject)) return; // If object already has special item in it, don't create a new one int i; for (i=0; i 1) return; // Check if the offering is accepted float fLevel = IntToFloat(GetLevel(oCloser)); fGold *= 0.15 + (0.01 * fLevel) + (0.005 * fLevel); float fOffering = IntToFloat(iOffering); if (fGold > fOffering) return; // Exit if the Shrine has been used successfully recently float fDelay = 1200.0 + Math_RandomFloat(1200.0); if (BlockMultiActivation(GetTag(oObject), oObject, fDelay)) return; // Take the offering and restore the item! Inventory_DestroyAllItems(oObject); object oNew = CreateItemOnObject(strItemTag, oObject); SetIdentified(oNew, TRUE); // Do lightning effect! Lightning_DivineFury(oObject, 10.0, 20); // Do the normal FX! effect eVFXC = EffectVisualEffect(VFX_IMP_HOLY_AID); // For shrine effect eVFXE = EffectVisualEffect(VFX_FNF_SCREEN_SHAKE); effect eVFXD = EffectVisualEffect(VFX_FNF_HOWL_ODD); // For shrine ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVFXE, GetLocation(oObject)); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFXC, oObject); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFXD, oObject); } // Gets the state of the lever specified int Placeable_Lever_GetState(object oLever) { return(GetLocalInt(oLever, "iState")); } // Switches the state of the lever that was pulled, stores the state variable // on it and returns that state to the caller. BE SURE to set the default state // of the Lever to DEACTIVATED from the toolset!!! int Placeable_Lever_SwitchState(object oLever) { int iState = GetLocalInt(oLever, "iState"); if (iState == TRUE) { iState = FALSE; PlayAnimation(ANIMATION_PLACEABLE_DEACTIVATE); AssignCommand(oLever, SpeakString("Off")); } else { iState = TRUE; PlayAnimation(ANIMATION_PLACEABLE_ACTIVATE); AssignCommand(oLever, SpeakString("On")); } // Store the lever's state SetLocalInt(oLever, "iState", iState); return iState; } // Allows the player a chance to revive if a temple is not in range void Player_HeartBeat_Raise(object oPC, float fHeartBeat=6.0, int iReviveDC = 25) { int iFortSave = GetFortitudeSavingThrow(oPC) + d20(); int iWillSave = GetWillSavingThrow(oPC) + d20(); int iDeathDC = 23; int iWillDC = 24; if (GetLevel(oPC) < 5) { iDeathDC -= 8; iWillDC -= 7; } else if (GetLevel(oPC) < 10) { iDeathDC -= 4; iWillDC -= 3; } else if (GetLevel(oPC) < 15) { iDeathDC -= 2; iWillDC -=1; } if (iFortSave >= iDeathDC) { SendMessageToPC(oPC, "Save (roll: " + IntToString(iFortSave) + ") vs. Death (" + IntToString(iDeathDC) + ") *success*"); if (iWillSave >= iWillDC) { SendMessageToPC(oPC, "Save (roll: " + IntToString(iWillSave) + ") vs. Will (" + IntToString(iWillDC) + ") *success*"); Player_RestoreToLife(oPC); return; } else SendMessageToPC(oPC, "Save (roll: " + IntToString(iWillSave) + ") vs. Will (" + IntToString(iWillDC) + ") *failure*"); } else SendMessageToPC(oPC, "Save (roll: " + IntToString(iFortSave) + ") vs. Death (" + IntToString(iDeathDC) + ") *failure*"); AssignCommand(oPC, DelayCommand(fHeartBeat, Player_HeartBeat_Revive(oPC))); } // Called from the modules OnPlayerDeath event. In case the PC is out of range of a Temple and cannot // respawn, he should have the chance of reviving in time void Player_HeartBeat_Revive(object oPC, float fHeartBeat=6.0, int iReviveDC = 25) { // If PC is alive, stop checking his saving throws!!!! if (!GetIsDead(oPC)) return; // Adjust the DC for PC's level int iLevel = GetLevel(oPC); if (iLevel < 5) iReviveDC -= 10; else if (iLevel < 10) iReviveDC -= 5; else if (iLevel < 15) iReviveDC -= 2; // Make the saving throw for life! int iSave = GetFortitudeSavingThrow(oPC) + d20(); if (iSave > iReviveDC) { SendMessageToPC(oPC, "Save (roll: " + IntToString(iSave) + ") vs. Fortitude (" + IntToString(iReviveDC) + ") *success*"); FloatingTextStringOnCreature("Your heart has begun to pump again... faintly...", oPC); effect eFX = EffectRegenerate(d4(), 1.0); ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFX, oPC, fHeartBeat); AssignCommand(oPC, DelayCommand(fHeartBeat, Player_HeartBeat_Raise(oPC, fHeartBeat, iReviveDC))); } else AssignCommand(oPC, DelayCommand(fHeartBeat, Player_HeartBeat_Revive(oPC, fHeartBeat, iReviveDC))); } // Raise the PC void Player_RestoreToLife(object oPC) { FloatingTextStringOnCreature("The fates are with you! Your heart refuses to quit and you regain consciousness!", oPC); effect eFX = EffectResurrection(); ApplyEffectToObject(DURATION_TYPE_INSTANT, eFX, oPC); eFX = EffectHeal(d12()); AssignCommand(GetArea(oPC), DelayCommand(2.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eFX, oPC))); } // Returns the default portal tag for the module string Recall_GetDefaultPortalTag() { return(GetLocalString(GetModule(), "strDefaultPortal")); } // Called from Recall_IsRecall() in order to determine the location to recall a PC to for an area string Recall_GetPortalTag(object oArea=OBJECT_SELF) { string strRecallPortal = GetLocalString(oArea, "strRecallPortal"); if (GetStringLength(strRecallPortal) < 1) { // Check if a default portal is available if (Recall_IsDefaultPortalActive()) { if (!Recall_IsDefaultPortalBlocked()) { // Check too see if area is blocking the default portal strRecallPortal = Recall_GetDefaultPortalTag(); } } } return(strRecallPortal); } // Called in the OnEnter event for an area to set the location tag that will be used for recalls void Recall_InitializeArea(string strRecallPortal, string strDefaultPortal="NULL", object oArea=OBJECT_SELF) { // Check to make sure that the portal tag string passed in was valid object oPortal = GetObjectByTag(strRecallPortal); if (!GetIsObjectValid(oPortal)) return; // Set the recall variables for the area if all is good SetLocalInt(oArea, "iRecallActive", TRUE); SetLocalString(oArea, "strRecallPortal", strRecallPortal); // If a default portal was passed in, set it for the module if (strDefaultPortal != "NULL") Recall_SetDefaultPortal(strDefaultPortal); } // Called to determine if the recall stone is active for the given area int Recall_IsAreaActive(object oArea=OBJECT_SELF) { if (GetLocalInt(oArea, "iRecallActive")) { return TRUE; // Yes, area was initialized } if (Recall_IsDefaultPortalActive() && !Recall_IsDefaultPortalBlocked(oArea)) return TRUE; // Yes, area was initialized with a default portal and is not blocked return FALSE; // No, area not initialized } // Checks too see if there is a default portal int Recall_IsDefaultPortalActive() { return(GetLocalInt(GetModule(), "iDefaultActive")); } // If the default portal is active for the module, an area can see if it's blocked (PCs are not permitted to // transport out) by calling this function. int Recall_IsDefaultPortalBlocked(object oArea=OBJECT_SELF) { return(GetLocalInt(oArea, "iBlockValue")); } // Called from the modules OnActivateItem script to determine if the item used was a Recall Stone // Hope you kept it from the original campaign, fucka! When adding a portal to an area, be sure to // use the CUSTOM | VISUAL EFFECTS | Portal placeable and to give it a UNIQUE name! int Recall_IsRecall() { if (GetTag(GetItemActivated()) == "NW_IT_RECALL") { object oPC = GetItemActivator(); object oArea = GetArea(oPC); // Check the area's Stone global too see if player can transport from the area if (!Recall_IsAreaActive(oArea)) { // No idea what the following is, so just leave it... couldn't find the 'string table' AssignCommand(GetItemActivator(), ActionSpeakStringByStrRef(10611)); return TRUE; } // Continue with transport string strPortal = Recall_GetPortalTag(oArea); object oPortal = GetObjectByTag(strPortal); if (GetIsObjectValid(oPortal) == TRUE) { // Set the last used location flag and value SetLocalInt(GetItemActivator(), "NW_L_USED_RECALL", 1); SetLocalLocation(GetItemActivator(), "NW_L_LOC_RECALL", GetLocation(GetItemActivator())); // Carry out the transport command AssignCommand(oPC, ClearAllActions()); AssignCommand(oPC, PlaySound("as_mg_telepout1")); AssignCommand(oPC, JumpToLocation(GetLocation(oPortal))); AssignCommand(oPC, ActionDoCommand(ClearAllActions())); return TRUE; } else { // Error string, no temple is nearby! AssignCommand(GetItemActivator(), ActionSpeakStringByStrRef(10614)); return TRUE; } } // end If (GetTag()) return FALSE; } // Called once from an area's OnEnter slot to set whether the Stone of Recall can be used in that area. // Normally, this is used to block retrieval to the Default Recall Portal for the module, set with // Recall_SetDefaultPortal(). void Recall_SetAreaBlocked(int iBlockValue=TRUE, object oArea=OBJECT_SELF) { SetLocalInt(oArea, "iBlockValue", iBlockValue); } // Called from a modules OnModuleLoad or once from the OnClientEnter events to allow one portal // to be associated with all areas of the module. The portal assigned to an area with Recall_InitializeArea() // takes precedence over the default portal. void Recall_SetDefaultPortal(string strDefaultPortal) { SetLocalString(GetModule(), "strDefaultPortal", strDefaultPortal); SetLocalInt(GetModule(), "iDefaultActive", TRUE); } // Adds iValue to the Item Tag passed in - assumes there are suppossed to be 3 // digits trailing!!!! string String_AddDigits(string strItemTag, int iValue, int iDigits=2) { string strDigits = IntToString(iValue); int iIndex; for (iIndex=GetStringLength(strDigits); iIndex 0) AssignCommand(GetArea(oUndead), DelayCommand(fFrequency, Sunlight_DamageUndead(oUndead, iMaxDamage, iMinDamage, fFrequency, iRepeats))); } void Sunlight_DestroyUndead(object oUndead) { effect eVFX = EffectVisualEffect(VFX_IMP_FLAME_M); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oUndead); DestroyObject(oUndead, 0.5); } // Called from Sunlight_CheckLight() to kill all Undead in the area that was passed. Any undead found // will explode into flames!!!! void Sunlight_DestroyUndeadInArea(object oArea) { object oObject = GetFirstObjectInArea(oArea); effect eVFX = EffectVisualEffect(VFX_IMP_FLAME_M); // Find a zombie! while (GetIsObjectValid(oObject)) { if (Object_IsClassAndRace(CLASS_TYPE_UNDEAD, oObject)) AssignCommand(oArea, DelayCommand(6.0 + IntToFloat(d10()), Sunlight_DestroyUndead(oObject))); oObject = GetNextObjectInArea(oArea); } } // Calculates the fuel left remaining on the torch. Does not stop executing until // the PC leaves the game! void TorchLight_Heartbeat(object oPC, float fHeartBeat, float fBurnLimit, float fBurnRate) { int iHoldingTorchNow = TorchLight_IsHoldingTorch(oPC); float fTorchFuelRemaining = GetLocalFloat(oPC, "fTorchFuelRemaining"); // If torch is depleted, destroy it and reset the Fuel value to the BurnLimit if (fTorchFuelRemaining <= 1.0) { object oTorch = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); string strItemTag = GetTag(oTorch); SetLocalFloat(oPC, "fTorchFuelRemaining", fBurnLimit); if (strItemTag == "NW_IT_TORCH001") { DestroyObject(oTorch, 1.5); FloatingTextStringOnCreature("My torch has run out of fuel...", oPC); } } // Check to see if the PC is using a torch... if (iHoldingTorchNow) { fTorchFuelRemaining -= fBurnRate; // Consume some fuel for the torch SetLocalFloat(oPC, "fTorchFuelRemaining", fTorchFuelRemaining); } // Check what the PC's status is again in fHeartBeat seconds AssignCommand(oPC, DelayCommand(fHeartBeat, TorchLight_Heartbeat(oPC, fHeartBeat, fBurnLimit, fBurnRate))); } // Initializes the Torch Light System on the PC that has just entered the game. // Care should be taken to ensure the object specified is, in fact, a PC. The burn limit is // set to 2880, which is 48 minutes Real time given that the burn rate stays at 6.0. 48 minutes // Real time is about equal to 1 day in the game. This is a *generous* setting... void TorchLight_Initialize(object oPC, float fHeartBeat=6.0, float fBurnLimit=2880.0, float fBurnRate=6.0) { AssignCommand(oPC, DelayCommand(fHeartBeat, TorchLight_Heartbeat(oPC, fHeartBeat, fBurnLimit, fBurnRate))); } // Returns TRUE if the PC is holding his torch, FALSE otherwise. int TorchLight_IsHoldingTorch(object oPC) { object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); string strItemTag = GetTag(oItem); if (strItemTag == "NW_IT_TORCH001") return TRUE; return FALSE; } // Places the custom meat item on the body once it's dead int Treasure_Death_CreateMeat(int iProb=45, int iMax=3) { if (Random(100) <= iProb) return (Random(iMax)+1); return (0); } // Returns TRUE if the item was placed, false if not int Treasure_Death_DecideOnBodyParts(int nPercentChance, int nQuant, string strTemplate, object oMonster) { if (nQuant == 0) nQuant = 1; int nRand; int nResult = FALSE; int nIndex; for (nIndex=0; nIndex 40 && iType <= 70) strItemTag = "cu_recip"; if (iType > 70 && iType <= 90) strItemTag = "cu_book"; if (iType > 90) strItemTag = "cu_scroll"; // Determine the Level of the item if (iQuality == TREASURE_LOW) { if (strItemTag == "cu_recip") iMAX = MAX_RECIPES_LOW; if (strItemTag == "cu_book") iMAX = MAX_BOOKS_LOW; if (strItemTag == "cu_scroll") iMAX = MAX_SCROLLS_LOW; strItemTag += "0"; strFinal = String_AddDigits(strItemTag, Random(iMAX)); } if (iQuality == TREASURE_MEDIUM) { if (strItemTag == "cu_recip") iMAX = MAX_RECIPES_MEDIUM; if (strItemTag == "cu_book") iMAX = MAX_BOOKS_MEDIUM; if (strItemTag == "cu_scroll") iMAX = MAX_SCROLLS_MEDIUM; strItemTag += "1"; strFinal = String_AddDigits(strItemTag, Random(iMAX)); } if (iQuality == TREASURE_HIGH) { if (strItemTag == "cu_recip") iMAX = MAX_RECIPES_HIGH; if (strItemTag == "cu_book") iMAX = MAX_BOOKS_HIGH; if (strItemTag == "cu_scroll") iMAX = MAX_SCROLLS_HIGH; strItemTag += "2"; strFinal = String_AddDigits(strItemTag, Random(iMAX)); } CreateItemOnObject(strFinal, oContainer); } void Treasure_GenerateCustomTreasure(object oContainer, int iWealth=0) { // Default treasure is from Low, Med, or High int iRand; string strItemTag; int iStack; int iProb = d12(); // Probability of a certain category of item to come up if (iWealth == TREASURE_LOW) iProb = 1; // Treasure is low category if (iWealth == TREASURE_MEDIUM) iProb = 8; // Treasure is medium category if (iWealth == TREASURE_HIGH) iProb = 10; // Treasure is high category // Select an item category iRand = Random(15); // Purposely high!!! if (iProb <= 6) { // Potions if (iRand == 0) { iStack = Random(3); strItemTag = String_AddDigits("cu_potion0", Random(MAX_POTIONS_LOW)); } // Scrolls if (iRand == 1) { iStack = Random(2); strItemTag = String_AddDigits("cu_scroll0", Random(MAX_SCROLLS_LOW)); } // Reagents if (iRand == 2) { iStack = Random(5); strItemTag = String_AddDigits("cu_reag0", Random(MAX_REAGENTS_LOW)); } // Ammunitions if (iRand == 3) { iStack = Random(50); strItemTag = String_AddDigits("cu_ammo0", Random(MAX_AMMO_LOW)); } // Armors if (iRand == 4) { iStack = Random(2); strItemTag = String_AddDigits("cu_armor0", Random(MAX_ARMOR_LOW)); } // Books if (iRand == 5) { iStack = Random(2); strItemTag = String_AddDigits("cu_book0", Random(MAX_BOOKS_LOW)); } // Clothing if (iRand == 6) { iStack = Random(2); strItemTag = String_AddDigits("cu_cloth0", Random(MAX_CLOTHING_LOW)); } // Jewelry if (iRand == 7) { iStack = Random(2); strItemTag = String_AddDigits("cu_jewel0", Random(MAX_JEWELRY_LOW)); } // Notes if (iRand == 8) { iStack = Random(2); strItemTag = String_AddDigits("cu_note0", Random(MAX_ALCHEM_NOTES-7)); } // Recipes if (iRand == 9) { iStack = Random(3); strItemTag = String_AddDigits("cu_recip0", Random(MAX_RECIPES_LOW)); } // Weapons if (iRand == 10) { iStack = Random(2); strItemTag = String_AddDigits("cu_weap0", Random(MAX_WEAPONS_LOW)); } } else if (iProb <= 9) { // Potions if (iRand == 0) { iStack = Random(2); strItemTag = String_AddDigits("cu_potion1", Random(MAX_POTIONS_MEDIUM)); } // Scrolls if (iRand == 1) { iStack = Random(2); strItemTag = String_AddDigits("cu_scroll1", Random(MAX_SCROLLS_MEDIUM)); } // Reagents if (iRand == 2) { iStack = Random(3); strItemTag = String_AddDigits("cu_reag1", Random(MAX_REAGENTS_MEDIUM)); } // Ammunitions if (iRand == 3) { iStack = Random(15); strItemTag = String_AddDigits("cu_ammo1", Random(MAX_AMMO_MEDIUM)); } // Armors if (iRand == 4) { iStack = Random(2); strItemTag = String_AddDigits("cu_armor1", Random(MAX_ARMOR_MEDIUM)); } // Books if (iRand == 5) { iStack = Random(2); strItemTag = String_AddDigits("cu_book1", Random(MAX_BOOKS_MEDIUM)); } // Clothing if (iRand == 6) { iStack = Random(2); strItemTag = String_AddDigits("cu_cloth1", Random(MAX_CLOTHING_MEDIUM)); } // Jewelry if (iRand == 7) { iStack = Random(2); strItemTag = String_AddDigits("cu_jewel1", Random(MAX_JEWELRY_MEDIUM)); } // Notes if (iRand == 8) { iStack = Random(2); strItemTag = String_AddDigits("cu_note1", Random(MAX_ALCHEM_NOTES-4)); } // Recipes if (iRand == 9) { iStack = Random(2); strItemTag = String_AddDigits("cu_recip1", Random(MAX_RECIPES_MEDIUM)); } // Weapons if (iRand == 10) { iStack = Random(2); strItemTag = String_AddDigits("cu_weap1", Random(MAX_WEAPONS_MEDIUM)); } // Misc Items if (iRand == 11) Treasure_GenerateMiscItem(oContainer); // Generate Poison if (iRand == 12) Treasure_GeneratePoisonPotion(oContainer); } else { // Potions if (iRand == 0) { iStack = Random(2); strItemTag = String_AddDigits("cu_potion2", Random(MAX_POTIONS_HIGH)); } // Scrolls if (iRand == 1) { iStack = Random(2); strItemTag = String_AddDigits("cu_scroll2", Random(MAX_SCROLLS_HIGH)); } // Reagents if (iRand == 2) { iStack = Random(2); strItemTag = String_AddDigits("cu_reag2", Random(MAX_REAGENTS_HIGH)); } // Ammunitions if (iRand == 3) { iStack = Random(8); strItemTag = String_AddDigits("cu_ammo2", Random(MAX_AMMO_HIGH)); } // Armors if (iRand == 4) { iStack = Random(2); strItemTag = String_AddDigits("cu_armor2", Random(MAX_ARMOR_HIGH)); } // Books if (iRand == 5) { iStack = Random(2); strItemTag = String_AddDigits("cu_book2", Random(MAX_BOOKS_HIGH)); } // Clothing if (iRand == 6) { iStack = Random(2); strItemTag = String_AddDigits("cu_cloth2", Random(MAX_CLOTHING_HIGH)); } // Jewelry if (iRand == 7) { iStack = Random(2); strItemTag = String_AddDigits("cu_jewel2", Random(MAX_JEWELRY_HIGH)); } // Notes if (iRand == 8) { iStack = Random(3); strItemTag = String_AddDigits("cu_note2", Random(MAX_ALCHEM_NOTES)); } // Recipes if (iRand == 9) { iStack = Random(2); strItemTag = String_AddDigits("cu_recip2", Random(MAX_RECIPES_HIGH)); } // Weapons if (iRand == 10) { iStack = Random(2); strItemTag = String_AddDigits("cu_weap2", Random(MAX_WEAPONS_HIGH)); } // Misc Items if (iRand == 11) Treasure_GenerateMiscItem(oContainer); } // Create the object in inventory // NOTE! iStack -can- be ZERO (by design), which will cause the creation to fail! CreateItemOnObject(strItemTag, oContainer, iStack); } // Create a custom-misc item on the container object Treasure_GenerateMiscItem(object oContainer) { int iRand = Random(MAX_MISCELLANEOUS); string strItemTag = "INVALID"; int iStack = 0; if (iRand == 0) if (RollChance(6)) { strItemTag = "AlchemistsEquipment"; iStack = 1; } if (iRand == 1) if (RollChance(8)) { strItemTag = "ScribesKit"; iStack = 1; } if (iRand == 2) if (RollChance(85)) { strItemTag = "BlankParchment"; iStack = d8(); } if (iRand == 3) if (RollChance(3)) { strItemTag = "BucknardsEverfullPurse"; iStack = 1; } if (iRand == 4) if (RollChance(90)) { strItemTag = "EmptyVile"; iStack = d10(); } if (iRand == 5) if (RollChance(12)) { strItemTag = "FigurineofWonderousPower"; iStack = 1; } if (iRand == 6) if (RollChance(12)) { strItemTag = "figurineofwon002"; iStack = 1; } if (iRand == 7) if (RollChance(12)) { strItemTag = "figurineofwonder"; iStack = 1; } if (iRand == 8) if (RollChance(60)) { strItemTag = "EmptyBottle"; iStack = d10(); } if (iRand == 8) if (RollChance(5)) { strItemTag = "ThievesTools"; iStack = 1; } return(CreateItemOnObject(strItemTag, oContainer, iStack)); } // Creates a book item (book or scroll) from the stock NWN items void Treasure_GenerateNWNBook(object oContainer, object oLastOpener) { if (d100() < 60) CreateBook(oContainer); else CreateArcaneScroll(oContainer, oLastOpener); } // Create a Poison in the container object Treasure_GeneratePoisonPotion(object oContainer) { string strPoison; object oPoison; strPoison = String_AddDigits("cu_potion0", Random(MAX_POISONS)); if (60 <= Random(100)) oPoison = CreateItemOnObject(strPoison, oContainer, d4()); // 40% chance of creating a poison potion to make them fairly rare return (oPoison); } // Create a custom potion in the container void Treasure_GeneratePotion(object oContainer, object oAdventurer, int iQuality) { string strItemTag; if (iQuality == TREASURE_LOW) strItemTag = String_AddDigits("cu_potion0", Random(MAX_POTIONS_LOW)); if (iQuality == TREASURE_MEDIUM) strItemTag = String_AddDigits("cu_potion1", Random(MAX_POTIONS_MEDIUM)); if (iQuality == TREASURE_HIGH) strItemTag = String_AddDigits("cu_potion2", Random(MAX_POTIONS_HIGH)); CreateItemOnObject(strItemTag, oContainer); } // Create a reagent item in the container void Treasure_GenerateReagent(object oContainer, int iQuality) { string strReagent = "cu_reag"; if (iQuality == TREASURE_LOW) { strReagent += "0"; strReagent = String_AddDigits(strReagent, Random(MAX_REAGENTS_LOW)); }; if (iQuality == TREASURE_MEDIUM) { strReagent += "1"; strReagent = String_AddDigits(strReagent, Random(MAX_REAGENTS_MEDIUM)); }; if (iQuality == TREASURE_HIGH) { strReagent += "2"; strReagent = String_AddDigits(strReagent, Random(MAX_REAGENTS_HIGH)); }; CreateItemOnObject(strReagent, oContainer); } // Generate Custom and Standard treasure on the object with the information passed void Treasure_GenerateTreasure(object oObject, object oLastOpener, int iQuality, int iType) { // Determine difficulty of the object relative to the PC's level int iIndex=0; int iPCLevel = GetLevel(oLastOpener); int iChance = iPCLevel * 3; float iQAdj = IntToFloat(iQuality) * ((100.0 - IntToFloat(iPCLevel))/100.0); if (iType == OBJECT_TYPE_BARREL) if (d100() < (80 - iPCLevel)) GenerateLowTreasure(oLastOpener, oObject); if (iType == OBJECT_TYPE_BOOKS) { if (d100() >= 70 - iPCLevel) Treasure_GenerateNWNBook(oObject, oLastOpener); if (iQAdj <= QUALITY_LOW) Treasure_GenerateBook(oObject, oLastOpener, TREASURE_LOW); if (iQAdj > QUALITY_LOW && iQAdj <= QUALITY_MEDIUM) Treasure_GenerateBook(oObject, oLastOpener, TREASURE_MEDIUM); if (iQAdj > QUALITY_MEDIUM) Treasure_GenerateBook(oObject, oLastOpener, TREASURE_HIGH); } else if (iType == OBJECT_TYPE_GOLD_PILE) { if (iQAdj <= QUALITY_LOW) Treasure_GenerateValuable(oObject, oLastOpener, TREASURE_LOW); if (iQAdj > QUALITY_LOW && iQAdj <= QUALITY_MEDIUM) for (iIndex=0; iIndex QUALITY_MEDIUM) for (iIndex=0; iIndex QUALITY_LOW && iQAdj <= QUALITY_MEDIUM) for (iIndex=0; iIndex QUALITY_MEDIUM) for (iIndex=0; iIndex QUALITY_LOW && iQAdj <= QUALITY_MEDIUM) for (iIndex=0; iIndex QUALITY_MEDIUM) for (iIndex=0; iIndex QUALITY_LOW && iQAdj <= QUALITY_MEDIUM) { GenerateMediumTreasure(oLastOpener, oObject); iStack = d3(); iMod = TREASURE_MEDIUM; } if (iQAdj > QUALITY_MEDIUM) { GenerateHighTreasure(oLastOpener, oObject); iStack = d6(); iMod = TREASURE_HIGH; } // Include additional treasure? int iTreasure = Random(13) + 1; int i=0; for (i=0; i= 40 && iType < 70) CreateGold(oContainer, oAdventurer, iQuality); if (iType >= 70 && iType < 95) CreateGem(oContainer, oAdventurer, iQuality); if (iType >= 95) CreateJewel(oContainer, oAdventurer, iQuality); } // Create Reagents, Books, Potions, Gems or Jewels in the container void Treasure_GenerateWizardSupplies(object oContainer, object oAdventurer, int iQuality) { int iType = d100(); int iIndex; int iMaxReag; if (iQuality == TREASURE_LOW) iMaxReag = d6(); if (iQuality == TREASURE_MEDIUM) iMaxReag = d12(); if (iQuality == TREASURE_HIGH) iMaxReag = d20(); if (iType < 80) for (iIndex=0; iIndex= 80 && iType < 82) Treasure_GenerateNWNBook(oContainer, oAdventurer); if (iType >= 82 && iType < 90) Treasure_GenerateBook(oContainer, oAdventurer, iQuality); if (iType >= 90 && iType < 93) CreatePotion(oContainer, oAdventurer, iQuality); if (iType >= 93 && iType < 96) Treasure_GeneratePotion(oContainer, oAdventurer, iQuality); if (iType >= 96 && iType < 98) CreateGem(oContainer, oAdventurer, iQuality); if (iType >= 98) CreateJewel(oContainer, oAdventurer, iQuality); } // When a character enters the module and is low-level, his gold should be converted // to copper with consideration given to his character class. Called from the modules // OnClientEnter() event slot. void Treasure_GoldForClassAdjust(object oTarget) { // Convert gold to copper if at a low level int iLevel = GetLevel(oTarget); int iGold = GetGold(oTarget); if (iGold > 100) return; if (iLevel > 2) return; // Award more gold depending on class int iMaxClassValue; if (GetLevelByClass(CLASS_TYPE_BARBARIAN, oTarget)) iMaxClassValue = 200; else if (GetLevelByClass(CLASS_TYPE_BARD, oTarget)) iMaxClassValue = 400; else if (GetLevelByClass(CLASS_TYPE_CLERIC, oTarget)) iMaxClassValue = 500; else if (GetLevelByClass(CLASS_TYPE_DRUID, oTarget)) iMaxClassValue = 200; else if (GetLevelByClass(CLASS_TYPE_FIGHTER, oTarget)) iMaxClassValue = 750; else if (GetLevelByClass(CLASS_TYPE_MONK, oTarget)) iMaxClassValue = 100; else if (GetLevelByClass(CLASS_TYPE_PALADIN, oTarget)) iMaxClassValue = 150; else if (GetLevelByClass(CLASS_TYPE_RANGER, oTarget)) iMaxClassValue = 100; else if (GetLevelByClass(CLASS_TYPE_ROGUE, oTarget)) iMaxClassValue = 500; else if (GetLevelByClass(CLASS_TYPE_SORCERER, oTarget)) iMaxClassValue = 200; else if (GetLevelByClass(CLASS_TYPE_WIZARD, oTarget)) iMaxClassValue = 750; else iMaxClassValue = 550; // Give the new gold amount in copper int iCopper = iGold * 25 + Random(iMaxClassValue); // ~ 1/4 of the Gold -> Copper exchange rate AssignCommand(oTarget, GiveGoldToCreature(oTarget, iCopper)); } // Returns the equivalent value of the passed coin in gold. Outputs the amount of 'change' // PC has on his person. int Treasure_RollCoins(object oPC, object oItem, float fExchangeRate) { int iStack = GetNumStackedItems(oItem); if (iStack > 1) iStack++; // Get the PC's change for this coin type string strCoinTag = GetStringLowerCase(GetTag(oItem)); float fCoins = IntToFloat(GetLocalInt(oPC, strCoinTag)); // Add the current change to the new change fCoins += IntToFloat(iStack); // Convert to gold with exchange rate int iGoldCoins = 0; while (fCoins >= fExchangeRate) { iGoldCoins++; fCoins -= fExchangeRate; } // Store remaining change SetLocalInt(oPC, strCoinTag, FloatToInt(fCoins)); // Output remaining change string strCoinName; if (strCoinTag == "cu_gold001") strCoinName = "copper"; if (strCoinTag == "cu_gold002") strCoinName = "silver"; if (strCoinTag == "cu_gold003") strCoinName = "electrum"; if (strCoinTag == "cu_gold004") strCoinName = "gold"; if (strCoinTag == "cu_gold005") strCoinName = "platinum"; SendMessageToPC(oPC, "Your " + strCoinName + " was worth " + IntToString(iGoldCoins) + " gold pieces. You have " + IntToString(FloatToInt(fCoins)) + " " + strCoinName + " pieces remaining in change."); // Return the amount that was exchanged to gold return iGoldCoins; } // Entry function for treasure generating library. Place in the OnOpen() of a container. void Treasure_StockContainerByDCAndRespawn(object oObject) { // Run code only once if (BlockCodeExecution(oObject, "NW_DO_ONCE")) return; // Store the tags of any items in the chest so they can be recalled later if need string strInventoryArray = Object_StoreSpecialInventory(); // Determine information needed to create treasure object oLastOpener = GetLastOpener(); // PC's level int iContainerQuality = Object_GetDifficulty(oObject, TRUE); // Difficulty of container int iContainerType = Object_GetType(oObject); // The type of the container // Place the treasure Treasure_GenerateTreasure(oObject, oLastOpener, iContainerQuality, iContainerType); // Respawn the chest on a delay float fRespawnDelay = 1200.0 + IntToFloat(iContainerQuality*40); Respawn_ByResrefWithDelay(oObject, fRespawnDelay, strInventoryArray); // Finish up ShoutDisturbed(); } // The OnUse code for a Ballista that can fire fireballs, level 6 void Weapon_FireBallista(object oWeapon, object oPC, int iPower=6) { // Find nearest hostile within range object oCreature = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC); if (GetIsObjectValid(oCreature)) { if (60 < GetStandardFactionReputation(STANDARD_FACTION_HOSTILE, oCreature)) { float iDist = GetDistanceToObject(oCreature); if (iDist < 35.0) { if (BlockMultiActivation("iFired", oWeapon, 2.0, "Waiting for reload!!")) return; ActionCastSpellAtObject(SPELL_FIREBALL, oCreature, METAMAGIC_ANY, TRUE, iPower); } } } else AssignCommand(oPC, SpeakString("No enemies in range!")); }