#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 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(){}