Alangara_PRC8/_module/nss/mn_i_pwfunctions.nss
Jaysyn904 86feb9ca6f Initial commit
Initial commit.
2024-06-05 21:21:06 -04:00

1768 lines
63 KiB
Plaintext

#include "mn_i_uservars"
#include "mn_i_persist"
#include "mn_i_util"
// Varnames on module
const string MODULE_DEFAULT_CLEANUP_POLICY = "DEFAULT_CLEANUP_POLICY"; // (integer) Corresponds to CLEANUP_POLICY. Default value, module scope
const string MODULE_DEFAULT_CLEANUP_TARGETS = "DEFAULT_CLEANUP_TARGETS"; // (integer) Corresponds to OBJECT_TYPE or CLEANUP_TARGET, module scope
// Varnames on areas
const string AREA_EXPLORE_FULLEXPLORE = "EXPLORE_TOTAL"; // (integer) The sum of all explorationids + 1 (the 1 is from the EXPLORE_DISCOVERED). Manually calculated.
const string AREA_DEFAULT_CLEANUP_POLICY = "DEFAULT_CLEANUP_POLICY"; // (integer) Corresponds to CLEANUP_POLICY. Default value, area scope
const string AREA_DEFAULT_CLEANUP_TARGETS = "DEFAULT_CLEANUP_TARGETS"; // (integer) Corresponds to OBJECT_TYPE or CLEANUP_TARGET, area scope
const string AREA_BLOCK_TELEPORT_INCOMING = "BLOCK_TELEPORT_INCOMING"; // (integer) Corresponds to TELEPORT_*. Blocks _incoming_ teleportation to area.
const string AREA_BLOCK_TELEPORT_OUTGOING = "BLOCK_TELEPORT_OUTGOING"; // (integer) Corresponds to TELEPORT_*. Blocks _outgoing_ teleporation to area.
const string AREA_ID = "ID"; // (string) The id of the area. Defaults to resref if no ID is specified
// Varnames on objects
const string OBJ_EXPLORE_NODEID = "EXPLORE_ID"; // (integer) The explorationid of a exploration landmark trigger (integer).
const string OBJ_EXPLORE_NODEFOUND = "EXPLORE_FOUND_COMMENT"; // (string) The string spoken by a PC when he explores the trigger the first time
const string OBJ_EXPLORE_NODEBACK = "EXPLORE_BACK_COMMENT"; // (string) The string spoken by a PC when he finds the trigger again
const string OBJ_CLEANUP = "CLEANUP"; // (integer) Tag used for marking objects for cleanup. Corresponds to CLEANUP_POLICY constant.
const string OBJ_TELEPORT_PREFIX = "TELEPORT_";
const string OBJ_TELEPORT_DESTINATION = "_DESTINATION"; // (string) Tag used for specifying the destination of a teleportation item or placeable
const string OBJ_TELEPORT_DESTINATION_NAME = "_DESTINATION_NAME"; // (string) Name used for specifying the name of the destination of a teleportation item or placeable
const string OBJ_TELEPORT_TYPE = "_TYPE"; // (integer) Tag used for specifying the teleportation type of a teleportation item or placeable.
const string OBJ_TELEPORT_SPEED = "_SPEED"; // (integer) Tag used for specifying the teleportation type of a teleportation item or placeable.
const string OBJ_TELEPORT_REQ_QUEST_ID = "_REQ_QUEST_ID"; // (string) Quest id for the quest which must be completed before the teleportation can be performed.
const string OBJ_TELEPORT_ANIMATION = "_ANIMATION"; // (integer) Tag used for specifying the animation to be used by a teleportation item or placeable.
const string OBJ_TELEPORT_PARTY = "_PARTY"; // (integer) Whether teleportation affects entire party
const string OBJ_DESCRIPTION = "DESCRIPTION"; // (string) Tag used for specifying the description of the object. Generic field, with specific usage for specific cases.
const string OBJ_UNIQUE_POWER_ID = "UNIQUE_POWER_ID"; // (int) The UNIQUE_POWER_* constant specifying how to interpret the unique power of the item.
const string OBJ_UNIQUE_POWER_NAME = "UNIQUE_POWER_NAME"; // (string) The name of the script to run when the unique power is executed.
const string OBJ_ID = "ID"; // (string) The id of the object. Without an ID, no persistance functions will be allowed for this object.
// Varnames on PCs
const string PC_P_BLOCK_TELEPORT = "BLOCK_TELEPORT"; // (integer) Local teleportation override, restricting player outbound teleportation. Corresponds to TELEPORT_*
// Custom tokens
const int TOKEN_TELEPORTATION_NAME = 8000;
// General constants
const int EXPLORE_DISCOVERED = 1;
const int EXPLORE_POINT_1 = 2;
const int EXPLORE_POINT_2 = 4;
const int EXPLORE_POINT_3 = 8;
const int EXPLORE_POINT_4 = 16;
const int EXPLORE_POINT_5 = 32;
const int EXPLORE_POINT_6 = 64;
const int EXPLORE_POINT_7 = 128;
const int EXPLORE_POINT_8 = 256;
const int EXPLORE_POINT_9 = 512;
const int QUEST_UNKNOWN = 0;
const int QUEST_BEGUN = 1;
const int QUEST_COMPLETED = 2;
const int QUEST_REJECTED = 4;
const int QUEST_FAILED = 8;
const int QUEST_BLOCKED = 16;
const int TELEPORT_DYNAMIC = 1; // Spells, creatures, and items
const int TELEPORT_STATIONARY = 2; // Placeables
const int TELEPORT_DIVINE = 4; // Extraordinary, such as reborn power
const int TELEPORT_OOC = 8; // Crash ring etc.
const int TELEPORT_NONE = 0;
const int TELEPORT_ALL = 15;
const int TELEPORT_ANIMATION_NONE = 0;
const int TELEPORT_ANIMATION_UNSUMMONEFFECT = 1;
const int TELEPORT_SPEED_DELAYED = 0;
const int TELEPORT_SPEED_BLINK = 1;
const string TELEPORT_UNKNOWN_DESTINATION = "an unknown destination";
const string TELEPORT_TARGET_SELF = "SELF";
// positive number
const int CLEANUP_POLICY_NONE = 0; // Deletes nothing
const int CLEANUP_POLICY_DROPPED_BY_PLAYERS = 1; // Deletes player dropped objects
const int CLEANUP_POLICY_ENVIRONMENT_SPAWNS = 2; // Deletes automatic (encounter) spawns (including loot bags)
const int CLEANUP_POLICY_DM_SPAWNS = 4; // Deletes DM spawns
const int CLEANUP_POLICY_EXEMPT = 8; // Deletes stuff explicitly marked as exempt from cleanup!
const int CLEANUP_POLICY_UNMARKED = 16; // Deletes unmarked!
const int CLEANUP_POLICY_MARKED = 7; // Deletes all marked objects (except for exempt).
const int CLEANUP_POLICY_ALL = 31; // Deletes everything!
const int CLEANUP_TARGET_NONE = 0; // Matches none
const int CLEANUP_TARGET_ALL_VALID_TARGETS = 83; // Matches OBJECT_TYPE creature, item, area of effect and placeable, but not trigger, door, waypoint, store, or encounters
const string EXCHANGE_PREFIX = "EX_";
const string EXCHANGE_TAKE_ITEM = "_TAKE_ITEM_"; // Tag of taken item.
const string EXCHANGE_TAKE_ITEM_AMOUNT = "_TAKE_ITEM_AMOUNT_"; // Number of taken item to be taken.
const string EXCHANGE_TAKE_GOLD = "_TAKE_GOLD"; // Amount of gold taken.
const string EXCHANGE_TAKE_XP = "_TAKE_XP"; // Amount of XP taken.
const string EXCHANGE_QUEST_ID = "_QUEST_ID"; // Quest to increment when quest is performed.
const string EXCHANGE_QUEST_REPEAT = "_QUEST_REPEAT"; // Number of times the quest can be completed. -1 means unlimited.
const string EXCHANGE_MIN_LEVEL = "_MIN_LVL"; // Minimum total levels to make exchange (inclusive).
const string EXCHANGE_MAX_LEVEL = "_MAX_LVL"; // Maximum total level allowed in order to get this reward (inclusive).
const string EXCHANGE_ALLOW_BATCH = "_ALLOW_BATCH"; // Whether the NPC can loop through exchanges until no longer valie instead of only one at a time.
const string EXCHANGE_UNKNOWN_OK = "_UNKNOWN_OK"; // Whether the quest can be completed without it being open (known) for the player.
const string EXCHANGE_JOURNAL = "_JOURNAL"; // Whether the quest has journal entries.
const string EXCHANGE_GIVE_ITEM = "_GIVE_ITEM_"; // Resref of item to give
const string EXCHANGE_GIVE_ITEM_AMOUNT = "_GIVE_ITEM_AMOUNT_"; // Number of given item to give
const string EXCHANGE_GIVE_GOLD = "_GIVE_GOLD"; // Amount of gold to give
const string EXCHANGE_GIVE_XP = "_GIVE_XP"; // Amount of xp to give
const string EXCHANGE_OPEN_QUEST = "_OPEN_QUEST"; // Open quest as reward. If questID, happens when quest is marked as completed.
const string EXCHANGE_VISUALEFFECT = "_VFX"; // The VFX_FNF or VFX_IMP constant to play on the PC when the quest is completed, or 0 if none.
const int EXCHANGE_ALLOWED = 0;
const int EXCHANGE_INSUFFICIENT_GOLD = 1;
const int EXCHANGE_INSUFFICIENT_XP = 2;
const int EXCHANGE_INSUFFICIENT_ITEMS = 4;
const int EXCHANGE_LEVEL_TOO_LOW = 8;
const int EXCHANGE_LEVEL_TOO_HIGH = 16;
const int EXCHANGE_QUEST_ALREADY_COMPLETED = 32;
const int EXCHANGE_QUEST_BLOCKED = 64;
const int EXCHANGE_QUEST_FAILED = 128;
const int EXCHANGE_QUEST_REJECTED = 256;
const int EXCHANGE_QUEST_MISSING_ITEM = 512;
const int EXCHANGE_QUEST_UNKNOWN = 1024;
const int EXCHANGE_SUCCEEDED = 0;
const int EXCHANGE_FAILED = 1;
const int UNIQUE_POWER_BY_LOCAL_HANDLER = 0;
const int UNIQUE_POWER_BY_TAG = 1;
const int UNIQUE_POWER_BY_RESREF = 2;
const int UNIQUE_POWER_BY_SCRIPT = 3;
const int UNIQUE_POWER_TELEPORT = 4;
const int UNIQUE_POWER_MAP = 5;
// Please notice that when using local caching of quest status, this cache isn't reset when questDB is (re)initialized
// Since I cannot iterate through variables on user, I have disabled this functionality for the time being.
const int QUESTS_USE_LOCAL_CACHE = FALSE;
// ********** PROTOTYPES **********
// ***** Quest scripts *****
// Returns the QUEST_* status code
int GetQuestStatus(object oPC, string questID);
// Returns TRUE if the QUEST_* questStatus corresponds to the current quest-status for that quest.
int CheckQuestStatus(object oPC, string questID, int questStatus);
// Sets the QUEST_* status for the quest. If it is a numbered quest, use SetGlobalQuestCompletedTimes instead.
void SetQuestStatus(object oPC, string questID, int status);
// Sets the number of times the quest has been completed. Must be at least 1.
void SetQuestCompletedTimes(object oPC, string questID, int number);
// Gets the number of times the quest has been completed, or -1 if not applicable.
int GetQuestCompletedTimes(object oPC, string questID);
// Returns the QUEST_* status code for the global quest
int GetGlobalQuestStatus(string entityId, string questID);
// Returns TRUE if the QUEST_* questStatus corresponds to the current quest-status for that global quest.
int CheckGlobalQuestStatus(string entityId, string questID, int questStatus);
// Sets the QUEST_* status for the global quest. If it is a numbered quest, use SetGlobalQuestCompletedTimes instead.
void SetGlobalQuestStatus(string entityId, string questID, int status);
// Sets the number of times the global quest has been completed. Must be at least 1.
void SetGlobalQuestCompletedTimes(string entityId, string questID, int number);
// Gets the number of times the global quest has been completed, or -1 if not applicable.
int GetGlobalQuestCompletedTimes(string entityId, string questID);
// Helper method. Do not call directly
void SetPQuestStatus(object obj, string questID, int status);
// Helper method. Do not call directly
int GetPQuestStatus(object obj, string questID);
// Helper method. Do not call directly
void SetGlobalPQuestStatus(string globalId, string questID, int status);
// Helper method. Do not call directly
int GetGlobalPQuestStatus(string globalId, string questID);
// Exploration scripts
// Calls the FoundExploreArea and RevealExploredArea methods in succession.
// Convenience method for calling from within area onEnter script.
void WrapExploreOnEnterArea(object oPC, int exploreFull);
// Updates exploration status for first entering an area, and gives bonus if applicable.
void FoundExploreArea(object oPC, string areaId);
// Reveals entered area to player, if it is explored.
// This is the case if the player has explored all landmarks or memorized has memorized a map of the area.
void RevealExploredArea(object oPC, string areaId, int exploreFull);
// Calls the ExploreNodeFound and RevealsExploredArea methods in succession.
// Convenience method for calling from within trigger onEnter script.
void WrapExploreNodeFound(object oPC, int exploreID, int exploreFull);
// Updates exploration status for first entering an exploration node/landmark.
void ExploreNodeFound(object oPC, int exploreID);
// Handles reading or examining of a map. Returns text to show to player. Called from onActivated or (with events) from examining the map.
string ReadExploreMap(object oPC, object oMap);
// 'or' with total-1
// Helper method. Do not call directly
int GetPExplored(object obj, string areaID);
// Helper method. Do not call directly
void SetPExplored(object obj, string areaID, int status);
// Helper method. Do not call directly
void AddPExplored(object obj, string areaID, int status);
// Admin scripts
// Resets server. Delay is in minutes.
void ResetServer(int delay=0);
// Misc scripts
// Wraps around getLocalInt, so that marker variables can be imported from database
int GetMarkerInt(object obj, string varname, int persistantVar);
// Wraps around getLocalFloat, so that marker variables can be imported from database
float GetMarkerFloat(object obj, string varname, int persistantVar);
// Wraps around getLocalString, so that marker variables can be imported from database
string GetMarkerString(object obj, string varname, int persistantVar);
// Wraps around setLocalInt, so that marker variables can be exported to database
void SetMarkerInt(object obj, string varname, int value, int persistantVar);
// Wraps around setLocalFloat, so that marker variables can be exported to database
void SetMarkerFloat(object obj, string varname, float value, int persistantVar);
// Wraps around setLocalString, so that marker variables can be exported to database
void SetMarkerString(object obj, string varname, string value, int persistantVar);
// Wraps around DeleteLocalString, so that marker variables can be deleted from database
void DeleteMarkerString(object obj, string varname, int persistantVar);
// Wraps around DeleteLocalFloat, so that marker variables can be deleted from database
void DeleteMarkerFloat(object obj, string varname, int persistantVar);
// Wraps around DeleteLocalInt, so that marker variables can be deleted from database
void DeleteMarkerInt(object obj, string varname, int persistantVar);
// Returns the effective level from an xp amount
int GetLevelFromXP(int xp);
// Returns the minimum xp required for the given level.
int GetXPMinForLevel(int level);
// Return the number of xp needed to go from current xp to wanted level (if 0, then the amount needed to go up one level.)
int GetXPNeededForLevel(int currentXP, int wantedLevel =0);
// Checks if PC has the necessary prerequisites for getting a reward
int CheckExchangeBlocked(object PC, object NPC, int index = 1);
// Takes prerequisite items from PC, and give reward to PC
void PerformExchange(object PC, object NPC, int index = 1);
// Creates items as normal, but also initializes necessary local variables. Always call this instead of CreateItemOnObject
object CreateItemOnObjectMN( string sItemTemplate, object oTarget=OBJECT_SELF, int nStackSize=1, string sNewTag="", string creationOptions="" );
int NewValidate(object oPC);
int LevelupValidate(object oPC);
void StripPlayerOfItems(object oPC);
int ProcessIncomingPlayer(object oPC);
void FlushPInventory(object container);
void StorePInventory(object container, int emptyInventoryAfterwards = TRUE);
void GetStoredPInventory(object container, int emptyInventoryBefore = FALSE);
// Returns the Alangara ID for the requested object
string GetId(object obj);
// Cleans up the target area, deleting all masked OBJECT_TYPE objects with masked CLEANUP_POLICY flags.
void CleanupArea(object area, int cleanupTargets, int cleanupPolicy);
// Delete an object. If the object has an inventory, also delete the inventory, recursively (e.g. inventory of inventory items are also deleted)
void RecursiveDeleteObject(object obj, float timeOffset = 0.0, int ignorePlot = FALSE);
// Checks if flag is set in value.
int CheckFlag(int value, int flag);
// RestXyz
// Teleport
// Teleport a creature. Information about the teleportation is read from the teleporter object.
void DoTeleport( object oPC, object teleporter, int index = 1 , int teleportBlocksMovement = TRUE);
// Teleport a creature to the waypoint with the specified tag, using the specified method (sourceType). The script checks for teleportation blocking.
void DoScriptTeleport( object oPC, object destinationObject, int teleportationType = TELEPORT_OOC, int teleportationAnimation = TELEPORT_ANIMATION_NONE, int teleportationSpeed = TELEPORT_SPEED_BLINK, string questRequired = "", int teleportBlocksMovement = TRUE);
// Sends message to first PC
void Debug(string msg);
// ********** Business Methods **********
int CheckFlag(int value, int flag)
{
return ( (value & flag) != 0 );
}
// Helper method, converts varname plus index to the name of the variable to read from.
string TeleportVar(string var, int index)
{
string returnValue = OBJ_TELEPORT_PREFIX+IntToString(index)+var;
return returnValue;
}
int ShowTeleportConversationOption( object oPC, object teleporter, int index = 1 )
{
int showNode = TRUE;
string tokenText = TELEPORT_UNKNOWN_DESTINATION;
// Retrieve destination
string destinationTag = GetLocalString(teleporter, TeleportVar(OBJ_TELEPORT_DESTINATION , index) );
if ( destinationTag == "" )
{
// No destination for this index, so don't show the node
return FALSE;
}
// Retrieve custom text
// If text is set on the teleporter, it is used. Else, check if the description is set on the destination, and if
// so, use that. If neither text is filled out, set the text on the teleporter to the unknown constant.
string destinationText = GetLocalString(teleporter, TeleportVar(OBJ_TELEPORT_DESTINATION_NAME , index) );
if (destinationText == "")
{
// Destination on teleporter was not set in toolset.
// Try to read value from destination instead
object destWp = GetWaypointByTag(destinationTag);
if (GetIsObjectValid(destWp))
{
destinationText = GetLocalString(destWp, OBJ_DESCRIPTION) ;
}
// If text wasn't on destination description either, then default it to the unknown constant.
// In any case, cache locally on teleporter for later lookups (so we don't have to seach for the destination
// each time).
if (destinationText == "")
{
SetLocalString(teleporter, TeleportVar(OBJ_TELEPORT_DESTINATION_NAME , index), TELEPORT_UNKNOWN_DESTINATION);
}
else
{
SetLocalString(teleporter, TeleportVar(OBJ_TELEPORT_DESTINATION_NAME , index), destinationText);
}
}
// Check quest prereq
int known = TRUE;
string questId = GetLocalString(teleporter, TeleportVar(OBJ_TELEPORT_REQ_QUEST_ID , index) );
if ( questId != "")
{
known = CheckQuestStatus( oPC, questId, QUEST_COMPLETED );
}
if (known && destinationText != "")
{
tokenText = destinationText;
}
SetCustomToken(TOKEN_TELEPORTATION_NAME+index, tokenText);
return showNode;
}
void DoScriptTeleport( object oPC, object destinationObject, int teleportationType = TELEPORT_OOC, int teleportationAnimation = TELEPORT_ANIMATION_NONE, int teleportationSpeed = TELEPORT_SPEED_BLINK, string questRequired = "", int teleportBlocksMovement = TRUE)
{
if (oPC == destinationObject)
{
// Trying to teleport to itself
return;
}
object originArea = GetArea(oPC);
location destination = GetLocation ( destinationObject );
object destinationArea = GetAreaFromLocation(destination);
// Check for errors, abort if any error found.
if ( !GetIsObjectValid(oPC) || ! GetIsObjectValid(destinationObject) || ( !GetIsPC(oPC) && GetObjectType(oPC) != OBJECT_TYPE_CREATURE ) || !GetIsObjectValid(originArea) || !GetIsObjectValid(destinationArea) || !GetIsObjectValid(destinationArea) || teleportationType < 1 || teleportationType > TELEPORT_ALL)
{
return;
}
// Check if teleport is blocked
int outgoingBlock = GetLocalInt( originArea, AREA_BLOCK_TELEPORT_OUTGOING );
int incomingBlock = GetLocalInt( destinationArea, AREA_BLOCK_TELEPORT_INCOMING );
int personalBlock = GetLocalInt( oPC, PC_P_BLOCK_TELEPORT );
int blocked = CheckFlag(teleportationType, outgoingBlock) || CheckFlag(teleportationType, incomingBlock) || CheckFlag(teleportationType, personalBlock);
// Check for attunement, if applicable
int attuned = TRUE;
if ( questRequired != "")
{
attuned = CheckQuestStatus(oPC, questRequired, QUEST_COMPLETED);
}
// Perform the teleportation, unless either blocked or unattuned
if (blocked)
{
FloatingTextStringOnCreature( "Something interferes with your teleportation", oPC, FALSE );
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DAZED_S), oPC);
}
else if (!attuned)
{
FloatingTextStringOnCreature( "Your lack of attunement to the teleportation source prevents your translocation.", oPC, FALSE );
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DAZED_S), oPC);
}
else
{
AssignCommand(oPC, ClearAllActions());
float delay = 3.0f;
switch (teleportationSpeed)
{
case TELEPORT_SPEED_BLINK:
delay = 0.0f;
break;
case TELEPORT_SPEED_DELAYED:
delay = 3.0f;
break;
}
AssignCommand(oPC, ClearAllActions());
if (delay > 0.0f)
{
if (teleportBlocksMovement)
{
effect blockMovement = EffectCutsceneImmobilize();
FloatingTextStringOnCreature( "Debug: Block movement", oPC, FALSE );
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, blockMovement, oPC, delay);
// DelayCommand(delay, AssignCommand(oPC, RemoveEffect(oPC, blockMovement)));
}
DelayCommand(delay, AssignCommand(oPC, ActionJumpToLocation(destination)));
}
else
{
if (teleportBlocksMovement)
{
effect blockMovement = EffectCutsceneImmobilize();
FloatingTextStringOnCreature( "Debug: Block movement", oPC, FALSE );
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, blockMovement, oPC, 0.1f);
// DelayCommand(delay, AssignCommand(oPC, RemoveEffect(oPC, blockMovement)));
}
AssignCommand(oPC, ActionJumpToLocation(destination));
}
// Play animation if applicable
switch (teleportationAnimation)
{
case TELEPORT_ANIMATION_UNSUMMONEFFECT:
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_UNSUMMON), oPC);
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_UNSUMMON), destination);
break;
}
}
}
void DoTeleport( object oPC, object teleporter, int index = 1, int teleportBlocksMovement = TRUE )
{
string destinationTag = GetLocalString(teleporter, TeleportVar( OBJ_TELEPORT_DESTINATION, index ) );
string questRequired = GetLocalString(teleporter, TeleportVar( OBJ_TELEPORT_REQ_QUEST_ID, index ) );
int sourceType = GetLocalInt(teleporter, TeleportVar( OBJ_TELEPORT_TYPE, index ) );
int teleportationAnimation = GetLocalInt(teleporter, TeleportVar( OBJ_TELEPORT_ANIMATION, index ) );
int teleportSpeed = GetLocalInt(teleporter, TeleportVar( OBJ_TELEPORT_SPEED, index ) );
int partyTeleport = GetLocalInt(teleporter, TeleportVar( OBJ_TELEPORT_PARTY, index ) );
object targetObject;
if ( destinationTag == TELEPORT_TARGET_SELF )
{
targetObject = oPC;
}
else
{
targetObject = GetWaypointByTag(destinationTag);
}
if ( partyTeleport )
{
object partyMember = GetFirstFactionMember(oPC, TRUE);
while(GetIsObjectValid(partyMember) == TRUE)
{
DoScriptTeleport( partyMember, targetObject, sourceType, teleportationAnimation, teleportSpeed, questRequired, teleportBlocksMovement);
partyMember = GetNextFactionMember(oPC, TRUE);
}
// We always disable the animation for NPC members - let's try to keep down the VFX ;-)
object partyMemberNpc = GetFirstFactionMember(oPC, FALSE);
while(GetIsObjectValid(partyMemberNpc) == TRUE)
{
DoScriptTeleport( partyMemberNpc, targetObject, sourceType, TELEPORT_ANIMATION_NONE, teleportSpeed, questRequired, teleportBlocksMovement);
partyMemberNpc = GetNextFactionMember(oPC, FALSE);
}
}
else
{
DoScriptTeleport( oPC, targetObject, sourceType, teleportationAnimation, teleportSpeed, questRequired, teleportBlocksMovement);
}
}
void RecursiveDeleteObject(object obj, float timeOffset = 0.0, int ignorePlot = TRUE)
{
if ( !GetIsPC(obj) && ( CheckFlag(GetObjectType(obj), (OBJECT_TYPE_PLACEABLE|OBJECT_TYPE_CREATURE|OBJECT_TYPE_ITEM|OBJECT_TYPE_AREA_OF_EFFECT) ) ) )
{
if (GetObjectType(obj) == OBJECT_TYPE_CREATURE)
{
// Delete equipped items
// SendMessageToPC(GetFirstPC(), "Check equipped");
int slot = 0;
for (slot = 0; slot < NUM_INVENTORY_SLOTS; slot++)
{
object item = GetItemInSlot(slot, obj);
if ( GetIsObjectValid(item) )
{
SetPlotFlag( item, FALSE );
DestroyObject ( item, timeOffset+0.1 );
// SendMessageToPC(GetFirstPC(), "Destroyed "+GetName(item));
}
}
}
if (GetHasInventory(obj))
{
// Delete inventory as well
// SendMessageToPC(GetFirstPC(), "Check inventory");
object oItem = GetFirstItemInInventory(obj);
while( GetIsObjectValid( oItem ))
{
SetPlotFlag( oItem, FALSE );
DestroyObject( oItem, GetHasInventory( oItem) ? timeOffset+0.2 : timeOffset+0.1);
// SendMessageToPC(GetFirstPC(), "Destroyed "+GetName(oItem));
oItem = GetNextItemInInventory(obj);
}
}
// Delete object
if (ignorePlot)
{
SetPlotFlag( obj, FALSE );
}
DestroyObject (obj, timeOffset+0.3);
// SendMessageToPC(GetFirstPC(), "Destroyed "+GetName(obj));
}
}
void CleanupArea(object area, int cleanupTargets, int cleanupPolicy)
{
if ( GetIsObjectValid( area ) )
{
object obj = GetFirstObjectInArea( area );
while ( GetIsObjectValid ( obj ) )
{
// Never delete PCs and DMs, or their familars/followers
if ( GetIsPC(obj) || (GetMaster(obj) != OBJECT_INVALID ) )
{
// do nothing
// SendMessageToPC(GetFirstPC(), "Do nothing");
}
// Else check if target is valid
else if ( (cleanupTargets != CLEANUP_TARGET_NONE) && ( (cleanupTargets & GetObjectType(obj)) != 0 ) )
{
// SendMessageToPC(GetFirstPC(), "Valid target");
// Check if object flag corresponds to the chosen cleanup policy
int cleanupTag = GetLocalInt(obj, OBJ_CLEANUP);
// Ok to delete if both unmarked and if unmarked deletion is ok, or if marked and the mark corresponds with the cleanup policy.
if ( ( cleanupTag == 0 && (cleanupPolicy & CLEANUP_POLICY_UNMARKED) != 0 ) || ( cleanupTag != 0 && (cleanupTag & cleanupPolicy ) != 0 ) )
{
// SendMessageToPC(GetFirstPC(), "Deletion is ok");
// Delete object, and it's inventory. Always delete inventory, disregarding cleanup tag.
RecursiveDeleteObject(obj);
}
}
obj = GetNextObjectInArea( area );
}
}
}
// ***** QUEST RELATED METHODS *****
int GetQuestStatus(object oPC, string questID)
{
int returnValue = 0;
if (QUESTS_USE_LOCAL_CACHE)
{
returnValue = GetLocalInt(oPC, "Q_"+questID);
}
if (returnValue == 0)
{
returnValue = GetPQuestStatus(oPC, questID);
}
return returnValue;
}
int CheckQuestStatus(object oPC, string questID, int questStatus)
{
int status = GetPQuestStatus(oPC, questID);
int returnValue = ( (questStatus == QUEST_UNKNOWN && status == QUEST_UNKNOWN) || ( (status & questStatus) != 0 ) );
return returnValue;
}
void SetQuestStatus(object oPC, string questID, int status)
{
SetPQuestStatus(oPC, questID, status);
int journalState = status;
if (status > 20)
{
// No seperate entries for multiple completion quests.
journalState = 20;
}
/* No journal support for quests yet
if (journalState == QUEST_UNKNOWN)
{
RemoveJournalQuestEntry(questID, oPC, FALSE, FALSE);
}
else
{
AddJournalQuestEntry(questID, journalState, oPC, FALSE, FALSE, TRUE);
}
if (QUESTS_USE_LOCAL_CACHE)
{
SetLocalInt(oPC, "Q_"+questID, status);
}
*/
}
void SetQuestCompletedTimes(object oPC, string questID, int number)
{
SetQuestStatus(oPC, questID, number+20);
}
// Returns -1 for queststatus below 21, or queststatus-20 otherwise.
int GetQuestCompletedTimes(object oPC, string questID)
{
int returnValue = -1;
int questStatus = GetPQuestStatus(oPC, questID);
if (questStatus > 20)
{
returnValue = questStatus-20;
}
return returnValue;
}
// Calls the FoundExploreArea and RevealExploredArea methods in succession.
// Convenience method for calling from within area onEnter script.
void WrapExploreOnEnterArea(object oPC, int exploreFull)
{
}
// Updates exploration status for first entering an area, and gives bonus if applicable.
void FoundExploreArea(object oPC, string areaId)
{
}
// Reveals entered area to player, if it is explored.
// This is the case if the player has explored all landmarks or memorized has memorized a map of the area.
void RevealExploredArea(object oPC, string areaId, int exploreFull)
{
}
// Calls the ExploreNodeFound and RevealsExploredArea methods in succession.
// Convenience method for calling from within trigger onEnter script.
void WrapExploreNodeFound(object oPC, int exploreID, int exploreFull)
{
}
// Updates exploration status for first entering an exploration node/landmark.
void ExploreNodeFound(object oPC, int exploreID)
{
int exploration = GetPExplored(oPC, GetId(GetArea(oPC)));
}
// Handles reading or examining of a map. Returns text to show to player. Called from onActivated or (with events) from examining the map.
string ReadExploreMap(object oPC, object oMap)
{
string returnValue = "";
// int oMap = GetLocalInt
return returnValue;
}
// 'or' with total-1
// ***** ADMIN METHODS *****
void ResetServer(int delay=0)
{
// SendMessageToPC( GetFirstPC(), "Entering server reset" );
if (delay != 0)
{
object source = GetObjectByTag("SRVMSG");
AssignCommand(source, ActionSpeakString("Warning! Server will shut down in "+IntToString(delay*60)+" seconds!", TALKVOLUME_SHOUT) );
DelayCommand( IntToFloat(delay * 60), SetLocalString(GetModule(), "NWNX!RESETPLUGIN!SHUTDOWN", "0") );
}
else
{
// Reset
// TODO Log reset action
SetLocalString(GetModule(), "NWNX!RESETPLUGIN!SHUTDOWN", "0");
}
}
// ********** Persistance helper methods **********
// ***** QUEST RELATED METHODS *****
// Sets the quest-status for the given PC
void SetPQuestStatus(object obj, string questID, int status)
{
StorePInt( QUESTPROGRESS_TABLE, GetId(obj), questID, status );
}
// Gets the quest-status for the given PC
int GetPQuestStatus(object obj, string questID)
{
return FetchPInt(QUESTPROGRESS_TABLE, GetId(obj), questID );
}
// Sets the quest-status for the given PC
void SetGlobalPQuestStatus(string globalId, string questID, int status)
{
StorePInt( QUESTPROGRESS_TABLE, globalId, questID, status );
}
// Gets the quest-status for the given PC
int GetGlobalPQuestStatus(string globalId, string questID)
{
return FetchPInt(QUESTPROGRESS_TABLE, globalId, questID );
}
// ***** EXPLORATION RELATED METHODS *****
// Gets the mapexploration-status for the given PC
int GetPExplored(object obj, string areaID)
{
// SendMessageToPC(GetFirstPC(), "FetchPInt("+EXPLORATION_TABLE+", "+GetId(obj)+", "+areaID+");");
return FetchPInt(EXPLORATION_TABLE, GetId(obj), areaID );
}
// Sets the mapexploration-status for the given PC
void SetPExplored(object obj, string areaID, int status)
{
// SendMessageToPC(GetFirstPC(), "StorePInt("+EXPLORATION_TABLE+", "+GetId(obj)+", "+areaID+", "+IntToString(status)+");");
StorePInt(EXPLORATION_TABLE, GetId(obj), areaID, status );
}
// Updates the mapexploration-status for the given PC
void AddPExplored(object obj, string areaID, int status)
{
int oldValue = GetPExplored(obj, areaID);
int newValue = oldValue | status;
// SendMessageToPC(GetFirstPC(), "StorePInt("+EXPLORATION_TABLE+", "+GetId(obj)+", "+areaID+", "+IntToString(newValue)+");");
StorePInt(EXPLORATION_TABLE, GetId(obj), areaID, newValue );
}
// -----------------------------------------------------------
int NewValidate(object oPC)
{
return TRUE;
}
int LevelupValidate(object oPC)
{
return TRUE;
}
void StripPlayerOfItems(object oPC)
{
object item = GetFirstItemInInventory(oPC);
while (item != OBJECT_INVALID)
{
DestroyObject(item);
item = GetNextItemInInventory(oPC);
}
TakeGoldFromCreature(GetGold(oPC), oPC, TRUE);
}
int ProcessIncomingPlayer(object oPC)
{
string accountName = EncodeSpecialChars( GetPCPlayerName(oPC) );
string characterName = EncodeSpecialChars( GetName(oPC) );
string cdkey = GetPCPublicCDKey(oPC);
int id = ResolveUserID(accountName, characterName);
if (IsAccountOrKeyBanned( accountName, cdkey ) )
{
BootPC(oPC);
string errorMessage = "Warning - account "+accountName+" tried to login with key "+cdkey+". One of both of these are registered as banned!";
SendMessageToAllDMs(errorMessage);
DbLog(errorMessage, LOGTYPE_UNATHORIZED, "", LOGSUSPECT_VIOLATION);
}
else if ( id == 0 )
{
int validated = TRUE;
// Not in DB
// Verify correctness of new oPC
if (GetXP(oPC) != 0 )
{
string errorMessage = "Warning - account "+accountName+" tried to login with new character named "+characterName+", with more than 0 XP!";
SendMessageToAllDMs(errorMessage);
DbLog(errorMessage, LOGTYPE_UNATHORIZED, "", LOGSUSPECT_VIOLATION);
validated = FALSE;
}
validated = validated & NewValidate(oPC);
// Verify that the player has no token
if (validated)
{
// Strip char of start items
StripPlayerOfItems(oPC);
// Enter player in database
// Give token and start equipment/gold to player, and set XP to 1
SetXP(oPC, 1);
// Set local vars on character (corresponding to a ok login)
SetLocalInt(oPC, "auth", AUTH_PLAYER);
SetLocalInt(oPC, "id", ResolveUserID(accountName, characterName, cdkey) );
}
}
else
{
int validated = TRUE;
// Check for valid cd key
if (ResolveUserID(accountName, characterName, cdkey) == 0)
{
}
// Check for valid token
// Check for valid pass-code
}
if (GetXP(oPC) == 0)
{
object token = GetItemPossessedBy(oPC, "mn_servertoken");
}
return TRUE;
}
// This method deletes the contents of the given object's inventory from the database
void FlushPInventory(object container)
{
ClearTable(OBJECTSTORAGE_TABLE, GetTag(container), "");
}
// This method stores the contents of the given object's inventory to the database. Optional parameter empties the inventory afterwards.
// This version always flushes the existing inventory! (This to avoid having to implement logic for finding the next unique ID)
void StorePInventory(object container, int emptyInventoryAfterwards = TRUE)
{
// Flush persistant inventory
FlushPInventory(container);
// Store items persistently in inventory
int counter = 1;
object item = GetFirstItemInInventory(container);
object skip;
string containerid = GetTag(container);
{
while (GetIsObjectValid(item))
{
StorePObject(OBJECTSTORAGE_TABLE, containerid, "SLOT_"+IntToString(counter), item );
counter++;
if (GetHasInventory(item))
{
skip = GetFirstItemInInventory(item);
while (GetIsObjectValid(skip))
{
GetNextItemInInventory(container);
skip = GetNextItemInInventory(item);
}
}
item = GetNextItemInInventory(container);
}
}
if (emptyInventoryAfterwards == TRUE)
{
object item = GetFirstItemInInventory(container);
while (GetIsObjectValid(item))
{
DestroyObject(item);
item = GetNextItemInInventory(container);
}
}
}
// This method gets the contents of the given object's inventory from the database and adds it to the object's inventory.
void GetStoredPInventory(object container, int emptyInventoryBefore = FALSE)
{
// Empty local inventory before loading new stuff
if (emptyInventoryBefore)
{
object item = GetFirstItemInInventory(container);
while (GetIsObjectValid(item))
{
DestroyObject(item);
item = GetNextItemInInventory(container);
}
}
// Load persistent inventory into chest
int counter = 1;
int loop = TRUE;
object item;
string containerID = GetTag(container);
while (loop)
{
item = FetchPObject(OBJECTSTORAGE_TABLE, containerID, "SLOT_" + IntToString(counter), container );
if (!GetIsObjectValid(item))
loop = FALSE;
else
counter++;
}
}
string GetId(object obj)
{
string id = "";
if (GetIsObjectValid(obj))
{
if (GetIsDM(obj))
{
id = "DM";
}
else if (GetIsPC(obj))
{
id = GetLocalString(obj, OBJ_ID);
if (id == "")
{
if (PERSISTENCE_TYPE == PERSISTENCE_METHOD_BIOWARE_DB)
{
// If using bioware DB (for debugging), there is a limit to var length, so shorten id to name in this case.
// Note that this has the weakness of sharing variables between PCs with the same name.
// Also, malfunction is a risk for PCs with long names, since the varname + char name cannot exceed 32 characters.
id = GetName(obj);
}
else
{
// Generate full id
id = GetPCPublicCDKey(obj)+GetPCPlayerName(obj)+GetName(obj);
}
SetLocalString(obj, OBJ_ID, id);
}
}
else if (GetIsDMPossessed(obj) || GetIsPossessedFamiliar(obj))
{
id = "";
}
else if (GetIsAreaNatural(obj) != AREA_INVALID)
{
// Clumsy hack, in order to identify an area
// TODO Test if it works
string tempID = GetLocalString(obj, AREA_ID);
if (tempID == "")
{
tempID = GetResRef(obj);
}
id = tempID;
}
else
{
switch (GetObjectType(obj))
{
case OBJECT_TYPE_CREATURE:
id = GetLocalString(obj, OBJ_ID);
break;
}
}
}
return id;
}
object CreateItemOnObjectMN( string sItemTemplate, object oTarget=OBJECT_SELF, int nStackSize=1, string sNewTag="", string creationOptions = "" )
{
return CreateItemOnObject(sItemTemplate, oTarget, nStackSize, sNewTag);
}
// Helper method, converts varname plus indexes to the name of the variable to read from.
string ExchangeVar(string var, int index, int itemIndex = 0)
{
string returnValue = EXCHANGE_PREFIX+IntToString(index)+var+((itemIndex == 0)?"":IntToString(itemIndex));
// Debug(returnValue);
return returnValue;
}
int CheckExchangeBlocked(object PC, object NPC, int index = 1)
{
int returnValue = 0;
// Init variables
int takenGoldAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_GOLD, index ) );
int takenXpAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_XP, index ) );
string questId = GetLocalString(NPC, ExchangeVar( EXCHANGE_QUEST_ID, index ) );
int questTimes = GetLocalInt(NPC, ExchangeVar( EXCHANGE_QUEST_REPEAT, index ) );
int minLevel = GetLocalInt(NPC, ExchangeVar( EXCHANGE_MIN_LEVEL, index ) );
int maxLevel = GetLocalInt(NPC, ExchangeVar( EXCHANGE_MAX_LEVEL, index ) );
int allowBatch = GetLocalInt(NPC, ExchangeVar( EXCHANGE_ALLOW_BATCH, index ) );
int givenGoldAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_GOLD, index ) );
int givenXpAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_XP, index ) );
string unlockQuest = GetLocalString(NPC, ExchangeVar( EXCHANGE_OPEN_QUEST, index ) );
int allowUnknown = GetLocalInt(NPC, ExchangeVar( EXCHANGE_UNKNOWN_OK, index ) );
int hasJournalEntry = GetLocalInt(NPC, ExchangeVar( EXCHANGE_JOURNAL, index ) );
// Perform checks
// Check if has enough gold, if applicable
if (takenGoldAmount >= 1)
{
int hasPrereq = (GetGold(PC) >= takenGoldAmount);
if (!hasPrereq)
{
returnValue = returnValue | EXCHANGE_INSUFFICIENT_GOLD;
}
}
// Check if have enough XP to take, if applicable
if (takenXpAmount >= 1)
{
int hasPrereq = (GetXP(PC) >= takenXpAmount);
if (!hasPrereq)
{
returnValue = returnValue | EXCHANGE_INSUFFICIENT_XP;
}
}
// Check if fulfills minimum level requirement, if applicable
if (minLevel >= 2)
{
int hasPrereq = (GetLevelFromXP(GetXP(PC)) >= minLevel);
if (!hasPrereq)
{
returnValue = returnValue | EXCHANGE_LEVEL_TOO_LOW;
}
}
// Check if fulfills max level requirement, if applicable
if (maxLevel >= 1)
{
int hasPrereq = (GetLevelFromXP(GetXP(PC)) <= maxLevel);
if (!hasPrereq)
{
returnValue = returnValue | EXCHANGE_LEVEL_TOO_HIGH;
}
}
// Check if quest not already completed
if (questId != "")
{
int questStatus = GetQuestStatus ( PC, questId );
// Debug(questId+"!!!"+IntToString(questStatus));
int numberPerformed = 0;
if (questStatus > 20)
{
numberPerformed = questStatus - 20;
}
if (numberPerformed > 0)
{
if (numberPerformed >= questTimes)
{
returnValue = returnValue | EXCHANGE_QUEST_ALREADY_COMPLETED;
}
}
else
{
switch (questStatus)
{
case QUEST_COMPLETED:
returnValue = returnValue | EXCHANGE_QUEST_ALREADY_COMPLETED;
break;
case QUEST_BLOCKED:
returnValue = returnValue | EXCHANGE_QUEST_BLOCKED;
break;
case QUEST_FAILED:
returnValue = returnValue | EXCHANGE_QUEST_FAILED;
break;
case QUEST_REJECTED:
returnValue = returnValue | EXCHANGE_QUEST_REJECTED;
break;
case QUEST_UNKNOWN:
if (!allowUnknown)
{
returnValue = returnValue | EXCHANGE_QUEST_UNKNOWN;
}
break;
}
}
}
// Check if in possession of needed objects
// Logic: If atomic item (only one item, with amount = 1), check for itemPossessedBy
// If missing, abort with error.
// If multiple items, loop through inventory, accumulating count (count stacks)
// If by end of inventory, any count is less than amount, abort with error.
// Supports up to 9 items
string takeItemTag1 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 1 ));
int takeItemAmount1 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 1 ));
int itemCount1 = 0;
string takeItemTag2 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 2 ));
int takeItemAmount2 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 2 ));
int itemCount2 = 0;
string takeItemTag3 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 3 ));
int takeItemAmount3 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 3 ));
int itemCount3 = 0;
string takeItemTag4 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 4 ));
int takeItemAmount4 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 4 ));
int itemCount4 = 0;
string takeItemTag5 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 5 ));
int takeItemAmount5 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 5 ));
int itemCount5 = 0;
string takeItemTag6 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 6 ));
int takeItemAmount6 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 6 ));
int itemCount6 = 0;
string takeItemTag7 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 7 ));
int takeItemAmount7 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 7 ));
int itemCount7 = 0;
string takeItemTag8 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 8 ));
int takeItemAmount8 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 8 ));
int itemCount8 = 0;
string takeItemTag9 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 9 ));
int takeItemAmount9 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 9 ));
int itemCount9 = 0;
if (takeItemTag1 != "")
{
if (takeItemTag2 == "" && takeItemAmount1 <= 1)
{
takeItemAmount1 = 1;
}
/*
if (takeItemTag2 == "" && takeItemAmount1 <= 1)
{
// Simple check - Check directly
object oItem = GetItemPossessedBy(PC, takeItemTag1);
if ( !GetIsObjectValid(oItem) )
{
returnValue = returnValue | EXCHANGE_INSUFFICIENT_ITEMS;
}
}
else
{ */
// Advanced check - loop through entire inventory
object item = GetFirstItemInInventory(PC);
while (GetIsObjectValid(item))
{
string tag = GetTag(item);
int stackSize = GetNumStackedItems(item);
if (tag == takeItemTag1)
{
itemCount1 += stackSize;
}
else if (tag == takeItemTag2)
{
itemCount2 += stackSize;
}
else if (tag == takeItemTag3)
{
itemCount3 += stackSize;
}
else if (tag == takeItemTag4)
{
itemCount4 += stackSize;
}
else if (tag == takeItemTag5)
{
itemCount5 += stackSize;
}
else if (tag == takeItemTag6)
{
itemCount6 += stackSize;
}
else if (tag == takeItemTag7)
{
itemCount7 += stackSize;
}
else if (tag == takeItemTag8)
{
itemCount8 += stackSize;
}
else if (tag == takeItemTag9)
{
itemCount9 += stackSize;
}
item = GetNextItemInInventory(PC);
}
int tooFew = (itemCount1<takeItemAmount1) ||(itemCount2<takeItemAmount2) ||(itemCount3<takeItemAmount3) ||(itemCount4<takeItemAmount4) ||(itemCount5<takeItemAmount5) ||(itemCount6<takeItemAmount6) ||(itemCount7<takeItemAmount7) ||(itemCount8<takeItemAmount8) ||(itemCount9<takeItemAmount9);
if (tooFew)
{
returnValue = returnValue | EXCHANGE_INSUFFICIENT_ITEMS;
}
// }
}
return returnValue;
}
int TakeItem(object PC, object item, int stackSize, int takeAmount)
{
if (takeAmount > 0)
{
if (stackSize <= takeAmount)
{
// Remove entire stack
takeAmount -= stackSize;
DestroyPersistentVarsOnObject ( GetId(item) );
ActionTakeItem( item, PC );
DestroyObject(item);
}
else
{
// Remove part of stack
SetItemStackSize(item, stackSize - takeAmount);
takeAmount = 0;
}
}
return takeAmount;
}
// Helper method for PerformExchange. Wraps CreateItemOnObjectMN,
// but increases number to 1 if lower than 1, returns
// without doing anything if resref is "", and loops through
// item creation rather than setting stack size.
void GiveItemToPC(object PC, string resref, int number = 1)
{
if (resref == "")
{
return;
}
if (number < 1)
{
number = 1;
}
int i;
for (i = 0; i < number; i++)
{
CreateItemOnObjectMN(resref, PC, 1, "", "");
}
}
void PerformExchange(object PC, object NPC, int index = 1)
{
// We check again to prevent cheats
if ( !CheckExchangeBlocked(PC, NPC, index) )
{
// Step 1: Init variables
int takenGoldAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_GOLD, index ) );
int takenXpAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_XP, index ) );
string questId = GetLocalString(NPC, ExchangeVar( EXCHANGE_QUEST_ID, index ) );
int questTimes = GetLocalInt(NPC, ExchangeVar( EXCHANGE_QUEST_REPEAT, index ) );
int minLevel = GetLocalInt(NPC, ExchangeVar( EXCHANGE_MIN_LEVEL, index ) );
int maxLevel = GetLocalInt(NPC, ExchangeVar( EXCHANGE_MAX_LEVEL, index ) );
int allowBatch = GetLocalInt(NPC, ExchangeVar( EXCHANGE_ALLOW_BATCH, index ) );
int givenGoldAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_GOLD, index ) );
int givenXpAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_XP, index ) );
string unlockQuest = GetLocalString(NPC, ExchangeVar( EXCHANGE_OPEN_QUEST, index ) );
int visualEffect = GetLocalInt(NPC, ExchangeVar( EXCHANGE_VISUALEFFECT, index ) );
// Display visual effect
if (visualEffect != 0)
{
effect vfx = EffectVisualEffect(visualEffect);
ApplyEffectToObject( DURATION_TYPE_INSTANT, vfx, PC);
}
// Step 2: Take cost
// Take gold
if (takenGoldAmount > 0)
{
TakeGoldFromCreature(takenGoldAmount, PC, TRUE);
}
// Take XP - this will lower PC's level - beware of exploits, BETA!
if (takenXpAmount > 0)
{
SetXP(PC, GetXP(PC)-takenXpAmount);
}
// Increment quest if applicable, and mark quest as completed, if applicable
int completed = FALSE;
if (questId != "")
{
int questStatus = GetQuestStatus(PC, questId);
int completedTimes = GetQuestCompletedTimes(PC, questId);
if (questTimes == -1)
{
// This quest can be performed an unlimited amount of times,
// so do nothing
}
else if (questTimes > 0 )
{
if ( completedTimes > 0 )
{
completedTimes++;
}
else
{
completedTimes = 1;
}
if (completedTimes >= questTimes)
{
// Quest is completed
SetQuestStatus(PC, questId, QUEST_COMPLETED);
completed = TRUE;
}
else
{
// Increment quest
SetQuestCompletedTimes(PC, questId, completedTimes);
}
}
else
{
// One time quest
SetQuestStatus(PC, questId, QUEST_COMPLETED);
completed = TRUE;
}
}
// Begin unlocked quest, if applicable
// (we use the "completed" variable from earlier, so that the quest is
// only unlocked after the last iteration of a multiple iteration quest)
if ( completed && unlockQuest != "" )
{
SetQuestStatus(PC, unlockQuest, QUEST_BEGUN);
}
// take item or items
string takeItemTag1 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 1 ));
int takeItemAmount1 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 1 ));
if (takeItemTag1 != "" && takeItemAmount1 == 0)
{
takeItemAmount1 = 1;
}
string takeItemTag2 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 2 ));
int takeItemAmount2 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 2 ));
if (takeItemTag2 != "" && takeItemAmount2 == 0)
{
takeItemAmount2 = 1;
}
string takeItemTag3 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 3 ));
int takeItemAmount3 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 3 ));
if (takeItemTag3 != "" && takeItemAmount3 == 0)
{
takeItemAmount3 = 1;
}
string takeItemTag4 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 4 ));
int takeItemAmount4 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 4 ));
if (takeItemTag4 != "" && takeItemAmount4 == 0)
{
takeItemAmount4 = 1;
}
string takeItemTag5 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 5 ));
int takeItemAmount5 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 5 ));
if (takeItemTag5 != "" && takeItemAmount5 == 0)
{
takeItemAmount5 = 1;
}
string takeItemTag6 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 6 ));
int takeItemAmount6 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 6 ));
if (takeItemTag6 != "" && takeItemAmount6 == 0)
{
takeItemAmount6 = 1;
}
string takeItemTag7 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 7 ));
int takeItemAmount7 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 7 ));
if (takeItemTag7 != "" && takeItemAmount7 == 0)
{
takeItemAmount7 = 1;
}
string takeItemTag8 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 8 ));
int takeItemAmount8 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 8 ));
if (takeItemTag8 != "" && takeItemAmount8 == 0)
{
takeItemAmount8 = 1;
}
string takeItemTag9 = GetLocalString(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM, index, 9 ));
int takeItemAmount9 = GetLocalInt(NPC, ExchangeVar( EXCHANGE_TAKE_ITEM_AMOUNT, index, 9 ));
if (takeItemTag9 != "" && takeItemAmount9 == 0)
{
takeItemAmount9 = 1;
}
if (takeItemTag1 != "")
{
RemoveXItemsByTag(PC, takeItemTag1, takeItemAmount1);
}
if (takeItemTag2 != "")
{
RemoveXItemsByTag(PC, takeItemTag2, takeItemAmount2);
}
if (takeItemTag3 != "")
{
RemoveXItemsByTag(PC, takeItemTag3, takeItemAmount3);
}
if (takeItemTag4 != "")
{
RemoveXItemsByTag(PC, takeItemTag4, takeItemAmount4);
}
if (takeItemTag5 != "")
{
RemoveXItemsByTag(PC, takeItemTag5, takeItemAmount5);
}
if (takeItemTag6 != "")
{
RemoveXItemsByTag(PC, takeItemTag6, takeItemAmount6);
}
if (takeItemTag7 != "")
{
RemoveXItemsByTag(PC, takeItemTag7, takeItemAmount7);
}
if (takeItemTag8 != "")
{
RemoveXItemsByTag(PC, takeItemTag8, takeItemAmount8);
}
if (takeItemTag9 != "")
{
RemoveXItemsByTag(PC, takeItemTag9, takeItemAmount9);
}
/*
if (takeItemTag2 == "" && takeItemAmount1 <= 1)
{
// Simple check - Check directly
object oItem = GetItemPossessedBy(PC, takeItemTag1);
if ( GetIsObjectValid(oItem) )
{
int iNumStackedItems = GetNumStackedItems(oItem);
if (iNumStackedItems>takeItemAmount1)
{
SetItemStackSize(oItem,iNumStackedItems-takeItemAmount1);
}
else
{
DestroyPersistentVarsOnObject ( GetId(oItem) );
ActionTakeItem( oItem, PC );
DestroyObject(oItem);
}
// SetLocalObject(OBJECT_SELF, "DELETE_ITEM", oItem);
// ExecuteScript("util_deleteitem", OBJECT_SELF);
}
}
else
{
// Advanced check - loop through entire inventory
object item = GetFirstItemInInventory(PC);
while (GetIsObjectValid(item))
{
string tag = GetTag(item);
int stackSize = GetNumStackedItems(item);
if (tag == takeItemTag1)
{
takeItemAmount1 = TakeItem(PC, item, stackSize, takeItemAmount1);
}
else if (tag == takeItemTag2)
{
takeItemAmount2 = TakeItem(PC, item, stackSize, takeItemAmount2);
}
else if (tag == takeItemTag3)
{
takeItemAmount3 = TakeItem(PC, item, stackSize, takeItemAmount3);
}
else if (tag == takeItemTag4)
{
takeItemAmount4 = TakeItem(PC, item, stackSize, takeItemAmount4);
}
else if (tag == takeItemTag5)
{
takeItemAmount5 = TakeItem(PC, item, stackSize, takeItemAmount5);
}
else if (tag == takeItemTag6)
{
takeItemAmount6 = TakeItem(PC, item, stackSize, takeItemAmount6);
}
else if (tag == takeItemTag7)
{
takeItemAmount7 = TakeItem(PC, item, stackSize, takeItemAmount7);
}
else if (tag == takeItemTag8)
{
takeItemAmount8 = TakeItem(PC, item, stackSize, takeItemAmount8);
}
else if (tag == takeItemTag9)
{
takeItemAmount9 = TakeItem(PC, item, stackSize, takeItemAmount9);
}
if (takeItemAmount1+takeItemAmount2+takeItemAmount3+takeItemAmount4+takeItemAmount5+takeItemAmount6+takeItemAmount7+takeItemAmount8+takeItemAmount9 == 0)
{
break;
}
item = GetNextItemInInventory(PC);
}
}
*/
// Step 3: Give reward
// Give gold
if (givenGoldAmount > 0)
{
GiveGoldToCreature(PC, givenGoldAmount);
}
// Give XP
if (givenXpAmount > 0)
{
SetXP(PC, GetXP(PC)+givenXpAmount);
}
// Give items
string giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 1) );
int giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 1) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 2) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 2) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 3) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 3) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 4) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 4) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 5) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 5) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 6) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 6) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 7) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 7) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 8) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 8) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
giveItemResref = GetLocalString(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM, index, 9) );
giveItemAmount = GetLocalInt(NPC, ExchangeVar( EXCHANGE_GIVE_ITEM_AMOUNT, index, 9) );
GiveItemToPC(PC, giveItemResref, giveItemAmount);
}
}
int GetLevelFromXP(int xp)
{
return FloatToInt(0.5 + sqrt(0.25 + ( IntToFloat(xp) / 500 )));
}
int GetXPMinForLevel(int level)
{
return ((level) *(level-1) *500);
}
int GetXPNeededForLevel(int currentXP, int wantedLevel = 0)
{
int minNeeded;
if (wantedLevel == 0)
{
minNeeded = GetXPMinForLevel(GetLevelFromXP(currentXP)+1);
}
else
{
minNeeded = GetXPMinForLevel(wantedLevel);
}
return minNeeded-currentXP;
}
// Wraps around getLocalInt, so that marker variables can be imported from database
int GetMarkerInt(object obj, string varname, int persistantVar)
{
int result = GetLocalInt(obj, varname);
if (persistantVar && PERSIST_LOCAL_MARKERS && PERSIST_LOCAL_MARKERS_LAZY_INIT && result == 0)
{
result = FetchPInt(MARKERS_TABLE, GetId(obj), varname );
SetLocalInt(obj, varname, result);
}
return result;
}
float GetMarkerFloat(object obj, string varname, int persistantVar)
{
float result = GetLocalFloat(obj, varname);
if (persistantVar && PERSIST_LOCAL_MARKERS && PERSIST_LOCAL_MARKERS_LAZY_INIT && result == 0.0f)
{
result = FetchPFloat(MARKERS_TABLE, GetId(obj), varname );
SetLocalFloat(obj, varname, result);
}
return result;
}
// Wraps around getLocalString, so that marker variables can be imported from database
string GetMarkerString(object obj, string varname, int persistantVar)
{
string result = GetLocalString(obj, varname);
if (persistantVar && PERSIST_LOCAL_MARKERS && PERSIST_LOCAL_MARKERS_LAZY_INIT && result == "")
{
result = FetchPString(MARKERS_TABLE, GetId(obj), varname );
SetLocalString(obj, varname, result);
}
return result;
}
// Wraps around setLocalInt, so that marker variables can be exported to database
void SetMarkerInt(object obj, string varname, int value, int persistantVar)
{
SetLocalInt(obj, varname, value);
if (persistantVar && PERSIST_LOCAL_MARKERS)
{
StorePInt(MARKERS_TABLE, GetId(obj), varname, value);
}
}
void SetMarkerFloat(object obj, string varname, float value, int persistantVar)
{
SetLocalFloat(obj, varname, value);
if (persistantVar && PERSIST_LOCAL_MARKERS)
{
StorePFloat(MARKERS_TABLE, GetId(obj), varname, value);
}
}
// Wraps around setLocalString, so that marker variables can be exported to database
void SetMarkerString(object obj, string varname, string value, int persistantVar)
{
SetLocalString(obj, varname, value);
if (persistantVar && PERSIST_LOCAL_MARKERS)
{
StorePString(MARKERS_TABLE, GetId(obj), varname, value);
}
}
// Wraps around DeleteLocalString, so that marker variables can be deleted from database
void DeleteMarkerString(object obj, string varname, int persistantVar)
{
DeleteLocalString(obj, varname);
if (persistantVar && PERSIST_LOCAL_MARKERS)
{
DeletePString(MARKERS_TABLE, GetId(obj), varname);
}
}
// Wraps around DeleteLocalFloat, so that marker variables can be deleted from database
void DeleteMarkerFloat(object obj, string varname, int persistantVar)
{
DeleteLocalFloat(obj, varname);
if (persistantVar && PERSIST_LOCAL_MARKERS)
{
DeletePString(MARKERS_TABLE, GetId(obj), varname);
}
}
// Wraps around DeleteLocalInt, so that marker variables can be deleted from database
void DeleteMarkerInt(object obj, string varname, int persistantVar)
{
DeleteLocalInt(obj, varname);
if (persistantVar && PERSIST_LOCAL_MARKERS)
{
DeletePString(MARKERS_TABLE, GetId(obj), varname);
}
}
void Debug(string msg)
{
// object oPC = GetFirstPC();
// FloatingTextStringOnCreature(msg, oPC, FALSE); Disable debug in production
}
//void main(){}