Files
Anphillia_PRC8/_module/nss/chr_inc.nss
Jaysyn904 00d2c183e8 Update for PRC8 parity
Update for PRC8 parity.  Full compile.
2025-02-08 14:10:00 -05:00

913 lines
30 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
// chr_inc
// written by: eyesolated
// written at: April 13, 2018
//
// Notes: Include File for Character related stuff
///////////
// Includes
//
#include "datetime_inc"
#include "color_inc"
#include "chr_cfg"
#include "cnr_property_inc"
#include "sql_inc"
#include "util_inc"
#include "util_names_inc"
#include "nwnx_time"
#include "prc_inc_skin"
//:: void main (){}
///////////////////////
// Function Declaration
//
// Rest functions
// Queues oObject as the Food object to use for next rest
void chr_QueueFood(object oPC, object oObject);
// Starts the OnRest for oPC and nRestEvent
void chr_OnRest(object oPC, int nRestEvent);
// Sets the AFK status on a player, including a nice effect
void chr_SetAFKStatus(object oPC, int nIsAFK);
// Creates a player Backpack with Gold and items on the location of oPC
void chr_Drop_Backpack(object oPC);
// Gets Ability name
string chr_GetAbilityName(int nAbility, int nShort = FALSE);
// Gets Skill name
string chr_GetSkillName(int nSkill);
// Does an ability check
// DC: The DC to roll against, returns TRUE if Check succeded
// nAbility: ABILITY_*
// ShowMessage: Show a message to the player about the roll
// ForceDelay: Don't do the roll if other rolls (Ability Checks, Saving Throws etc.) are currently active
// ShowAnimation:Show an animation for rolling
int chr_DoAbilityCheck(object oPC, int nAbility, int DC = 0, int ShowMessage = FALSE, int ForceDelay = FALSE, int ShowAnimation = FALSE);
// Does an ability check
// DC: The DC to roll against, returns TRUE if Check succeded
// nAbility: ABILITY_*
// ShowMessage: Show a message to the player about the roll
// ForceDelay: Don't do the roll if other rolls (Ability Checks, Saving Throws etc.) are currently active
// ShowAnimation:Show an animation for rolling
// AllowUntrained: Allow rolling skills that would require training even if the character isn't trained in them
int chr_DoSkillCheck(object oPC, int nSkill, int DC = 0, int ShowMessage = FALSE, int ForceDelay = FALSE, int ShowAnimation = FALSE, int AllowUntrained = FALSE);
// Does a saving throw
// DC: The DC to roll against, returns TRUE if Check succeded
// nAbility: ABILITY_*
// ShowMessage: Show a message to the player about the roll
// ForceDelay: Don't do the roll if other rolls (Ability Checks, Saving Throws etc.) are currently active
// ShowAnimation:Show an animation for rolling
int chr_DoSavingThrow(object oPC, int nSaveType, int nDC = 0, int bShowMessage = FALSE, int bForceDelay = FALSE, int bShowAnimation = FALSE);
// Does a saving throw
// DC: The DC to roll against, returns TRUE if Check succeded
// nAbility: ABILITY_*
// ShowMessage: Show a message to the player about the roll
// ForceDelay: Don't do the roll if other rolls (Ability Checks, Saving Throws etc.) are currently active
// ShowAnimation:Show an animation for rolling
int chr_DoDieRoll(object oPC, int nSides, int nCount = 1, int ShowMessage = FALSE, int ForceDelay = FALSE, int ShowAnimation = FALSE);
// This function should be called on ClientLeave, so the Character System can undo necessary things
void chr_OnPlayerExit(object OPC);
// Returns the PC's persistence item that they will always have on them
object chr_GetPCItem(object oPC);
// Returns the PC's unique integer ID, which is always >0
// Returns <= 0 on error
int chr_GetPCID(object oPC);
// Initializes the PC tracking system. Returns new PCID, or 0 if character already initialized
int chr_InitializePC(object oPC);
// Returns the PC object corresponding to the given ID, if the PC is online at the time
object chr_GetPCByPCID(int nPCID);
// Stores the message in the players last messages log
void chr_LogPlayerChatMessage(object oPC, string sMessage);
// Returns the nthLast message of the PC
string chr_GetPlayerChatMessage(object oPC, int nthLast=0);
// Sets whether the oPC's messages will be displayed as usual.
void chr_SetPlayerCanSpeak(object oPC, int bCanSpeak=TRUE);
// Gets whether the oPC's messages will be displayed as usual.
int chr_GetPlayerCanSpeak(object oPC);
////////////////////////////////////////////////////////////////////////////////
// Internal Functions
//
void DoRest(object oPC)
{
FadeToBlack(oPC);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_IMP_SLEEP), oPC, 7.0f);
DelayCommand(7.0f, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_IMP_SLEEP), oPC, 7.0f));
}
void SaveRestTime(object oPC)
{
SetLocalInt(oPC, CHR_REST_VAR_REST_TIME, NWNX_Time_GetTimeStamp());
}
string GetNearbyHealer(object oPC)
{
object oHealer = GetNearestObjectByTag ("KaliaCain", oPC);
if (GetIsObjectValid(oHealer) && !GetIsDead(oHealer))
return GetName(oHealer);
oHealer = GetNearestObjectByTag ("ElrielDesifer", oPC);
if (GetIsObjectValid(oHealer) && !GetIsDead(oHealer))
return GetName(oHealer);
oHealer = GetNearestObjectByTag ("KadCulling", oPC);
if (GetIsObjectValid(oHealer) && !GetIsDead(oHealer))
return GetName(oHealer);
oHealer = GetNearestObjectByTag ("ClaraFenson", oPC);
if (GetIsObjectValid(oHealer) && !GetIsDead(oHealer))
return GetName(oHealer);
oHealer = GetNearestObjectByTag ("WanreDLloth", oPC);
if (GetIsObjectValid(oHealer) && !GetIsDead(oHealer))
return GetName(oHealer);
oHealer = GetNearestObjectByTag ("Wacoel", oPC);
if (GetIsObjectValid(oHealer) && !GetIsDead(oHealer))
return GetName(oHealer);
return "";
}
int GetIsPCInFreeRestArea(object oPC)
{
object oArea = GetArea(oPC);
if (GetLocalInt(oArea, "FreeRest") == 1)
return TRUE;
return FALSE;
}
object GetPCRestFood(object oPC)
{
object oMyFood;
// Is there any queued food available?
oMyFood = GetLocalObject(oPC, CHR_REST_VAR_FOOD_QUEUE);
if (GetIsObjectValid(oMyFood) &&
GetItemPossessor(oMyFood) == oPC)
{
return oMyFood;
}
// If not, cycle through the available food items
object oEquip = GetFirstItemInInventory(oPC);
while(GetIsObjectValid(oEquip)) {
if(!FindSubString(CnrGetHcrCompatibleTag(GetTag(oEquip)),"Food")) {
oMyFood = oEquip;
break;
}
oEquip = GetNextItemInInventory(oPC);
}
return oMyFood;
}
void doMonsterSurprise(object oPlayer)
{
// Possibly spawn monsters to a lonely resting player
int nChanceToJump = d100(1);
if (nChanceToJump <= CHR_REST_SURPRISE_CHANCE)
return;
int nCreaturesToJump = Random(CHR_REST_SURPRISE_MONSTERS_MAX) + 1;
if (nCreaturesToJump > 0)
{
int nCreatureCount = 0;
int nCreatureNumber = 1;
object oNearestPCToCreature;
float fDistanceToPC;
object oCreatureSelected = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, oPlayer, nCreatureNumber, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
while (GetIsObjectValid(oCreatureSelected) &&
nCreatureCount < nCreaturesToJump)
{
oNearestPCToCreature = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oCreatureSelected);
fDistanceToPC = GetDistanceBetween(oCreatureSelected, oNearestPCToCreature);
if (fDistanceToPC > 35.0f)
{
DelayCommand(1.0f, AssignCommand(oCreatureSelected, ActionJumpToObject(oPlayer)));
nCreatureCount++;
}
nCreatureNumber++;
oCreatureSelected = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, oPlayer, nCreatureNumber, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
}
}
}
int isGuarded(object oPC, int nRequiredGuards = 1)
{
int nth = 1;
int nFoundGuards = 0;
string sGuards = "";
object oOtherPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oPC, nth);
float fDistance;
while (GetIsObjectValid(oOtherPC))
{
if (!GetIsResting(oOtherPC) &&
!GetIsDM(oOtherPC))
{
fDistance = GetDistanceBetween(oPC, oOtherPC);
if (fDistance > 35.0f)
break;
nFoundGuards++;
sGuards += " [" + color_ConvertString(GetName(oOtherPC), COLOR_GREEN) + "]";
}
nth++;
oOtherPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oPC, nth);
}
if (nRequiredGuards <= nFoundGuards)
{
if (nFoundGuards > 4)
sGuards = " " + IntToString(nFoundGuards) + " people.";
SendMessageToPC(oPC, "You are guarded by:" + sGuards);
//SendMessageToAllDMs(GetName(oPC) + " is resting in " + GetName(GetArea(oPC)) + " and is guarded by:" + sGuards);
return TRUE;
}
else
{
SendMessageToPC(oPC, "You are resting alone.");
SendMessageToAllDMs(COLOR_CODE_BLUE_DARK + GetName(oPC) + " is resting alone in " + GetName(GetArea(oPC)) + "." + COLOR_CODE_END);
return FALSE;
}
}
void CancelRest(object oPC)
{
SetLocalInt(oPC, CHR_REST_VAR_REST_CANCELLED_BY_SCRIPT, 1);
}
int AllowRestByTime(object oPC)
{
// Get the current date/time
int tNow = NWNX_Time_GetTimeStamp();
// Get the last time the PC rested
int tLastRest = GetLocalInt(oPC, CHR_REST_VAR_REST_TIME);
int tDifference = tNow - tLastRest;
if (tDifference < FloatToInt(HoursToSeconds(CHR_REST_INTERVAL_HOURS)))
{
float fHoursToWait = (HoursToSeconds(CHR_REST_INTERVAL_HOURS) - tDifference) / HoursToSeconds(1);
SendMessageToPC(oPC, "You can't rest at this time, try again in " + FloatToString(fHoursToWait, 2, 1) + " hours.");
CancelRest(oPC);
AssignCommand(oPC, ClearAllActions());
return (FALSE);
}
return (TRUE);
}
void SendFamiliarAway(object oPC)
{
object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC);
if(!GetIsObjectValid(oFamiliar))
oFamiliar= GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC);
DestroyObject(oFamiliar);
}
void RemoveBlackScreen(object oPC)
{
FadeFromBlack(oPC);
}
////////////////////////////////////////////////////////////////////////////////
// Function Code
//
void chr_ZombieWalk(object oPC, int nDuration)
{
//object oPCSkin = GetItemInSlot (INVENTORY_SLOT_CARMOUR, oPC);
object oPCSkin = GetPCSkin(oPC);
// We only relate this message once, it's just so they know what is going on.
if (!GetLocalInt (oPC, CHR_NEARDEATH_VAR_MESSAGE))
{
FloatingTextStringOnCreature (CHR_NEARDEATH_MESSAGE, oPC, FALSE);
SetLocalInt (oPC, CHR_NEARDEATH_VAR_MESSAGE, 1);
}
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertySpecialWalk(0), oPCSkin, IntToFloat (nDuration));
}
void ApplyFoodEffect(object oPC)
{
string sFoodTag = GetLocalString(oPC, CHR_REST_VAR_FOOD_TAG);
effect eEffect;
if (sFoodTag == "cnrRyeBread")
eEffect = EffectAbilityIncrease(ABILITY_STRENGTH, 1);
else if (sFoodTag == "cnrWheatBread")
eEffect = EffectAbilityIncrease(ABILITY_DEXTERITY, 1);
else if (sFoodTag == "cnrRiceBread")
eEffect = EffectAbilityIncrease(ABILITY_CONSTITUTION, 1);
else if (sFoodTag == "cnrOatsBread")
eEffect = EffectAbilityIncrease(ABILITY_INTELLIGENCE, 1);
else if (sFoodTag == "cnrCornBread")
eEffect = EffectAbilityIncrease(ABILITY_WISDOM, 1);
else if (sFoodTag == "cnrApplePie")
eEffect = EffectSavingThrowIncrease(SAVING_THROW_FORT, 1, SAVING_THROW_TYPE_POISON);
else if (sFoodTag == "cnrCherryPie")
eEffect = EffectAbilityIncrease(ABILITY_CHARISMA, 1);
else if (sFoodTag == "cnrBlkberryPie")
eEffect = EffectSavingThrowIncrease(SAVING_THROW_FORT, 1, SAVING_THROW_TYPE_DISEASE);
else if (sFoodTag == "cnrBluberryPie")
eEffect = EffectAttackIncrease(1);
else if (sFoodTag == "cnrPecanPie")
eEffect = EffectAbilityIncrease(ABILITY_INTELLIGENCE, 1);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oPC, TurnsToSeconds(6));
}
void chr_QueueFood(object oPC, object oObject)
{
if(!FindSubString(CnrGetHcrCompatibleTag(GetTag(oObject)),"Food"))
{
SetLocalObject(oPC, CHR_REST_VAR_FOOD_QUEUE, oObject);
SendMessageToPC(oPC, "You will eat [" + color_ConvertString(GetName(oObject), COLOR_GREY) + "] on your next rest.");
}
}
void chr_OnRest(object oPC, int nRestEvent)
{
string sHealer;
object oFood;
string sFoodTag;
switch (nRestEvent)
{
case REST_EVENTTYPE_REST_STARTED:
{ SetLocalInt(oPC, CHR_REST_VAR_HITPOINTS_BEFORE_REST, GetCurrentHitPoints(oPC));
sHealer = GetNearbyHealer(oPC);
if (GetIsPCInFreeRestArea(oPC))
{
SetLocalInt(oPC, CHR_REST_VAR_NEARBYHEALER, 1);
}
else if (sHealer != "")
{
SetLocalInt(oPC, CHR_REST_VAR_NEARBYHEALER, 1);
SendMessageToPC(oPC, sHealer + "'s presence is soothing you.");
}
else if (AllowRestByTime(oPC))
{
oFood = GetPCRestFood(oPC);
if (!GetIsObjectValid(oFood))
{
FloatingTextStringOnCreature("You try to rest but you are too hungry.", oPC, FALSE);
CancelRest(oPC);
AssignCommand(oPC, ClearAllActions());
return;
}
SendFamiliarAway(oPC);
SetLocalString(oPC, CHR_REST_VAR_FOOD_TAG, GetTag(oFood));
DestroyObject(oFood);
DeleteLocalObject(oPC, CHR_REST_VAR_FOOD_QUEUE);
SendMessageToPC(oPC, "You eat [" + color_ConvertString(GetName(oFood), COLOR_GREY) + "]");
DoRest(oPC);
if (!isGuarded(oPC, CHR_REST_SURPRISE_GUARDS_NEEDED))
doMonsterSurprise(oPC);
}
break;
}
case REST_EVENTTYPE_REST_CANCELLED:
{
if (GetLocalInt(oPC, CHR_REST_VAR_NEARBYHEALER) == 1)
{
DeleteLocalInt(oPC, CHR_REST_VAR_NEARBYHEALER);
DeleteLocalInt(oPC, CHR_REST_VAR_REST_CANCELLED_BY_SCRIPT);
return;
}
// Delete the info on what food the PC ate
DeleteLocalString(oPC, CHR_REST_VAR_FOOD_TAG);
if (GetLocalInt(oPC, CHR_REST_VAR_REST_CANCELLED_BY_SCRIPT) == 1)
{
DeleteLocalInt(oPC, CHR_REST_VAR_REST_CANCELLED_BY_SCRIPT);
return;
}
// 120 seconds delay.
SetLocalInt(oPC, CHR_REST_VAR_REST_TIME, GetLocalInt(oPC, CHR_REST_VAR_REST_TIME) + 120);
NWNX_Object_SetCurrentHitPoints(oPC, GetLocalInt(oPC, CHR_REST_VAR_HITPOINTS_BEFORE_REST));
RemoveBlackScreen(oPC);
break;
}
case REST_EVENTTYPE_REST_FINISHED:
{ // Apply banked XP
SendMessageToPC(oPC, "Apply XP");
DelayCommand(0.0f, ExecuteScript("xp_apply", oPC));
SendMessageToPC(oPC, "Fire PRC reset script");
DelayCommand(0.0f, ExecuteScript("prc_rest", oPC));
SetLocalInt(oPC, CHR_REST_VAR_REST_TIME, NWNX_Time_GetTimeStamp());
if (GetLocalInt(oPC, CHR_REST_VAR_NEARBYHEALER) == 1)
{
DeleteLocalInt(oPC, CHR_REST_VAR_NEARBYHEALER);
DeleteLocalInt(oPC, CHR_REST_VAR_REST_CANCELLED_BY_SCRIPT);
return;
}
NWNX_Object_SetCurrentHitPoints(oPC,
PRCMin(GetMaxHitPoints(oPC), GetLocalInt(oPC, CHR_REST_VAR_HITPOINTS_BEFORE_REST) + 5 + GetHitDice(oPC)));
RemoveBlackScreen(oPC);
DeleteLocalInt(oPC, CHR_REST_VAR_REST_CANCELLED_BY_SCRIPT);
// Apply Food effect and delete the info on what food the PC ate
ApplyFoodEffect(oPC);
DeleteLocalString(oPC, CHR_REST_VAR_FOOD_TAG);
break;
}
}
}
void chr_SetAFKStatus(object oPC, int nIsAFK)
{
if (nIsAFK)
{
effect eImmobilize = EffectCutsceneImmobilize();
eImmobilize = TagEffect(eImmobilize, CHR_AFK_EFFECTTAG);
vector vVector = GetPosition(oPC);
int nCreatureSize = GetCreatureSize(oPC);
switch (nCreatureSize)
{
case CREATURE_SIZE_SMALL: vVector.z += 0.7f; break;
case CREATURE_SIZE_MEDIUM: vVector.z += 1.0f; break;
case CREATURE_SIZE_LARGE: vVector.z += 1.3f; break;
}
location lLocation = Location(GetArea(oPC), vVector, GetFacing(oPC));
object oButterFlies = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_butterflies", lLocation);
AssignCommand(oButterFlies, SetIsDestroyable(FALSE, FALSE, FALSE));
SetLocalObject(oPC, CHR_AFK_VAR_PLACEABLE, oButterFlies);
SetLocalInt(oPC, CHR_AFK_VAR_STATUS, TRUE);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eImmobilize, oPC);
}
else
{
object oButterFlies = GetLocalObject(oPC, CHR_AFK_VAR_PLACEABLE);
if (GetIsObjectValid(oButterFlies))
{
AssignCommand(oButterFlies, SetIsDestroyable(TRUE, FALSE, FALSE));
DestroyObject(oButterFlies, 0.1f);
}
DeleteLocalObject(oPC, CHR_AFK_VAR_PLACEABLE);
DeleteLocalInt(oPC, CHR_AFK_VAR_STATUS);
effect eEffect = GetFirstEffect(oPC);
while (GetIsEffectValid(eEffect))
{
if (GetEffectTag(eEffect) == CHR_AFK_EFFECTTAG)
{
RemoveEffect(oPC, eEffect);
break;
}
eEffect = GetNextEffect(oPC);
}
}
}
void chr_MoveInventoryToBackpack(object oOwner, object oDest, string sDropTags="")
{
if (!GetIsObjectValid(oDest))
return;
object oItem = GetFirstItemInInventory(oOwner);
while(oItem != OBJECT_INVALID)
{
if (GetItemCursedFlag(oItem))
{
oItem = GetNextItemInInventory(oOwner);
continue;
}
if (GetHasInventory(oItem))
{
chr_MoveInventoryToBackpack(oItem, oDest, sDropTags);
}
if (sDropTags == "" || FindSubString(sDropTags, GetTag(oItem)) >= 0)
{
CopyItem(oItem, oDest, TRUE);
DestroyObject(oItem);
}
oItem = GetNextItemInInventory (oOwner);
}
}
void chr_Drop_Backpack(object oPC)
{
location lDiedHere = GetLocation(oPC);
object oMod = GetModule();
object oBackpack;
string sID=GetName(oPC)+GetPCPublicCDKey(oPC);
// Create a Backpack if there is none yet at this location
oBackpack=GetLocalObject(oMod, "PlayerBackpack"+sID);
if (!GetIsObjectValid(oBackpack) ||
GetLocation(oBackpack) != lDiedHere)
{
oBackpack = CreateObject (OBJECT_TYPE_PLACEABLE, "playerbackpack", lDiedHere);
if (!GetIsObjectValid (oBackpack))
SendMessageToPC (oPC, "invalid backpack");
SetLocalObject(oMod,"PlayerBackpack"+sID, oBackpack);
}
// Drop player gold
int nAmtGold=GetGold(oPC);
WriteTimestampedLogEntry ("Dropping gold from player - " + IntToString (nAmtGold));
AssignCommand(oPC, TakeGoldFromCreature(nAmtGold, oPC, TRUE));
if (nAmtGold > CHR_DROP_GOLDMAXIMUM)
nAmtGold = CHR_DROP_GOLDMAXIMUM;
if (nAmtGold > 0)
CreateItemOnObject("NW_IT_GOLD001", oBackpack, nAmtGold);
// Drop Items
if (CHR_DROP_ITEMS)
{
string sDropTags = "playercorpse;";
sDropTags += "cnrNuggetCopp;cnrNuggetTin;cnrNuggetIron;cnrNuggetGold;cnrNuggetPlat;cnrNuggetAdam;cnrNuggetCoba;cnrNuggetSilv;cnrNuggetTita;cnrNuggetMith;";
sDropTags += "cnrGemMineral001;cnrGemMineral007;cnrGemMineral002;cnrGemMineral014;cnrGemMineral004;cnrGemMineral003;cnrGemMineral015;cnrGemMineral011;cnrGemMineral013;cnrGemMineral010;cnrGemMineral008;cnrGemMineral009;cnrGemMineral005;cnrGemMineral006;cnrGemMineral012;";
sDropTags += "cnrBranchHic;cnrBranchOak;cnrBranchMah;";
sDropTags += "ANPH_SUPPLIES_GOLD;ANPH_SUPPLIES_MAGIC;ANPH_SUPPLIES_ARMS;ANPH_SUPPLIES_ARMOR;";
chr_MoveInventoryToBackpack(oPC, oBackpack, sDropTags);
}
// If the Backpack is empty, destroy it
object oItem = GetFirstItemInInventory (oBackpack);
if (!GetIsObjectValid (oItem))
DestroyObject (oBackpack);
}
string chr_GetAbilityName(int nAbility, int nShort = FALSE)
{
switch (nAbility)
{
case ABILITY_STRENGTH:
if (nShort)
return "STR";
else
return "Strength";
break;
case ABILITY_DEXTERITY:
if (nShort)
return "DEX";
else
return "Dexterity";
break;
case ABILITY_CONSTITUTION:
if (nShort)
return "CON";
else
return "Constitution";
break;
case ABILITY_WISDOM:
if (nShort)
return "WIS";
else
return "Wisdom";
break;
case ABILITY_INTELLIGENCE:
if (nShort)
return "INT";
else
return "Intelligence";
break;
case ABILITY_CHARISMA:
if (nShort)
return "CHA";
else
return "Charisma";
break;
}
return "";
}
string chr_GetSkillName(int nSkill)
{
return GetStringByStrRef(StringToInt(Get2DAString("skills", "Name", nSkill)));
}
int chr_GetCurrentlyChecking(object oPC, int ShowMessage)
{
if (GetLocalInt(oPC, CHR_ROLL_VAR_CURRENTLYCHECKING))
{
if (ShowMessage)
SendMessageToPC(oPC, "You recently did a Roll, please don't spam.");
return TRUE;
}
return FALSE;
}
void chr_DoMessage(object oPC, string sMessage)
{
SendMessageToPC(oPC, sMessage);
// Always send Message to DMs regardless of privacy status
SendMessageToAllDMs(color_ConvertString(GetName(oPC), COLOR_GREY) + " - " + sMessage);
float fDistance = 30.0f;
int nPrivacy = GetLocalInt(oPC, CS_ROLLS_VAR_PRIVACY);
if (nPrivacy == CHR_ROLL_PRIVACY_PUBLIC)
{
location lLocation = GetLocation(oPC);
object oByStander = GetFirstObjectInShape(SHAPE_SPHERE, fDistance, lLocation, TRUE, OBJECT_TYPE_CREATURE);
while (GetIsObjectValid(oByStander))
{
if (GetIsPC(oByStander) &&
!GetIsDM(oByStander) &&
oPC != oByStander)
{
SendMessageToPC(oByStander, color_ConvertString(GetName(oPC), COLOR_GREY) + " - " + sMessage);
}
oByStander = GetNextObjectInShape(SHAPE_SPHERE, fDistance, lLocation, TRUE, OBJECT_TYPE_CREATURE);
}
}
}
void chr_DoCheckAnimationAndMessage(object oPC, int ShowMessage, int ShowAnimation, string sMessage, int nAnimation = ANIMATION_LOOPING_GET_MID, float fDuration = 2.0f)
{
// Roll Animation
if (ShowAnimation)
{
AssignCommand(oPC, ClearAllActions());
AssignCommand(oPC, ActionPlayAnimation(nAnimation, 1.0f, fDuration));
}
if (ShowMessage)
AssignCommand(GetModule(), DelayCommand(2.1f, chr_DoMessage(oPC, sMessage)));
}
void chr_DeleteCheck(object oPC)
{
DeleteLocalInt(oPC, CHR_ROLL_VAR_CURRENTLYCHECKING);
}
int chr_DoAbilityCheck(object oPC, int nAbility, int nDC = 0, int ShowMessage = FALSE, int ForceDelay = FALSE, int ShowAnimation = FALSE)
{
if (ForceDelay)
{
if (chr_GetCurrentlyChecking(oPC, ShowMessage))
return FALSE;
SetLocalInt(oPC, CHR_ROLL_VAR_CURRENTLYCHECKING, TRUE);
// Reset Check in Progress
AssignCommand(GetModule(), DelayCommand(3.0f, chr_DeleteCheck(oPC)));
}
int nAbilityModifier = GetAbilityModifier(nAbility, oPC);
int nRoll = d20();
int nResult = nAbilityModifier + nRoll;
// Build Message
string sMessage = "[" + color_ConvertString(chr_GetAbilityName(nAbility, TRUE), COLOR_ORANGE) + "] Check: ";
sMessage += color_ConvertString(IntToString(nResult), COLOR_GREY);
if (nDC != 0)
sMessage += " (DC: " + color_ConvertString(IntToString(nDC), COLOR_GREY) + ")";
chr_DoCheckAnimationAndMessage(oPC, ShowMessage, ShowAnimation, sMessage);
if (nDC != 0)
return (nDC <= nResult);
return nResult;
}
int chr_DoSkillCheck(object oPC, int nSkill, int nDC = 0, int ShowMessage = FALSE, int ForceDelay = FALSE, int ShowAnimation = FALSE, int AllowUntrained = FALSE)
{
if (ForceDelay)
{
if (chr_GetCurrentlyChecking(oPC, ShowMessage))
return FALSE;
SetLocalInt(oPC, CHR_ROLL_VAR_CURRENTLYCHECKING, TRUE);
// Reset Check in Progress
AssignCommand(GetModule(), DelayCommand(3.0f, chr_DeleteCheck(oPC)));
}
int nBaseScore = GetSkillRank(nSkill, oPC, TRUE);
if (nBaseScore == -1 &&
!AllowUntrained)
{
if (ShowMessage)
SendMessageToPC(oPC, "You don't have that skill.");
return FALSE;
}
int nSkillScore = GetSkillRank(nSkill, oPC);
int nRoll = d20();
int nResult = nSkillScore + nRoll;
// Build Message
string sMessage = "[" + color_ConvertString(chr_GetSkillName(nSkill), COLOR_ORANGE) + "] Check: ";
sMessage += color_ConvertString(IntToString(nResult), COLOR_GREY);
if (nDC != 0)
sMessage += " (DC: " + color_ConvertString(IntToString(nDC), COLOR_GREY) + ")";
chr_DoCheckAnimationAndMessage(oPC, ShowMessage, ShowAnimation, sMessage);
if (nDC != 0)
return (nDC <= nResult);
return nResult;
}
int chr_DoSavingThrow(object oPC, int nSaveType, int nDC = 0, int bShowMessage = FALSE, int bForceDelay = FALSE, int bShowAnimation = FALSE)
{
if (bForceDelay)
{
if (chr_GetCurrentlyChecking(oPC, bShowMessage))
return FALSE;
SetLocalInt(oPC, CHR_ROLL_VAR_CURRENTLYCHECKING, TRUE);
// Reset Check in Progress
AssignCommand(GetModule(), DelayCommand(3.0f, chr_DeleteCheck(oPC)));
}
int nSavingThrow;
string sSaveType;
switch (nSaveType)
{
case SAVING_THROW_FORT: nSavingThrow = GetFortitudeSavingThrow(oPC); sSaveType = "Fortitude"; break;
case SAVING_THROW_REFLEX: nSavingThrow = GetReflexSavingThrow(oPC); sSaveType = "Reflex"; break;
case SAVING_THROW_WILL: nSavingThrow = GetWillSavingThrow(oPC); sSaveType = "Will"; break;
}
int nRoll = d20();
int nResult = nSavingThrow + nRoll;
// Build Message
string sMessage = "[" + color_ConvertString(sSaveType, COLOR_ORANGE) + "] Saving Throw: ";
sMessage += color_ConvertString(IntToString(nResult), COLOR_GREY);
if (nDC != 0)
sMessage += " (DC: " + color_ConvertString(IntToString(nDC), COLOR_GREY) + ")";
chr_DoCheckAnimationAndMessage(oPC, bShowMessage, bShowAnimation, sMessage);
if (nDC != 0)
return (nDC <= nResult);
return nResult;
}
int chr_DoDieRoll(object oPC, int nSides, int nCount = 1, int ShowMessage = FALSE, int ForceDelay = FALSE, int ShowAnimation = FALSE)
{
if (ForceDelay)
{
if (chr_GetCurrentlyChecking(oPC, ShowMessage))
return FALSE;
SetLocalInt(oPC, CHR_ROLL_VAR_CURRENTLYCHECKING, TRUE);
// Reset Check in Progress
AssignCommand(GetModule(), DelayCommand(3.0f, chr_DeleteCheck(oPC)));
}
int nth;
int nRoll;
int nResult = 0;
for (nth = 1; nth <= nCount; nth++)
{
nRoll = Random(nSides) + 1;
nResult += nRoll;
}
// Build Message
string sMessage = "[" + IntToString(nCount) + "d" + color_ConvertString(IntToString(nSides), COLOR_ORANGE) + "]: ";
sMessage += color_ConvertString(IntToString(nResult), COLOR_GREY);
chr_DoCheckAnimationAndMessage(oPC, ShowMessage, ShowAnimation, sMessage);
return nResult;
}
void chr_OnPlayerExit(object oPC)
{
chr_SetAFKStatus(oPC, FALSE);
}
object chr_GetPCItem(object oPC)
{
object oPCItem = GetLocalObject(oPC, "chr_PCITEM");
if (!GetIsObjectValid(oPCItem))
{
oPCItem = GetItemPossessedBy(oPC, CHR_PCITEM_TAG);
if (oPCItem == OBJECT_INVALID)
{
oPCItem = CreateItemOnObject(CHR_PCITEM_RESREF, oPC);
}
SetLocalObject(oPC, "chr_PCITEM", oPCItem);
}
return oPCItem;
}
int chr_GetPCID(object oPC)
{
if (!GetIsPC(oPC) || GetIsObjectValid(GetMaster(oPC)))
return -1;
int nID = StringToInt(GetTag(oPC));
if (!nID)
{
nID = GetLocalInt(chr_GetPCItem(oPC), "chr_PCID");
if (!nID)
nID = sql_GetPCID(oPC);
SetTag(oPC, IntToString(nID));
//SetLocalObject(GetModule(), "chr_PCID_" + IntToString(nID), oPC);
}
return nID;
}
int chr_InitializePC(object oPC)
{
//if (!GetIsPC(oPC) || GetIsObjectValid(GetMaster(oPC)))
// return 0;
object oPCItem = chr_GetPCItem(oPC);
// Sanity check, avoid double inits
if (StringToInt(GetTag(oPC)) || GetLocalInt(oPCItem, "chr_PCID"))
return 0;
int nPCID = sql_RegisterNewPC(oPC);
if (!GetIsObjectValid(oPCItem))
{
oPCItem = CreateItemOnObject(CHR_PCITEM_RESREF, oPC, 1, CHR_PCITEM_TAG);
SetItemCursedFlag(oPCItem, TRUE);
SetStolenFlag(oPCItem, TRUE);
}
SetTag(oPC, IntToString(nPCID));
SetLocalInt(oPC, "chr_PCID", nPCID);
SetLocalObject(oPC, "chr_PCITEM", oPCItem);
return nPCID;
}
object chr_GetPCByPCID(int nPCID)
{
object oPC = GetLocalObject(GetModule(), "chr_PCID_" + IntToString(nPCID));
if (oPC != OBJECT_INVALID) // may still not be valid, but caller will check
return oPC;
oPC = GetFirstPC();
while (oPC != OBJECT_INVALID)
{
if (chr_GetPCID(oPC) > 0)
return oPC;
oPC = GetNextPC();
}
return oPC;
}
void chr_LogPlayerChatMessage(object oPC, string sMessage)
{
int nPos = GetLocalInt(oPC, "CHR_CHAT_MESSAGE_BUFFER_POS");
SetLocalString(oPC, "CHR_CHAT_MESSAGE_BUFFER_" + IntToString(nPos), sMessage);
nPos = (nPos + 1) % CHR_CHAT_MESSAGE_BUFFER_SIZE;
SetLocalInt(oPC, "CHR_CHAT_MESSAGE_BUFFER_POS", nPos);
}
string chr_GetPlayerChatMessage(object oPC, int nthLast=0)
{
if (nthLast >= CHR_CHAT_MESSAGE_BUFFER_SIZE)
return "";
int nPos = GetLocalInt(oPC, "CHR_CHAT_MESSAGE_BUFFER_POS");
nPos = (CHR_CHAT_MESSAGE_BUFFER_SIZE + nPos - nthLast - 1) % CHR_CHAT_MESSAGE_BUFFER_SIZE;
return GetLocalString(oPC, "CHR_CHAT_MESSAGE_BUFFER_" + IntToString(nPos));
}
void chr_SetPlayerCanSpeak(object oPC, int bCanSpeak=TRUE)
{
SetLocalInt(oPC, "CHR_CHAT_IS_MUTED", !bCanSpeak);
}
int chr_GetPlayerCanSpeak(object oPC)
{
return !GetLocalInt(oPC, "CHR_CHAT_IS_MUTED");
}