913 lines
30 KiB
Plaintext
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");
|
|
}
|