Added persistent player storage

Added persistent player storage.  Fixed store items.  Full compile.  Updated release archive.
This commit is contained in:
Jaysyn904
2025-03-09 20:14:36 -04:00
parent e9f1a582bc
commit 379c5dce03
1074 changed files with 30048 additions and 435 deletions

View File

@@ -0,0 +1,44 @@
void main()
{
//:: Declare major variables
object oPC = GetPCSpeaker();
object oBaseArea = GetObjectByTag("DBI_PC_ROOMS");
string sPCName = GetName(oPC);
string sBaseResRef = GetResRef(oBaseArea);
string sBaseName = GetName(oBaseArea);
string sNewTag = sPCName+sBaseResRef;
string sNewName = sPCName+"'s Room at the Dragonback Inn";
//:: Create instanced area from player's information
CreateArea(sBaseResRef, sNewTag, sNewName);
//:: Get information about new area instance
object oNewRoom = GetObjectByTag(sNewTag);
string sNewResRef = GetResRef(oNewRoom);
location lEntry = GetLocation(GetObjectByTag("DBI_ROOM_MAT"));
//:: Create entry waypoint in new area instance
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lEntry, FALSE, "WP_"+sNewTag);
//:: Make sure the area is valid
object oTarget;
location lTarget;
oTarget = GetWaypointByTag("WP_"+sNewTag);
lTarget = GetLocation(oTarget);
if (GetAreaFromLocation(lTarget)==OBJECT_INVALID)
{
SendMessageToPC(oPC, "Warning: Area does not exist!");
return;
}
//:: Move PC to instanced area.
AssignCommand(oPC, ClearAllActions());
AssignCommand(oPC, ActionJumpToLocation(lTarget));
}

View File

@@ -0,0 +1,44 @@
void main()
{
//:: Declare major variables
object oPC = GetPCSpeaker();
object oBaseArea = GetObjectByTag("IFM_PC_ROOMS");
string sPCName = GetName(oPC);
string sBaseResRef = GetResRef(oBaseArea);
string sBaseName = GetName(oBaseArea);
string sNewTag = sPCName+sBaseResRef;
string sNewName = sPCName+"'s Room at the Flying Monkey";
//:: Create instanced area from player's information
CreateArea(sBaseResRef, sNewTag, sNewName);
//:: Get information about new area instance
object oNewRoom = GetObjectByTag(sNewTag);
string sNewResRef = GetResRef(oNewRoom);
location lEntry = GetLocation(GetObjectByTag("PC_ROOM_MAT"));
//:: Create entry waypoint in new area instance
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lEntry, FALSE, "WP_"+sNewTag);
//:: Make sure the area is valid
object oTarget;
location lTarget;
oTarget = GetWaypointByTag("WP_"+sNewTag);
lTarget = GetLocation(oTarget);
if (GetAreaFromLocation(lTarget)==OBJECT_INVALID)
{
SendMessageToPC(oPC, "Warning: Area does not exist!");
return;
}
//:: Move PC to instanced area.
AssignCommand(oPC, ClearAllActions());
AssignCommand(oPC, ActionJumpToLocation(lTarget));
}

View File

@@ -0,0 +1,10 @@
void main()
{
object oPC = GetClickingObject();
if (!GetIsPC(oPC)) return;
ActionStartConversation(oPC, "");
}

View File

@@ -5,9 +5,16 @@
// multiple handlers for the onmoduleload event.
//
/////////////////////////////////////////////////////////////////////
#include "nui_i_main"
void main()
{
ExecuteScript("prc_onmodload", OBJECT_SELF);
SetEventScript(GetModule(), EVENT_SCRIPT_MODULE_ON_NUI_EVENT, "mod_events");
SetEventScript(GetModule(), EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET, "mod_events");
NUI_Initialize();
NUI_HandleEvents();
ExecuteScript("prc_onmodload", OBJECT_SELF);
ExecuteScript("trin_modload", OBJECT_SELF);
}

View File

@@ -0,0 +1,23 @@
#include "nui_i_main"
void main()
{
int nEvent = GetCurrentlyRunningEvent();
if (nEvent == EVENT_SCRIPT_MODULE_ON_MODULE_LOAD)
{
SetEventScript(GetModule(), EVENT_SCRIPT_MODULE_ON_NUI_EVENT, "mod_events");
SetEventScript(GetModule(), EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET, "mod_events");
NUI_Initialize();
NUI_HandleEvents();
}
else if (nEvent == EVENT_SCRIPT_MODULE_ON_NUI_EVENT)
{
NUI_HandleEvents();
}
else if (nEvent == EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET)
{
NUI_HandleEvents(GetLastPlayerToSelectTarget());
}
}

View File

@@ -0,0 +1,6 @@
#include "nui_i_main"
void main()
{
NUI_HandleEvents();
}

View File

@@ -0,0 +1,8 @@
#include "nui_i_main"
void main()
{
ExecuteScript("prc_onplaytarget");
NUI_HandleEvents(GetLastPlayerToSelectTarget());
}

View File

@@ -0,0 +1,50 @@
/// ----------------------------------------------------------------------------
/// @file nui_c_config.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief NUI Form Creation and Management System Configuration
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// FORM INSPECTOR
// -----------------------------------------------------------------------------
// The form inspection capability allows you to record data about a form's status,
// including bind values and how various events affect them. To use the inspection
// capability, you must include the `nui_f_inspector` formfile in your module and
// set NUI_FI_RECORD_DATA to the desired value.
// *** WARNING *** Form inspection is very expensive. Is it recommended that
// you only use the form inspection capability during debug or set this
// value to NUI_FI_WHEN_OPEN to prevent unnecessary traffic.
const int NUI_FI_NEVER = 0;
const int NUI_FI_ALWAYS = 1;
const int NUI_FI_WHEN_OPEN = 2;
const int NUI_FI_RECORD_DATA = NUI_FI_WHEN_OPEN;
// -----------------------------------------------------------------------------
// Custom Functions
// MODIFY THESE TO ENSURE COMPATIBILTY WITH YOUR MODULE'S SYSTEMS
// -----------------------------------------------------------------------------
// This system has some basic debugging functionality. In order to make this system work
// under multiple modules without collision, the following function is provided to
// allow you to call whatever debugging system you'd like to call. If you use
// squattingmonk's debug utility, no changes need to be made. The primary system does
// not provide any organic debug calls, but this function is available to all formfiles
// for debugging purposes.
//
const int NUI_DEBUG_SEVERITY_NONE = 0;
const int NUI_DEBUG_SEVERITY_CRITICAL = 1;
const int NUI_DEBUG_SEVERITY_ERROR = 2;
const int NUI_DEBUG_SEVERITY_WARNING = 3;
const int NUI_DEBUG_SEVERITY_NOTICE = 4;
const int NUI_DEBUG_SEVERITY_DEBUG = 5;
//#include "util_i_debug"
void NUI_Debug(string sMessage, int nSeverity = 4)
{
//Debug(sMessage, nSeverity);
}

View File

@@ -0,0 +1,225 @@
/// ----------------------------------------------------------------------------
/// @file nui_c_storage.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Persistent Storage formfile configuration settings
/// ----------------------------------------------------------------------------
/// @note Most of the following global/default settings can be overriden on a
/// per-container basis by setting a local variable on the container
/// object as noted in the setting descriptions below. Constants, such as
/// PS_TRUE, PS_NONE, etc. should be used in this configuration file, however,
/// actual values must be used when setting local overrides. Those values
/// are provided below.
/// @warning This system uses player character UUIDs. This can cause issues in
/// single-player modules if the player-character is not exported and the
/// UUID is not saved to the .bic file.
/// @brief By default, containers will be saved to a database table referenced
/// by the container object's tag. The saved item data includes the UUID
/// and CD Key of the PC that deposited the item so the same container can
/// be used for multiple PCs. To set a specific (unique) name to use as the
/// table name instead, set a local string called `PS_UNIQUE_ID` on the
/// container object and set it to any unique string.
/// @brief Determines usage of the `Search` button.
/// Configuration File:
/// PS_TRUE to force players to click the `Search` button
/// before an inventory search will commence. This is a good idea
/// for containers that you expect will have large inventories.
/// PS_FALSE to allow real-time searching as the player types
/// characters into the `Search` textbox.
/// Local Override (int): PS_FORCE_SEARCH_BUTTON
/// 1 = PS_TRUE
/// -1 = PS_FALSE
const int PS_FORCE_SEARCH_BUTTON_DEFAULT = PS_FALSE;
/// @brief Determines whether item object state is saved to the database. The
/// object state includes variables and effects.
/// Configuration File:
/// PS_TRUE saves the object state
/// PS_FALSE does not save the object state
/// Local Override (int): PS_FORCE_OBJECT_STATE
/// 1 = PS_TRUE
/// -1 = PS_FALSE
const int PS_FORCE_OBJECT_STATE_DEFAULT = PS_TRUE;
/// @brief Sets the item storage limit.
/// Configuration File:
/// PS_UNLIMITED to allow unlimited item storage.
/// Set to any positive integer to limit item storage to that amount.
/// Local Override (int): PS_STORAGE_LIMIT
/// -1 = PS_UNLIMITED
/// Set to any positive integer to limit item storage to that amount.
const int PS_STORAGE_LIMIT_DEFAULT = 500;
/// @brief Set the maximum distance (meters) a PC can travel from the container
/// before the form will auto-close.
/// Configuration File:
/// PS_UNLIMITED_DISTANCE to never auto-close the form
/// Set to any positive float to limit distance to that amount.
/// Local Override (float): PS_DISTANCE
/// -1.0 = PS_UNLIMITED_DISTANCE
/// Set to any positive float to limit distance to that amount.
const float PS_DISTANCE_DEFAULT = 2.0;
/// @brief Set the container access type. Container inventories can be accessed
/// by two methods: exclusive and contentious.
/// - Exclusive: Multiple players may open the same container, but each
/// player will only see items they've previously deposited. Players
/// may only withdraw items they've previously deposited.
/// - Contentious: Multiple players may open the same container. All
/// players will see all items deposited by any player. Any player
/// can remove any item, regardless of who originally deposited the
/// item.
///
/// Configuration File:
/// PS_ACCESS_EXCLUSIVE for exclusive access
/// PS_ACCESS_CONTENTIOUS for contentious access
/// Local Override (int): PS_ACCESS_TYPE
/// 1 = PS_ACCESS_EXCLUSIVE
/// 2 = PS_ACCESS_CONTENTIOUS
const int PS_ACCESS_TYPE_DEFAULT = PS_ACCESS_EXCLUSIVE;
/// @brief Set the container type. Containers can be of multiple types:
/// - Public: Any player can open, deposit and withdraw items from this
/// container. Whether they are limited to specific items is dependant
/// on the container's access setting.
/// - Character: Creates a 'portable' storage container for any player
/// character. Any container of this type will hold the same inventory
/// for any specific player.
/// - CD Key: Creates a 'portable' storage container for any characters
/// owned by cd-key associated with the player character. Any container
/// of this type will hold the inventory desposited by any character
/// sharing the player's cd key.
///
/// Configuration File:
/// PS_CONTAINER_PUBLIC for public.
/// PS_CONTAINER_CHARACTER for per-character.
/// PS_CONTAINER_CD_KEY for per-cdkey.
/// Local Override (int): PS_CONTAINER_TYPE
/// 1 = PS_CONTAINER_PUBLIC
/// 2 = PS_CONTAINER_CHARACTER
/// 3 = PS_CONTAINER_CDKEY
const int PS_CONTAINER_TYPE_DEFAULT = PS_CONTAINER_CDKEY;
/// @brief Set the default container type, if the container is an item. Containers
/// can be of multiple types:
/// - Public: Any player can open, deposit and withdraw items from this
/// container. Whether they are limited to specific items is dependant
/// on the container's access setting.
/// - Character: Creates a 'portable' storage container for any player
/// character. Any container of this type will hold the same inventory
/// for any specific player.
/// - CD Key: Creates a 'portable' storage container for any characters
/// owned by cd-key associated with the player character. Any container
/// of this type will hold the inventory desposited by any character
/// sharing the player's cd key.
///
/// Configuration File:
/// PS_CONTAINER_PUBLIC for public.
/// PS_CONTAINER_CHARACTER for per-character.
/// PS_CONTAINER_CD_KEY for per-cdkey.
/// Local Override (int): PS_CONTAINER_TYPE
/// 1 = PS_CONTAINER_PUBLIC
/// 2 = PS_CONTAINER_CHARACTER
/// 3 = PS_CONTAINER_CDKEY
const int PS_CONTAINER_ITEM_TYPE_DEFAULT = PS_CONTAINER_CDKEY;
/// @brief Determines whether the player's inventory window will be opened
/// when a container is opened.
/// Configuration File:
/// PS_TRUE to open the inventory window.
/// PS_FALSE to prevent the window from opening. If the inventory
/// window is already open, this will not close it.
/// Local Override (int): PS_OPEN_INVENTORY
/// 1 = PS_TRUE
/// -1 = PS_FALSE
const int PS_OPEN_INVENTORY_DEFAULT = PS_TRUE;
/// @brief Determines the maximum amount of gold a container can store.
/// If the container is set to store no gold, the form controls that
/// manipulate gold storage will not be visible on the form.
/// Configuration File:
/// PS_UNLIMITED to allow unlimited gold storage.
/// PS_NONE to prevent gold storage.
/// Set to any positive integer to limit gold to that amount.
/// Local Override (int): PS_MAX_GOLD
/// -1 = PS_UNLIMITED
/// -2 = PS_NONE
/// Set to any positive integer to limit gold to that amount.
const int PS_MAX_GOLD_DEFAULT = 2000000000;
/// @note Reference these terms for the following option:
/// Container: A persistent storage object in the game, such as a chest.
/// Container Item: An item which:
/// Can have its own inventory
/// Can be carried in a player's inventory
/// @brief Determines handling for container objects. Containers can optionally
/// store container items.
/// Configuration File:
/// PS_UNLIMITED to allow storage of an unlimited number of container items.
/// PS_NONE to prevent storage of any container items.
/// Set to any positive integer to limit storage to that number of container
/// items.
/// Local Override (int): PS_MAX_CONTAINER_TIMES
/// -1 = PS_UNLIMITED
/// -2 = PS_NONE
/// Set to any positive integer to limit storage to that number of container
/// items.
const int PS_MAX_CONTAINER_ITEMS_DEFAULT = 10;
/// @brief Determines how many items can be stored in stored container items.
/// Configuration File:
/// PS_UNLIMITED to allow storage of any number of items within a container
/// item's inventory. This will be naturally limited by the size of
/// the container item's inventory.
/// PS_NONE to prevent any items from being stored in a container item. If
/// container item storage is allow and PS_NONE is used, only empty
/// container items can be stored.
/// Set to any positive integer to limit the number of items stored in
/// the inventory of a container item.
/// Local Override (int): PS_MAX_CONTAINER_ITEMS_INVENTORY
/// -1 = PS_UNLIMITED
/// -2 = PS_NONE
/// Set to any positive integer to limit the number of items stored in
/// the inventory of a container item.
///
/// @note This configuration option has no effect if PS_MAX_CONTAINER_ITEMS_DEFAULT
/// is set to PS_NONE or its local override is set to -1.
/// @warning Items that fail check involved with container item limitations do
/// not have default messages reported to the player. The item will simply fail
/// to be stored.
const int PS_MAX_CONTAINER_ITEMS_INVENTORY_DEFAULT = 100;
/// @brief Creates the form's title.
/// @param oContainer The container object being used.
/// @param oPC The player using oContainer.
/// @note A local string called `PS_TITLE` may be set on the object for easy
/// reference in this function. The function below is an example and may
/// be modified in any way. The returned value will be displayed as the
/// form's title.
string ps_GetFormTitle(object oContainer, object oPC, int nAccess, int nType)
{
string sTitle;
if ((sTitle = GetLocalString(oContainer, PS_TITLE)) != "")
return sTitle;
else
{
switch (nType)
{
case PS_CONTAINER_PUBLIC:
return GetTag(oContainer);
case PS_CONTAINER_CDKEY:
return GetName(oPC) + "'s Player-Specific Storage";
case PS_CONTAINER_CHARACTER:
return GetName(oPC) + "'s Character-Specific Storage";
}
if (GetIsPC(OBJECT_SELF))
return GetName(OBJECT_SELF) + "'s Storage";
}
return "Persistent Storage";
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/// ----------------------------------------------------------------------------
/// @file nui_i_library.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Boilerplate code for creating a library dispatcher. Should only be
/// included in library scripts as it implements main().
/// ----------------------------------------------------------------------------
#include "nui_i_main"
// -----------------------------------------------------------------------------
// Function Protoypes
// -----------------------------------------------------------------------------
void DefineForm();
void BindForm();
void HandleNUIEvents();
void HandleModuleEvents();
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
// These are dummy implementations to prevent nwnsc from complaining that they
// do not exist. If you want to compile in the toolset rather than using nwnsc,
// comment these lines out.
//#pragma default_function(DefineForm)
//#pragma default_function(BindForm)
//#pragma default_function(HandleNUIEvents)
//#pragma default_function(HandleModuleEvents)
// -----------------------------------------------------------------------------
// Library Dispatch
// -----------------------------------------------------------------------------
void main()
{
string sOperation = GetScriptParam("NUI_FUNCTION");
if (sOperation == NUI_DEFINE) DefineForm();
else if (sOperation == NUI_BIND) BindForm();
else if (sOperation == NUI_EVENT_NUI) HandleNUIEvents();
else HandleModuleEvents();
}

2697
_module/nss/nui_i_main.nss Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
void main()
{
object oPlayer = GetExitingObject();
/* int nToken = NuiFindWindow(oPlayer, PC_NUI_WINDOW_ID);
//:: Murder the poor window
NuiDestroy(oPlayer, nToken);
nToken = NuiFindWindow(oPlayer, PC_NUI_GOLD_WINDOW_ID);
if (nToken)
NuiDestroy(oPlayer, nToken); */
}

View File

@@ -0,0 +1,6 @@
//Closes door if it is open
void main()
{
DelayCommand(13.0f,ActionCloseDoor(OBJECT_SELF));
}

View File

@@ -0,0 +1,86 @@
//::////////////////////////////////////////////////////////////////////////////
//:: Name Respawn Door/s v1.1
//:: FileName se_door_death
//:: Copyright (c) 2001 Bioware Corp.
//::////////////////////////////////////////////////////////////////////////////
/*
Respawn a door after a set delay
Set a float on the door ie. RESPAWN_TIMER = xx else the default is used
Thanks to Craig Welburn for the insight
Cheers to Zarathustra217
*/
//::////////////////////////////////////////////////////////////////////////////
//:: Created By: Sir Elric
//:: Created On: 8th May, 2006
//:: Modified On: 16th August, 2007
//:: Event Used: OnDeath event of a door
//::////////////////////////////////////////////////////////////////////////////
#include "x2_inc_compon"
// -----------------------------------------------------------------------------
// CONSTANTS - Settings below
// -----------------------------------------------------------------------------
const float RESPAWN_TIMER_DEFAULT = 600.0; // Default respawn delay
const int DO_CRAFT_DROP = TRUE; // Drop default Bioware crafting item?
// -----------------------------------------------------------------------------
// PROTOTYPES
// -----------------------------------------------------------------------------
// Respawn a door after a set delay
// If no float is set on the door ie. RESPAWN_TIMER = xx
// The default delay will be used ie. RESPAWN_TIMER_DEFAULT = xx
// oSelf: - Object calling the script
void SE_RespawnDoor(object oSelf);
// -----------------------------------------------------------------------------
// FUNCTIONS
// -----------------------------------------------------------------------------
void SE_RespawnDoor(object oSelf)
{
PlayAnimation(ANIMATION_DOOR_CLOSE);
int nHealAmount = GetMaxHitPoints(oSelf) - GetCurrentHitPoints(oSelf);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHealAmount), oSelf);
}
// -----------------------------------------------------------------------------
// MAIN
// -----------------------------------------------------------------------------
void main()
{
object oSelf = OBJECT_SELF;
object oKiller = GetLastKiller();
SetIsDestroyable(FALSE);
float fDelay = GetLocalFloat(oSelf, "RESPAWN_TIMER");
if(fDelay == 0.0)
fDelay = RESPAWN_TIMER_DEFAULT;
DelayCommand(fDelay, SE_RespawnDoor(oSelf));
if (!GetIsPC(oKiller))
return;
while (GetIsObjectValid(GetMaster(oKiller)))
{
oKiller=GetMaster(oKiller);
}
if(GetIsObjectValid(oKiller))
{
AdjustReputation(oKiller, OBJECT_SELF, -35);
AdjustAlignment (oKiller, ALIGNMENT_CHAOTIC, 10);
}
if(DO_CRAFT_DROP)
craft_drop_placeable();
}

34
_module/nss/sei_sit.nss Normal file
View File

@@ -0,0 +1,34 @@
//
// NWSit
//
// Script to make the PC using the object sit on it.
//
// (c) Shir'le E. Illios, 2002 (shirle@drowwanderer.com)
//
////////////////////////////////////////////////////////
void main()
{
// Get the character using the object.
object oPlayer = GetLastUsedBy();
// Make certain that the character is a PC.
if( GetIsPC( oPlayer ) )
{
// Get the object being sat on.
object oChair = OBJECT_SELF;
// If the object is valid and nobody else is currently sitting on it.
if( GetIsObjectValid( oChair ) &&
!GetIsObjectValid( GetSittingCreature( oChair ) ) )
{
// Let the player sit on the object.
AssignCommand( oPlayer, ActionSit( oChair ) );
}
} // End if
} // End main

View File

@@ -0,0 +1,7 @@
//Allow PC to see his description.
void main()
{
object oPC=GetLastUsedBy();
AssignCommand(oPC,ActionExamine(OBJECT_SELF));
}

View File

@@ -0,0 +1,225 @@
/// ----------------------------------------------------------------------------
/// @file util_c_color.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Configuration file for util_i_color.nss.
/// @details
/// These color codes are used with the functions from util_i_color.nss. These
/// are hex color codes, the same as you'd use in web design and may other
/// areas, so they are easy to look up and to copy-paste into other programs.
///
/// You can change the values of any of constants below, but do not change the
/// names of the constants themselves. You can also add your own constants for
/// use in your module.
///
/// ## Acknowledgement
/// - Function colors copied from https://nwn.wiki/display/NWN1/Colour+Tokens.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// X11 Color Palette
// -----------------------------------------------------------------------------
// ----- Whites ----------------------------------------------------------------
const int COLOR_AZURE = 0xf0ffff;
const int COLOR_BEIGE = 0xf5f5dc;
const int COLOR_BLUE_ALICE = 0xf0f8ff;
const int COLOR_HONEYDEW = 0xf0fff0;
const int COLOR_IVORY = 0xfffff0;
const int COLOR_LAVENDERBLUSH = 0xfff0f5;
const int COLOR_LINEN = 0xfaf0e6;
const int COLOR_MINTCREAM = 0xf5fffa;
const int COLOR_MISTYROSE = 0xffe4e1;
const int COLOR_OLDLACE = 0xfdf5e6;
const int COLOR_SEASHELL = 0xfff5ee;
const int COLOR_SNOW = 0xfffafa;
const int COLOR_WHITE = 0xffffff;
const int COLOR_WHITE_ANTIQUE = 0xfaebd7;
const int COLOR_WHITE_FLORAL = 0xfffaf0;
const int COLOR_WHITE_GHOST = 0xf8f8ff;
const int COLOR_WHITE_SMOKE = 0xf5f5f5;
// ----- Blues -----------------------------------------------------------------
const int COLOR_AQUA = 0x00ffff;
const int COLOR_AQUAMARINE = 0x7fffd4;
const int COLOR_BLUE = 0x0000ff;
const int COLOR_BLUE_CORNFLOWER = 0x6495ed;
const int COLOR_BLUE_DARK = 0x00008b;
const int COLOR_BLUE_DODGER = 0x1e90ff;
const int COLOR_BLUE_LIGHT = 0xadd8e6;
const int COLOR_BLUE_MEDIUM = 0x0000cd;
const int COLOR_BLUE_MIDNIGHT = 0x191970;
const int COLOR_BLUE_POWDER = 0xb0e0e6;
const int COLOR_BLUE_ROYAL = 0x4169e1;
const int COLOR_BLUE_SKY = 0x87ceeb;
const int COLOR_BLUE_SKY_DEEP = 0x00bfff;
const int COLOR_BLUE_SKY_LIGHT = 0x87cefa;
const int COLOR_BLUE_SLATE = 0x6a5acd;
const int COLOR_BLUE_SLATE_MEDIUM = 0x7b68ee;
const int COLOR_BLUE_STEEL = 0x4682b4;
const int COLOR_BLUE_STEEL_LIGHT = 0xb0c4de;
const int COLOR_CYAN = 0x00ffff;
const int COLOR_CYAN_LIGHT = 0xe0ffff;
const int COLOR_NAVY = 0x000080;
const int COLOR_TURQUOISE = 0x40e0d0;
const int COLOR_TURQUOISE_DARK = 0x00ced1;
const int COLOR_TURQUOISE_MEDIUM = 0x48d1cc;
const int COLOR_TURQUOISE_PALE = 0xafeeee;
// ----- Browns ----------------------------------------------------------------
const int COLOR_BISQUE = 0xffe4c4;
const int COLOR_BLANCHED_ALMOND = 0xffebcd;
const int COLOR_BROWN = 0xa52a2a;
const int COLOR_BROWN_LIGHT = 0xd0814b;
const int COLOR_BROWN_ROSY = 0xbc8f8f;
const int COLOR_BROWN_SADDLE = 0x8b4513;
const int COLOR_BROWN_SANDY = 0xf4a460;
const int COLOR_BURLYWOOD = 0xdeb887;
const int COLOR_CHOCOLATE = 0xd2691e;
const int COLOR_CORNSILK = 0xfff8dc;
const int COLOR_GOLDENROD = 0xdaa520;
const int COLOR_GOLDENROD_DARK = 0xb8860b;
const int COLOR_MAROON = 0x800000;
const int COLOR_PERU = 0xcd853f;
const int COLOR_SIENNA = 0xa0522d;
const int COLOR_TAN = 0xd2b48c;
const int COLOR_WHEAT = 0xf5deb3;
const int COLOR_WHITE_NAVAJO = 0xffdead;
// ----- Purples ---------------------------------------------------------------
const int COLOR_BLUE_SLATE_DARK = 0x483d8b;
const int COLOR_BLUE_VIOLET = 0x8a2be2;
const int COLOR_FUCHSIA = 0xff00ff;
const int COLOR_INDIGO = 0x4b0082;
const int COLOR_LAVENDER = 0xe6e6fa;
const int COLOR_MAGENTA = 0xff00ff;
const int COLOR_MAGENTA_DARK = 0x8b008b;
const int COLOR_ORCHID = 0xda70d6;
const int COLOR_ORCHID_DARK = 0x9932cc;
const int COLOR_ORCHID_MEDIUM = 0xba55d3;
const int COLOR_PLUM = 0xdda0dd;
const int COLOR_PURPLE = 0x800080;
const int COLOR_PURPLE_MEDIUM = 0x9370d8;
const int COLOR_THISTLE = 0xd8bfd8;
const int COLOR_VIOLET = 0xee82ee;
const int COLOR_VIOLET_DARK = 0x9400d3;
const int COLOR_VIOLET_LIGHT = 0xf397f8;
// ----- Oranges ---------------------------------------------------------------
const int COLOR_CORAL = 0xff7f50;
const int COLOR_ORANGE = 0xffa500;
const int COLOR_ORANGE_DARK = 0xff8c00;
const int COLOR_ORANGE_LIGHT = 0xf3b800;
const int COLOR_ORANGE_RED = 0xff4500;
const int COLOR_SALMON_LIGHT = 0xffa07a;
const int COLOR_TOMATO = 0xff6347;
// ----- Reds ------------------------------------------------------------------
const int COLOR_CORAL_LIGHT = 0xf08080;
const int COLOR_CRIMSON = 0xdc143c;
const int COLOR_FIREBRICK = 0xb22222;
const int COLOR_RED = 0xff0000;
const int COLOR_RED_DARK = 0x8b0000;
const int COLOR_RED_INDIAN = 0xcd5c4c;
const int COLOR_RED_LIGHT = 0xfa6155;
const int COLOR_SALMON = 0xfa8072;
const int COLOR_SALMON_DARK = 0xe9967a;
// ----- Pinks -----------------------------------------------------------------
const int COLOR_PINK = 0xffc0cb;
const int COLOR_PINK_DEEP = 0xff1493;
const int COLOR_PINK_HOT = 0xff69b4;
const int COLOR_PINK_LIGHT = 0xffb6c1;
const int COLOR_VIOLET_RED_MEDIUM = 0xc71585;
const int COLOR_VIOLET_RED_PALE = 0xdb7093;
// ----- Grays -----------------------------------------------------------------
const int COLOR_BLACK = 0x000000;
const int COLOR_GAINSBORO = 0xdcdcdc;
const int COLOR_GRAY = 0x808080;
const int COLOR_GRAY_DARK = 0xa9a9a9;
const int COLOR_GRAY_DIM = 0x696969;
const int COLOR_GRAY_LIGHT = 0xd3d3d3;
const int COLOR_GRAY_SLATE = 0x708090;
const int COLOR_GRAY_SLATE_DARK = 0x2f4f4f;
const int COLOR_GRAY_SLATE_LIGHT = 0x778899;
const int COLOR_SILVER = 0xc0c0c0;
// ----- Greens ----------------------------------------------------------------
const int COLOR_AQUAMARINE_MEDIUM = 0x66cdaa;
const int COLOR_CHARTREUSE = 0x7fff00;
const int COLOR_CYAN_DARK = 0x008b8b;
const int COLOR_GREEN = 0x008000;
const int COLOR_GREEN_DARK = 0x006400;
const int COLOR_GREEN_FOREST = 0x228b22;
const int COLOR_GREEN_LAWN = 0x7cfc00;
const int COLOR_GREEN_LIGHT = 0x90ee90;
const int COLOR_GREEN_LIME = 0x32cd32;
const int COLOR_GREEN_OLIVE_DARK = 0x556b2f;
const int COLOR_GREEN_PALE = 0x98fb98;
const int COLOR_GREEN_SEA = 0x2e8b57;
const int COLOR_GREEN_SEA_DARK = 0x8fbc8f;
const int COLOR_GREEN_SEA_LIGHT = 0x20b2aa;
const int COLOR_GREEN_SEA_MEDIUM = 0x3cb371;
const int COLOR_GREEN_SPRING = 0x00ff7f;
const int COLOR_GREEN_SPRING_MEDIUM = 0x00fa9a;
const int COLOR_GREEN_YELLOW = 0xadff2f;
const int COLOR_LIME = 0x00ff00;
const int COLOR_OLIVE = 0x808000;
const int COLOR_OLIVE_DRAB = 0x6b8e23;
const int COLOR_TEAL = 0x008080;
const int COLOR_YELLOW_GREEN = 0x9acd32;
// ----- Yellows ---------------------------------------------------------------
const int COLOR_GOLD = 0xffd700;
const int COLOR_GOLDENROD_LIGHT = 0xfafad2;
const int COLOR_GOLDENROD_PALE = 0xeee8aa;
const int COLOR_KHAKI = 0xf0e68c;
const int COLOR_KHAKI_DARK = 0xbdb76b;
const int COLOR_LEMON_CHIFFON = 0xfffacd;
const int COLOR_MOCCASIN = 0xffe4b5;
const int COLOR_PAPAYA_WHIP = 0xffefd5;
const int COLOR_PEACH_PUFF = 0xffdab9;
const int COLOR_YELLOW = 0xffff00;
const int COLOR_YELLOW_DARK = 0xd0ce00;
const int COLOR_YELLOW_LIGHT = 0xffffe0;
// -----------------------------------------------------------------------------
// Colors By Function
// -----------------------------------------------------------------------------
const int COLOR_DEFAULT = 0xfefefe;
const int COLOR_ATTENTION = 0xfea400;
const int COLOR_BUG = 0x660000;
const int COLOR_FAIL = 0xff0000;
const int COLOR_SUCCESS = 0x3dc93d;
const int COLOR_DEBUG = 0xb4b4b4;
const int COLOR_INFO = 0xd0814b;
// ----- Damage Types ----------------------------------------------------------
const int COLOR_DAMAGE_MAGICAL = 0xcc77ff;
const int COLOR_DAMAGE_ACID = 0x01ff01;
const int COLOR_DAMAGE_COLD = 0x99ffff;
const int COLOR_DAMAGE_DIVINE = 0xffff01;
const int COLOR_DAMAGE_ELECTRICAL = 0x0166ff;
const int COLOR_DAMAGE_FIRE = 0xff0101;
const int COLOR_DAMAGE_NEGATIVE = 0x999999;
const int COLOR_DAMAGE_POSITIVE = 0xffffff;
const int COLOR_DAMAGE_SONIC = 0xff9901;
// ----- Chat Log Messages -----------------------------------------------------
const int COLOR_MESSAGE_FEEDBACK = 0xffff01;
const int COLOR_MESSAGE_COMBAT = 0xff6601;
const int COLOR_MESSAGE_MAGIC = 0xcc77ff;
const int COLOR_MESSAGE_SKILLS = 0x0166ff;
const int COLOR_MESSAGE_SAVING = 0x66ccff;
const int COLOR_SAVE_STATUS = 0x20ff20;
const int COLOR_PAUSE_STATE = 0xff0101;
const int COLOR_NAME_CLIENT = 0x99ffff;
const int COLOR_NAME_OTHER = 0xcc99cc;
// -----------------------------------------------------------------------------
// Custom Colors
// -----------------------------------------------------------------------------
// You can add any custom colors you need for your functions below this line.
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,77 @@
/// ----------------------------------------------------------------------------
/// @file util_c_debug.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @brief Configuration file for util_i_debug.nss.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Helper Constants and Functions
// -----------------------------------------------------------------------------
// If you need custom constants, functions, or #includes, you may add them here.
// Since this file is included by util_i_debug.nss, you do not need to #include
// it to use its constants.
// -----------------------------------------------------------------------------
/*
// These constants are used by the example code below. Uncomment if you want to
// use them.
/// @brief This is the minimum debug level required to trigger custom handling.
/// @details Setting this to DEBUG_LEVEL_ERROR means OnDebug() will handle only
/// messages marked as DEBUG_LEVEL_ERROR and DEBUG_LEVEL_CRITICAL. If set to
/// DEBUG_LEVEL_NONE, the user-defined event will never be triggered.
/// @warning It is not recommended to set this level to DEBUG_LEVEL_NOTICE or
/// DEBUG_LEVEL_DEBUG as this could create high message traffic rates.
const int DEBUG_EVENT_TRIGGER_LEVEL = DEBUG_LEVEL_ERROR;
// These are varnames for script parameters
const string DEBUG_PARAM_PREFIX = "DEBUG_PARAM_PREFIX";
const string DEBUG_PARAM_MESSAGE = "DEBUG_PARAM_MESSAGE";
const string DEBUG_PARAM_LEVEL = "DEBUG_PARAM_LEVEL";
const string DEBUG_PARAM_TARGET = "DEBUG_PARAM_TARGET";
*/
// -----------------------------------------------------------------------------
// Debug Handler
// -----------------------------------------------------------------------------
// You may alter the contents of this function, but do not alter its signature.
// -----------------------------------------------------------------------------
/// @brief Custom debug event handler
/// @details This is a customizable function that runs before a message is shown
/// using Debug(). This function provides a user-definable hook into the
/// debug notification system. For example, a module can use this hook to
/// execute a script that may be able to handle module-specific error
/// handling or messaging.
/// @param sPrefix the debug message source provided by GetDebugPrefix()
/// @param sMessage the debug message provided to Debug()
/// @param nLevel the debug level of the message provided to Debug()
/// @param oTarget the game object being debugged as provided to Debug()
/// @returns TRUE if the message should be sent as normal; FALSE if no message
/// should be sent.
/// @note This function will never fire if oTarget is not debugging messages of
/// nLevel.
/// @warning Do not call Debug() or its aliases Notice(), Warning(), Error(), or
/// CriticalError() from this function; that would cause an infinite loop.
int HandleDebug(string sPrefix, string sMessage, int nLevel, object oTarget)
{
/*
// The following example code allows an external script to handle the event
// with access to the appropriate script parameters. Optionally, all event
// handling can be accomplished directly in this function.
// Only do custom handling if the debug level is error or critical error.
if (!nLevel || nLevel > DEBUG_EVENT_TRIGGER_LEVEL)
return TRUE;
SetScriptParam(DEBUG_PARAM_PREFIX, sPrefix);
SetScriptParam(DEBUG_PARAM_MESSAGE, sMessage);
SetScriptParam(DEBUG_PARAM_LEVEL, IntToString(nLevel));
SetScriptParam(DEBUG_PARAM_TARGET, ObjectToString(oTarget));
ExecuteScript("mydebugscript", oTarget);
return FALSE;
*/
return TRUE;
}

View File

@@ -0,0 +1,97 @@
/// ----------------------------------------------------------------------------
/// @file util_c_strftime.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @brief Configuration settings for util_i_strftime.nss.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Locale
// -----------------------------------------------------------------------------
// A locale is a group of localization settings stored as key-value pairs on a
// json object which is then stored on the module and accessed by a name. Some
// functions can take a locale name as an optional parameter so they can access
// those settings. If no name is provided, those functions will use the default
// locale instead.
// -----------------------------------------------------------------------------
/// This is the name for the default locale. All settings below will apply to
/// this locale.
const string DEFAULT_LOCALE = "EN_US";
// -----------------------------------------------------------------------------
// Translations
// -----------------------------------------------------------------------------
/// This is a 12-element comma-separated list of month names. `%B` evaluates to
/// the item at index `(month - 1) % 12`.
const string DEFAULT_MONTHS = "January, February, March, April, May, June, July, August, September, October, November, December";
/// This is a 12-element comma-separated list of abbreviated month names. `%b`
/// evaluates to the item at index `(month - 1) % 12`.
const string DEFAULT_MONTHS_ABBR = "Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec";
/// This is a 7-element comma-separated list of weekday names. `%A` evaluates to
/// the item at index `(day - 1) % 7`.
const string DEFAULT_DAYS = "Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday";
/// This is a 7-element comma-separated list of abbreviated day names. `%a`
/// evaluates to the item at index `(day - 1) % 7`.
const string DEFAULT_DAYS_ABBR = "Mon, Tue, Wed, Thu, Fri, Sat, Sun";
/// This is a 2-element comma-separated list with the preferred representation
/// of AM/PM. Noon is treated as PM and midnight is treated as AM. Evaluated by
/// `%p` (uppercase) and `%P` (lowercase).
const string DEFAULT_AMPM = "AM, PM";
/// This is a comma-separated list of suffixes for ordinal numbers. The list
/// should start with the suffix for 0. When formatting using the ordinal flag
/// (e.g., "Today is the %Od day of %B"), the number being formatted is used as
/// an index into this list. If the last two digits of the number are greater
/// than or equal to the length of the list, only the last digit of the number
/// is used. The default value will handle all integers in English.
const string DEFAULT_ORDINAL_SUFFIXES = "th, st, nd, rd, th, th, th, th, th, th, th, th, th, th";
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// -----------------------------------------------------------------------------
// Formatting
// -----------------------------------------------------------------------------
// These are strings that are used to format dates and times. Refer to the
// comments in `util_i_strftime.nss` for the meaning of format codes. Some codes
// are aliases for these values, so take care to avoid using those codes in
// these values to prevent an infinite loop.
// -----------------------------------------------------------------------------
/// This is a string used to format a date and time. Aliased by `%c`.
const string DEFAULT_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S:%f";
/// This is a string used to format a date without the time. Aliased by `%x`.
const string DEFAULT_DATE_FORMAT = "%Y-%m-%d";
/// This is a string used to format a time without the date. Aliased by `%X`.
const string DEFAULT_TIME_FORMAT = "%H:%M:%S";
/// This is a string used to format a time using AM/PM. Aliased by `%r`.
const string DEFAULT_AMPM_FORMAT = "%I:%M:%S %p";
/// This is a string used to format a date and time when era-based formatting is
/// used. If "", will fall back to DEFAULT_DATETIME_FORMAT. Aliased by `%Ec`.
const string DEFAULT_ERA_DATETIME_FORMAT = "";
/// This is a string used to format a date without the time when era-based
/// formatting is used. If "", will fall back to DEFAULT_DATE_FORMAT. Aliased by
/// `%Ex`.
const string DEFAULT_ERA_DATE_FORMAT = "";
/// This is a string used to format a time without the date when era-based
/// formatting is used. If "", will fall back to DEFAULT_TIME_FORMAT. Aliased by
/// `%EX`.
const string DEFAULT_ERA_TIME_FORMAT = "";
/// This is a string used to format years when era-based formatting is used. If
/// "", will always use the current year. Aliased by `%EY`.
const string DEFAULT_ERA_YEAR_FORMAT = "%Ey %EC";
/// This is a string used to format the era name when era-based formatting is
/// used. Normally, each era has its own name, but setting this can allow you
/// to display an era name even if you don't set up any eras for your locale.
const string DEFAULT_ERA_NAME = "";

View File

@@ -0,0 +1,23 @@
/// ----------------------------------------------------------------------------
/// @file util_c_targeting.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Configuration settings for util_i_targeting.nss.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Targeting Mode Script Handler
// -----------------------------------------------------------------------------
// You may alter the contents of this function, but do not alter its signature.
// -----------------------------------------------------------------------------
/// @brief Custom handler to run scripts associated with targeting hooks.
/// @param sScript The script assigned to the current targeting hook.
/// @param oSelf The PC object assigned to the current targeting event.
void RunTargetingHookScript(string sScript, object oSelf = OBJECT_SELF)
{
/* Use this function to implement your module's methodology for
running scripts.
ExecuteScript(sScript, oSelf);
*/
}

View File

@@ -0,0 +1,105 @@
/// ----------------------------------------------------------------------------
/// @file util_c_unittest.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Configuration file for util_i_unittest.nss.
/// ----------------------------------------------------------------------------
#include "util_i_debug"
// -----------------------------------------------------------------------------
// Unit Test Configuration Settings
// -----------------------------------------------------------------------------
// Set this value to the color the test title text will be colored to. The value
// can be a value from util_c_color or any other hex value representing a
// color.
// Example Output: Test My Variable Test
// ^^^^ This portion of the text will be affected
const int UNITTEST_TITLE_COLOR = COLOR_CYAN;
// Set this value to the color the test name text will be colored to. The value
// can be a value from util_c_color or any other hex value representing a
// color.
// Example Output: Test My Variable Test | PASS
// ^^^^^^^^^^^^^^^^ This portion of the text will be affected
const int UNITTEST_NAME_COLOR = COLOR_ORANGE_LIGHT;
// Set this value to the color the test parameter text will be colored to. The
// value can be a value from util_c_color or any other hex value representing a
// color.
// Example Output: Input: my_input
// Expected: my_assertion
// Received: my_output
// ^^^^^^^^^ This portion of the text will be affected
const int UNITTEST_PARAMETER_COLOR = COLOR_WHITE;
// Set this value to the color the test parameter text will be colored to. The
// value can be a value from util_c_color or any other hex value representing a
// color.
// Example Output: Input: my_input
// Expected: my_assertion
// ^^^^^^^^^^^^ This portion of the text will be affected
const int UNITTEST_PARAMETER_INPUT = COLOR_GREEN_SEA;
// Set this value to the color the test parameter text will be colored to. The
// value can be a value from util_c_color or any other hex value representing a
// color.
// Example Output: Received: my_output
// ^^^^^^^^^ This portion of the text will be affected
const int UNITTEST_PARAMETER_RECEIVED = COLOR_PINK;
// Set this value to the name of the script or event to run in case of a unit
// test failure.
const string UNITTEST_FAILURE_SCRIPT = "";
// This value determines whether test results are expanded. Set to TRUE to force
// all test results to show expanded data. Set to FALSE to show expanded data
// only on test failures.
const int UNITTEST_ALWAYS_EXPAND = FALSE;
// -----------------------------------------------------------------------------
// Helper Constants and Functions
// -----------------------------------------------------------------------------
// If you need custom constants, functions, or #includes, you may add them here.
// Since this file is included by util_i_unittest.nss, you do not need to
// #include it to use its constants.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Unit Test Output Handler
// -----------------------------------------------------------------------------
// You may alter the contents of this function, but do not alter its signature.
// -----------------------------------------------------------------------------
/// @brief Custom handler to handle reporting unit test results.
/// @param sOutput The formatted and colored output results of a unit test.
void HandleUnitTestOutput(string sOutput)
{
// This handler can be used to report the unit test output using any module
// debugging or other reporting system.
/*
SendMessageToPC(GetFirstPC(), sOutput);
*/
Notice(sOutput);
}
// -----------------------------------------------------------------------------
// Unit Test Failure Reporting Handler
// -----------------------------------------------------------------------------
// You may alter the contents of this function, but do not alter its signature.
// -----------------------------------------------------------------------------
/// @brief Custom handler to report unit testing failures.
/// @param sOutput The formatted and colored output results of a unit test.
void HandleUnitTestFailure(string sOutput)
{
// This handler can be used to report unit test failures to module systems
// or take specific action based on a failure. This function will
// generally not be used in a test environment, but may be useful for
// reporting failures in a production environment if unit tests are run
// during module startup.
if (UNITTEST_FAILURE_SCRIPT != "")
ExecuteScript(UNITTEST_FAILURE_SCRIPT, GetModule());
}

View File

@@ -0,0 +1,91 @@
/// ----------------------------------------------------------------------------
/// @file util_c_variables.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Configuration file for util_i_variables.nss.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Configuration
// -----------------------------------------------------------------------------
// This volatile table will be created on the GetModule() object the first time
// a module variable is set.
const string VARIABLE_TABLE_MODULE = "module_variables";
// This persitent table will be created on the PC object the first time a player
// variable is set. This table will be stored in the player's .bic file.
const string VARIABLE_TABLE_PC = "player_variables";
// A persistent table will be created in a campaign database with the following
// name. The table name will be VARIABLE_TABLE_MODULE above.
const string VARIABLE_CAMPAIGN_DATABASE = "module_variables";
// -----------------------------------------------------------------------------
// Local VarName Constructor
// -----------------------------------------------------------------------------
// This function is called when attempting to copy variables from a database
// to a game object. Since game objects do not accept additonal fields, such
// as a tag or timestamp, this function is provided to allow construction of
// a unique varname, if desired, from the fields in the database record. You
// may alter the contents of this function, but do not alter its signature.
// -----------------------------------------------------------------------------
/// @brief Constructs a varname for a local variable copied from a database.
/// @param oDatabase The database object the variable is sourced from. Will
/// be either a player object, DB_MODULE or DM_CAMPAIGN.
/// @param oTarget The game object the variable will be copied to.
/// @param sVarName VarName field retrieved from database.
/// @param sTag Tag field retrieved from database.
/// @param nType Type field retrieved from database. VARIABLE_TYPE_*, but
/// limited to VARIABLE_TYPE_INT|FLOAT|STRING|OBJECT|LOCATION|JSON.
/// @returns The constructed string that will be used as the varname once
/// copied to the target game object.
string DatabaseToObjectVarName(object oDatabase, object oTarget, string sVarName,
string sTag, int nType)
{
return sVarName;
}
// -----------------------------------------------------------------------------
// Database VarName and Tag Constructors
// -----------------------------------------------------------------------------
// These functions are called when attempting to copy variables from a game
// object to a database. These functions are provided to allow construction
// of unique varnames and tag from local variables varnames. If the function
// `DatabaseToObjectVarName()` above is used to copy database variables to a
// local object, these functions can be used to reverse the process if
// previously copied variables are returned to a database. You may alter the
// contents of these functions, but do not alter their signatures.
// -----------------------------------------------------------------------------
/// @brief Constructs a varname for a local variable copied to a database.
/// @param oSource The game object the variable will be copied from.
/// @param oDatabase The database object the variable will be copied to. Will
/// be either a player object, DB_MODULE or DM_CAMPAIGN.
/// @param sVarName VarName field retrieved from the local variable.
/// @param nType Type field retrieved from database. VARIABLE_TYPE_*, but
/// limited to VARIABLE_TYPE_INT|FLOAT|STRING|OBJECT|LOCATION|JSON.
/// @param sTag sTag as passed to `CopyLocalVariablesToDatabase()`.
/// @returns The constructed string that will be used as the varname once
/// copied to the target game object.
string ObjectToDatabaseVarName(object oSource, object oDatabase, string sVarName,
int nType, string sTag)
{
return sVarName;
}
/// @brief Constructs a varname for a local variable copied to a database.
/// @param oSource The game object the variable will be copied from.
/// @param oDatabase The database object the variable will be copied to. Will
/// be either a player object, DB_MODULE or DM_CAMPAIGN.
/// @param sVarName VarName field retrieved from the local variable.
/// @param nType Type field retrieved from database. VARIABLE_TYPE_*, but
/// limited to VARIABLE_TYPE_INT|FLOAT|STRING|OBJECT|LOCATION|JSON.
/// @param sTag sTag as passed to `CopyLocalVariablesToDatabase()`.
/// @returns The constructed string that will be used as the varname once
/// copied to the target game object.
string ObjectToDatabaseTag(object oSource, object oDatabase, string sVarName,
int nType, string sTag)
{
return sTag;
}

View File

@@ -0,0 +1,385 @@
/// ----------------------------------------------------------------------------
/// @file util_i_argstack.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for manipulating an argument stack.
/// @details
/// An argument stack provides a method for library functions to send values
/// to other functions without being able to call them directly. This allows
/// library functions to abstract away the connection layer and frees the
/// builder to design plug-and-play systems that don't break when unrelated
/// systems are removed or replaced.
///
/// Stacks work on a last in - first out basis and are split by variable type.
/// Popping a value will delete and return the last entered value of the
/// specified type stack. Other variable types will not be affected.
///
/// ```nwscript
/// PushInt(30);
/// PushInt(40);
/// PushInt(50);
/// PushString("test");
///
/// int nPop = PopInt(); // nPop = 50
/// string sPop = PopString(); // sPop = "test";
/// ```nwscript
/// ----------------------------------------------------------------------------
#include "util_i_varlists"
const string ARGS_DEFAULT_STACK = "ARGS_DEFAULT_STACK";
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Push as value onto the stack.
/// @param nValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Count of values on the stack.
int PushInt(int nValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
int PopInt(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
int PeekInt(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountIntStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Push as value onto the stack.
/// @param sValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Count of values on the stack.
int PushString(string sValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
string PopString(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
string PeekString(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountStringStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Push as value onto the stack.
/// @param fValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Count of values on the stack.
int PushFloat(float fValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
float PopFloat(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
float PeekFloat(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountFloatStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Push as value onto the stack.
/// @param oValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Count of values on the stack.
int PushObject(object oValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
object PopObject(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
object PeekObject(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountObjectStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Push as value onto the stack.
/// @param lValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.getlistfloat
/// @returns Count of values on the stack.
int PushLocation(location lValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
location PopLocation(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
location PeekLocation(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountLocationStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Push as value onto the stack.
/// @param vValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Count of values on the stack.
int PushVector(vector vValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
vector PopVector(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
vector PeekVector(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountVectorStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Push as value onto the stack.
/// @param jValue Value to add to stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Count of values on the stack.
int PushJson(json jValue, string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Pop a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
json PopJson(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Peek a value from the stack.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns Most recent value pushed on the stack.
json PeekJson(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Retrieve the stack size.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @returns The number of values in the stack.
int CountJsonStack(string sListName = "", object oTarget = OBJECT_INVALID);
/// @brief Clear all stack values.
/// @param sListName [Optional] Name of stack.
/// @param oTarget [Optional] Object stack will be saved to.
/// @note Use this function to ensure all stack values are cleared.
void ClearStacks(string sListName = "", object oTarget = OBJECT_INVALID);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
string _GetListName(string s)
{
return s == "" ? ARGS_DEFAULT_STACK : s;
}
object _GetTarget(object o)
{
if (o == OBJECT_INVALID || GetIsObjectValid(o) == FALSE)
return GetModule();
return o;
}
int PushInt(int nValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListInt(_GetTarget(oTarget), 0, nValue, _GetListName(sListName));
}
int PopInt(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListInt(_GetTarget(oTarget), _GetListName(sListName));
}
int PeekInt(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListInt(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountIntStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountIntList(_GetTarget(oTarget), _GetListName(sListName));
}
int PushString(string sValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListString(_GetTarget(oTarget), 0, sValue, _GetListName(sListName));
}
string PopString(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListString(_GetTarget(oTarget), _GetListName(sListName));
}
string PeekString(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListString(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountStringStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountStringList(_GetTarget(oTarget), _GetListName(sListName));
}
int PushFloat(float fValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListFloat(_GetTarget(oTarget), 0, fValue, _GetListName(sListName), FALSE);
}
float PopFloat(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListFloat(_GetTarget(oTarget), _GetListName(sListName));
}
float PeekFloat(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListFloat(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountFloatStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountFloatList(_GetTarget(oTarget), _GetListName(sListName));
}
int PushObject(object oValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListObject(_GetTarget(oTarget), 0, oValue, _GetListName(sListName));
}
object PopObject(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListObject(_GetTarget(oTarget), _GetListName(sListName));
}
object PeekObject(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListObject(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountObjectStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountObjectList(_GetTarget(oTarget), _GetListName(sListName));
}
int PushLocation(location lValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListLocation(_GetTarget(oTarget), 0, lValue, _GetListName(sListName));
}
location PopLocation(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListLocation(_GetTarget(oTarget), _GetListName(sListName));
}
location PeekLocation(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListLocation(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountLocationStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountLocationList(_GetTarget(oTarget), _GetListName(sListName));
}
int PushVector(vector vValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListVector(_GetTarget(oTarget), 0, vValue, _GetListName(sListName));
}
vector PopVector(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListVector(_GetTarget(oTarget), _GetListName(sListName));
}
vector PeekVector(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListVector(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountVectorStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountVectorList(_GetTarget(oTarget), _GetListName(sListName));
}
int PushJson(json jValue, string sListName = "", object oTarget = OBJECT_INVALID)
{
return InsertListJson(_GetTarget(oTarget), 0, jValue, _GetListName(sListName));
}
json PopJson(string sListName = "", object oTarget = OBJECT_INVALID)
{
return PopListJson(_GetTarget(oTarget), _GetListName(sListName));
}
json PeekJson(string sListName = "", object oTarget = OBJECT_INVALID)
{
return GetListJson(_GetTarget(oTarget), 0, _GetListName(sListName));
}
int CountJsonStack(string sListName = "", object oTarget = OBJECT_INVALID)
{
return CountJsonList(_GetTarget(oTarget), _GetListName(sListName));
}
void ClearStacks(string sListName = "", object oTarget = OBJECT_INVALID)
{
sListName = _GetListName(sListName);
oTarget = _GetTarget(oTarget);
DeleteIntList(oTarget, sListName);
DeleteStringList(oTarget, sListName);
DeleteFloatList(oTarget, sListName);
DeleteObjectList(oTarget, sListName);
DeleteLocationList(oTarget, sListName);
DeleteVectorList(oTarget, sListName);
DeleteJsonList(oTarget, sListName);
}

View File

@@ -0,0 +1,348 @@
/// ----------------------------------------------------------------------------
/// @file util_i_color.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @brief Functions to handle colors.
/// @details
/// NWN normally uses color codes to color strings. These codes take the format
/// `<cRGB>`, where RGB are ALT codes (0-0255) for colors.
///
/// Because color codes are arcane and can't be easily looked up, the functions
/// in this file prefer to use hex color codes. These codes are the same as
/// you'd use in web design and many other areas, so they are easy to look up
/// and can be copied and pasted into other programs. util_c_color.nss provides
/// some hex codes for common uses.
///
/// This file also contains functions to represent colors as RGB or HSV
/// triplets. HSV (Hue, Saturation, Value) may be particularly useful if you
/// want to play around with shifting colors.
///
/// ## Acknowledgements
/// - `GetColorCode()` function by rdjparadis.
/// - RGB <-> HSV colors adapted from NWShacker's Named Color Token System.
/// ----------------------------------------------------------------------------
#include "x3_inc_string"
#include "util_i_math"
#include "util_c_color"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
/// Used to generate colors from RGB values. NEVER modify this string.
/// @see https://nwn.wiki/display/NWN1/Colour+Tokens
/// @note First character is "nearest to 00" since we can't use `\x00` itself
/// @note COLOR_TOKEN originally by rdjparadis. Converted to NWN:EE escaped
/// characters by Jasperre.
const string COLOR_TOKEN = "\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
// -----------------------------------------------------------------------------
// Types
// -----------------------------------------------------------------------------
struct RGB
{
int r;
int g;
int b;
};
struct HSV
{
float h;
float s;
float v;
};
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
// ----- Type Creation ---------------------------------------------------------
/// @brief Create an RBG color struct.
/// @param nRed The value of the red channel (0..255).
/// @param nGreen The value of the green channel (0..255).
/// @param nBlue The value of the blue channel (0..255).
struct RGB GetRGB(int nRed, int nGreen, int nBlue);
/// @brief Create an HSV color struct.
/// @details The ranges are as follows:
/// 0.0 <= H < 360.0
/// 0.0 <= S <= 1.0
/// 0.0 <= V <= 1.0
/// @param fHue The hue (i.e. location on color wheel).
/// @param fSaturation The saturation (i.e., distance from white/black).
/// @param fValue The value (i.e., brightness of color).
struct HSV GetHSV(float fHue, float fSaturation, float fValue);
// ----- Type Conversion -------------------------------------------------------
/// @brief Convert a hexadecimal color to an RGB struct.
/// @param nColor Hexadecimal to convert to RGB.
struct RGB HexToRGB(int nColor);
/// @brief Convert an RGB struct to a hexadecimal color.
/// @param rgb RGB to convert to hexadecimal.
int RGBToHex(struct RGB rgb);
/// @brief Convert an RGB struct to an HSV struct.
/// @param rgb RGB to convert to HSV.
struct HSV RGBToHSV(struct RGB rgb);
/// @brief Convert an HSV struct to an RGB struct.
/// @param hsv HSV to convert to RGB.
struct RGB HSVToRGB(struct HSV hsv);
/// @brief Converts a hexadecimal color to an HSV struct.
/// @param nColor Hexadecimal to convert to HSV.
struct HSV HexToHSV(int nColor);
/// @brief Converts an HSV struct to a hexadecial color.
/// @param hsv HSV to convert to hexadecimal.
int HSVToHex(struct HSV hsv);
// ----- Coloring Functions ----------------------------------------------------
/// @brief Construct a color code that can be used to color a string.
/// @param nRed The intensity of the red channel (0..255).
/// @param nGreen The intensity of the green channel (0..255).
/// @param nBlue The intensity of the blue channel (0..255).
/// @returns A string color code in `<cRBG>` form.
string GetColorCode(int nRed, int nGreen, int nBlue);
/// @brief Convert a hexadecimal color to a color code.
/// @param nColor Hexadecimal representation of an RGB color.
/// @returns A string color code in `<cRBG>` form.
string HexToColor(int nColor);
/// @brief Convert a color code prefix to a hexadecimal
/// @param sColor A string color code in `<cRBG>` form.
int ColorToHex(string sColor);
/// @brief Color a string with a color code.
/// @param sString The string to color.
/// @param sColor A string color code in `<cRBG>` form.
string ColorString(string sString, string sColor);
/// @brief Color a string with a hexadecimal color.
/// @param sString The string to color.
/// @param nColor A hexadecimal color.
string HexColorString(string sString, int nColor);
/// @brief Color a string with an RGB color.
/// @param sString The string to color.
/// @param rgb The RGB color struct.
string RGBColorString(string sString, struct RGB rgb);
/// @brief Color a string with a struct HSV color
/// @param sString The string to color.
/// @param hsv The HSV color struct.
string HSVColorString(string sString, struct HSV hsv);
/// @brief Remove color codes from a string.
/// @param sString The string to uncolor.
/// @returns sString with color codes removed.
string UnColorString(string sString);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
// ----- Type Creation ---------------------------------------------------------
struct RGB GetRGB(int nRed, int nGreen, int nBlue)
{
struct RGB rgb;
rgb.r = clamp(nRed, 0, 255);
rgb.g = clamp(nGreen, 0, 255);
rgb.b = clamp(nBlue, 0, 255);
return rgb;
}
struct HSV GetHSV(float fHue, float fSat, float fVal)
{
struct HSV hsv;
hsv.h = fclamp(fHue, 0.0, 360.0);
hsv.s = fclamp(fSat, 0.0, 1.0);
hsv.v = fclamp(fVal, 0.0, 1.0);
if (hsv.h == 360.0)
hsv.h = 0.0;
return hsv;
}
// ----- Type Conversion -------------------------------------------------------
struct RGB HexToRGB(int nColor)
{
int nRed = (nColor & 0xff0000) >> 16;
int nGreen = (nColor & 0x00ff00) >> 8;
int nBlue = (nColor & 0x0000ff);
return GetRGB(nRed, nGreen, nBlue);
}
int RGBToHex(struct RGB rgb)
{
int nRed = (clamp(rgb.r, 0, 255) << 16);
int nGreen = (clamp(rgb.g, 0, 255) << 8);
int nBlue = clamp(rgb.b, 0, 255);
return nRed + nGreen + nBlue;
}
struct HSV RGBToHSV(struct RGB rgb)
{
// Ensure the RGB values are within defined limits
rgb = GetRGB(rgb.r, rgb.g, rgb.b);
struct HSV hsv;
// Convert RGB to a range from 0 - 1
float fRed = IntToFloat(rgb.r) / 255.0;
float fGreen = IntToFloat(rgb.g) / 255.0;
float fBlue = IntToFloat(rgb.b) / 255.0;
float fMax = fmax(fRed, fmax(fGreen, fBlue));
float fMin = fmin(fRed, fmin(fGreen, fBlue));
float fChroma = fMax - fMin;
if (fMax > fMin)
{
if (fMax == fRed)
hsv.h = 60.0 * ((fGreen - fBlue) / fChroma);
else if (fMax == fGreen)
hsv.h = 60.0 * ((fBlue - fRed) / fChroma + 2.0);
else
hsv.h = 60.0 * ((fRed - fGreen) / fChroma + 4.0);
if (hsv.h < 0.0)
hsv.h += 360.0;
}
if (fMax > 0.0)
hsv.s = fChroma / fMax;
hsv.v = fMax;
return hsv;
}
struct RGB HSVToRGB(struct HSV hsv)
{
// Ensure the HSV values are within defined limits
hsv = GetHSV(hsv.h, hsv.s, hsv.v);
struct RGB rgb;
// If value is 0, the resulting color will always be black
if (hsv.v == 0.0)
return rgb;
// If the saturation is 0, the resulting color will be a shade of grey
if (hsv.s == 0.0)
{
// Scale from white to black based on value
int nValue = FloatToInt(hsv.v * 255.0);
return GetRGB(nValue, nValue, nValue);
}
float h = hsv.h / 60.0;
float f = frac(h);
int v = FloatToInt(hsv.v * 255.0);
int p = FloatToInt(v * (1.0 - hsv.s));
int q = FloatToInt(v * (1.0 - hsv.s * f));
int t = FloatToInt(v * (1.0 - hsv.s * (1.0 - f)));
int i = FloatToInt(h);
switch (i % 6)
{
case 0: rgb = GetRGB(v, t, p); break;
case 1: rgb = GetRGB(q, v, p); break;
case 2: rgb = GetRGB(p, v, t); break;
case 3: rgb = GetRGB(p, q, v); break;
case 4: rgb = GetRGB(t, p, v); break;
case 5: rgb = GetRGB(v, p, q); break;
}
return rgb;
}
struct HSV HexToHSV(int nColor)
{
return RGBToHSV(HexToRGB(nColor));
}
int HSVToHex(struct HSV hsv)
{
return RGBToHex(HSVToRGB(hsv));
}
// ----- Coloring Functions ----------------------------------------------------
string GetColorCode(int nRed, int nGreen, int nBlue)
{
return "<c" + GetSubString(COLOR_TOKEN, nRed, 1) +
GetSubString(COLOR_TOKEN, nGreen, 1) +
GetSubString(COLOR_TOKEN, nBlue, 1) + ">";
}
string HexToColor(int nColor)
{
if (nColor < 0 || nColor > 0xffffff)
return "";
int nRed = (nColor & 0xff0000) >> 16;
int nGreen = (nColor & 0x00ff00) >> 8;
int nBlue = (nColor & 0x0000ff);
return GetColorCode(nRed, nGreen, nBlue);
}
int ColorToHex(string sColor)
{
if (sColor == "")
return -1;
string sRed = GetSubString(sColor, 2, 1);
string sGreen = GetSubString(sColor, 3, 1);
string sBlue = GetSubString(sColor, 4, 1);
int nRed = FindSubString(COLOR_TOKEN, sRed) << 16;
int nGreen = FindSubString(COLOR_TOKEN, sGreen) << 8;
int nBlue = FindSubString(COLOR_TOKEN, sBlue);
return nRed + nGreen + nBlue;
}
string ColorString(string sString, string sColor)
{
if (sColor != "")
sString = sColor + sString + "</c>";
return sString;
}
string HexColorString(string sString, int nColor)
{
string sColor = HexToColor(nColor);
return ColorString(sString, sColor);
}
string RGBColorString(string sString, struct RGB rgb)
{
string sColor = GetColorCode(rgb.r, rgb.g, rgb.b);
return ColorString(sString, sColor);
}
string HSVColorString(string sString, struct HSV hsv)
{
struct RGB rgb = HSVToRGB(hsv);
return RGBColorString(sString, rgb);
}
string UnColorString(string sString)
{
return RegExpReplace("<c[\\S\\s]{3}>|<\\/c>", sString, "");
}

View File

@@ -0,0 +1,209 @@
/// ----------------------------------------------------------------------------
/// @file util_i_constants.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions to retrieve the value of a constant from a script file.
/// @details
///
/// ## Example Usage
///
/// To retrieve the value of string constant `MODULE_EVENT_ON_NUI` from the Core
/// Framework file `core_i_constants`:
/// ```nwscript
/// struct CONSTANT c = GetConstantString("MODULE_EVENT_ON_NUI", "core_i_constants");
/// string sSetting = c.sValue;
/// ```
/// If successful, `sSetting` will contain the string value "OnNUI". If not
/// successful, `c.bError` will be TRUE, `c.sError` will contain the reason for
/// the error, and `c.sValue` will be set to an empty string ("").
///
/// To retrieve the value of integer constant `EVENT_STATE_OK`from the Core
/// Framework file `core_i_constants`:
/// ```nwscript
/// struct CONSTANT c = GetConstantInt("EVENT_STATE_OK", "core_i_constants");
/// int nState = c.bError ? -1 : c.nValue;
///
/// // or...
/// if (!c.bError)
/// {
/// int nState = c.nValue;
/// ...
/// }
/// ```
/// If successful, `nState` will contain the integer value 0. Since an error
/// value will also return 0, scripts should check `[struct].bError` before
/// using any constant that could return 0 as a valid value.
///
/// @note These functions require uncompiled `.nss` files, otherwise only base
/// nwscript constants will be retrievable. If you use a tool such as nasher
/// to build your module, ensure you do not filter out the `.nss` files when
/// building.
///
/// @note Based on clippy's code at
/// https://github.com/Finaldeath/nwscript_utility_scripts
/// ----------------------------------------------------------------------------
#include "util_i_debug"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
const string CONSTANTS_RESULT = "CONSTANTS_RESULT";
const string CONSTANTS_ERROR_FILE_NOT_FOUND = "FILE NOT FOUND";
const string CONSTANTS_ERROR_CONSTANT_NOT_FOUND = "VARIABLE DEFINED WITHOUT TYPE";
// -----------------------------------------------------------------------------
// Types
// -----------------------------------------------------------------------------
struct CONSTANT
{
int bError;
string sError;
string sValue;
int nValue;
float fValue;
string sFile;
string sConstant;
};
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Retrieves a constant string value from a script file
/// @param sConstant Name of the constant, must match case
/// @param sFile Optional: file to retrieve value from; if omitted, nwscript
/// is assumed
/// @returns a CONSTANT structure containing the following:
/// bError - TRUE if the constant could not be found
/// sError - The reason for the error, if any
/// sValue - The value of the constant retrieved, if successful, or ""
struct CONSTANT GetConstantString(string sConstant, string sFile = "");
/// @brief Retrieves a constant integer value from a script file
/// @param sConstant Name of the constant, must match case
/// @param sFile Optional: file to retrieve value from; if omitted, nwscript
/// is assumed
/// @returns a CONSTANT structure containing the following:
/// bError - TRUE if the constant could not be found
/// sError - The reason for the error, if any
/// nValue - The value of the constant retrieved, if successful, or 0
struct CONSTANT GetConstantInt(string sConstant, string sFile = "");
/// @brief Retrieves a constant float value from a script file
/// @param sConstant Name of the constant, must match case
/// @param sFile Optional: file to retrieve value from; if omitted, nwscript
/// is assumed
/// @returns a CONSTANT structure containing the following:
/// bError - TRUE if the constant could not be found
/// sError - The reason for the error, if any
/// fValue - The value of the constant retrieved, if successful, or 0.0
struct CONSTANT GetConstantFloat(string sConstant, string sFile = "");
/// @brief Find an constant name given the constant value.
/// @param sPrefix The prefix portion of the constant name being sought.
/// @param jValue The value of the sPrefix_* constant being sought. This must be
/// a json value to simplify argument passage. Create via a Json* function,
/// such as JsonInt(n), JsonString(s) or JsonFloat(f).
/// @param bSuffixOnly If TRUE, will only return the portion of the constant name
/// found after sPrefix, not including an intervening underscore.
/// @param sFile If passed, sFile will be searched for the appropriate constant name.
/// If not passed, `nwscript.nss` will be searched.
/// @note Does not work with nwscript TRUE/FALSE. Floats that are affected by
/// floating point error, such as 1.67, will also fail to find the correct
/// constant name. Floats that end in .0, such as for DIRECTION_, work correctly.
/// @warning This function is primarily designed for debugging messages. Using it
/// regularly can result in degraded performance.
string GetConstantName(string sPrefix, json jValue, int bSuffixOnly = FALSE, string sFile = "");
// -----------------------------------------------------------------------------
// Private Functions
// -----------------------------------------------------------------------------
// Attempts to retrieve the value of sConstant from sFile. If found, the
// appropriate fields in struct CONSTANT are populated. If not, [struct].bError is
// set to TRUE and the reason for failure is populated into [struct].sError. If the
// error cannot be determined, the error returned by ExecuteScriptChunk is
// populated directly into [struct].sError.
struct CONSTANT constants_RetrieveConstant(string sConstant, string sFile, string sType)
{
int COLOR_KEY = COLOR_BLUE_LIGHT;
int COLOR_VALUE = COLOR_SALMON;
int COLOR_FAILED = COLOR_MESSAGE_FEEDBACK;
struct CONSTANT c;
string sError, sChunk = "SetLocal" + sType + "(GetModule(), \"" +
CONSTANTS_RESULT + "\", " + sConstant + ");";
c.sConstant = sConstant;
c.sFile = sFile == "" ? "nwscript" : sFile;
if (sFile != "")
sChunk = "#include \"" + sFile + "\" void main() {" + sChunk + "}";
if ((sError = ExecuteScriptChunk(sChunk, GetModule(), sFile == "")) != "")
{
c.bError = TRUE;
if (FindSubString(sError, CONSTANTS_ERROR_FILE_NOT_FOUND) != -1)
c.sError = "Unable to find file `" + c.sFile + ".nss`";
else if (FindSubString(sError, CONSTANTS_ERROR_CONSTANT_NOT_FOUND) != -1)
c.sError = "Constant `" + c.sConstant + "` not found in `" + c.sFile + ".nss`";
else
c.sError = sError;
string sMessage = "[CONSTANTS] " + HexColorString("Failed", COLOR_FAILED) + " to retrieve constant value" +
"\n " + HexColorString("sConstant", COLOR_KEY) + " " + HexColorString(sConstant, COLOR_VALUE) +
"\n " + HexColorString("sFile", COLOR_KEY) + " " + HexColorString(c.sFile, COLOR_VALUE) +
"\n " + HexColorString("Reason", COLOR_KEY) + " " + HexColorString(c.sError, COLOR_VALUE);
Warning(sMessage);
}
return c;
}
// -----------------------------------------------------------------------------
// Public Function Implementations
// -----------------------------------------------------------------------------
struct CONSTANT GetConstantString(string sConstant, string sFile = "")
{
struct CONSTANT c = constants_RetrieveConstant(sConstant, sFile, "String");
if (!c.bError)
c.sValue = GetLocalString(GetModule(), CONSTANTS_RESULT);
return c;
}
struct CONSTANT GetConstantInt(string sConstant, string sFile = "")
{
struct CONSTANT c = constants_RetrieveConstant(sConstant, sFile, "Int");
if (!c.bError)
c.nValue = GetLocalInt(GetModule(), CONSTANTS_RESULT);
return c;
}
struct CONSTANT GetConstantFloat(string sConstant, string sFile = "")
{
struct CONSTANT c = constants_RetrieveConstant(sConstant, sFile, "Float");
if (!c.bError)
c.fValue = GetLocalFloat(GetModule(), CONSTANTS_RESULT);
return c;
}
string GetConstantName(string sPrefix, json jValue, int bSuffixOnly = FALSE, string sFile = "")
{
if (sFile == "") sFile = "nwscript";
sPrefix = GetStringUpperCase(bSuffixOnly ? sPrefix + "_?(" : "(" + sPrefix);
json jMatch = RegExpMatch(sPrefix + ".*?)(?: |=).*?=\\s*(" +
JsonDump(jValue) + ")\\s*;", ResManGetFileContents(sFile, RESTYPE_NSS));
return jMatch != JsonArray() ? JsonGetString(JsonArrayGet(jMatch, 1)) : "";
}

View File

@@ -0,0 +1,353 @@
/// ----------------------------------------------------------------------------
/// @file util_i_csvlists.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for manipulating comma-separated value (CSV) lists.
/// @details
///
/// ## Usage:
///
/// ```nwscript
/// string sKnight, sKnights = "Lancelot, Galahad, Robin";
/// int i, nCount = CountList(sKnights);
/// for (i = 0; i < nCount; i++)
/// {
/// sKnight = GetListItem(sKnights, i);
/// SpeakString("Sir " + sKnight);
/// }
///
/// int bBedivere = HasListItem(sKnights, "Bedivere");
/// SpeakString("Bedivere " + (bBedivere ? "is" : "is not") + " in the party.");
///
/// sKnights = AddListItem(sKnights, "Bedivere");
/// bBedivere = HasListItem(sKnights, "Bedivere");
/// SpeakString("Bedivere " + (bBedivere ? "is" : "is not") + " in the party.");
///
/// int nRobin = FindListItem(sKnights, "Robin");
/// SpeakString("Robin is knight " + IntToString(nRobin) + " in the party.");
/// ```
/// ----------------------------------------------------------------------------
#include "x3_inc_string"
#include "util_i_math"
#include "util_i_strings"
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Trim excess space around commas and, optionally, remove excess commas/
/// empty list items.
/// @param sList The CSV list to normalize.
/// @param bRemoveEmpty TRUE to remove empty items.
string NormalizeList(string sList, int bRemoveEmpty = TRUE);
/// @brief Return the number of items in a CSV list.
/// @param sList The CSV list to count.
int CountList(string sList);
/// @brief Add an item to a CSV list.
/// @param sList The CSV list to add the item to.
/// @param sListItem The item to add to sList.
/// @param bAddUnique If TRUE, will only add the item to the list if it is not
/// already there.
/// @returns A modified copy of sList with sListItem added.
string AddListItem(string sList, string sListItem, int bAddUnique = FALSE);
/// @brief Insert an item into a CSV list.
/// @param sList The CSV list to insert the item into.
/// @param sListItem The item to insert into sList.
/// @param nIndex The index of the item to insert (0-based).
/// @param bAddUnique If TRUE, will only insert the item to the list if it is not
/// already there.
/// @returns A modified copy of sList with sListItem inserted.
string InsertListItem(string sList, string sListItem, int nIndex = -1, int bAddUnique = FALSE);
/// @brief Modify an existing item in a CSV list.
/// @param sList The CSV list to modify.
/// @param sListItem The item to insert at nIndex.
/// @param nIndex The index of the item to modify (0-based).
/// @param bAddUnique If TRUE, will only modify the item to the list if it is not
/// already there.
/// @returns A modified copy of sList with item at nIndex modified.
/// @note If nIndex is out of bounds for sList, no values will be modified.
/// @warning If bAddUnique is TRUE and a non-unique value is set, the value with a lower
/// list index will be kept and values with higher list indices removed.
string SetListItem(string sList, string sListItem, int nIndex = -1, int bAddUnique = FALSE);
/// @brief Return the item at an index in a CSV list.
/// @param sList The CSV list to get the item from.
/// @param nIndex The index of the item to get (0-based).
string GetListItem(string sList, int nIndex = 0);
/// @brief Return the index of a value in a CSV list.
/// @param sList The CSV list to search.
/// @param sListItem The value to search for.
/// @param nNth The nth repetition of sListItem.
/// @returns -1 if the item was not found in the list.
int FindListItem(string sList, string sListItem, int nNth = 0);
/// @brief Return whether a CSV list contains a value.
/// @param sList The CSV list to search.
/// @param sListItem The value to search for.
/// @returns TRUE if the item is in the list, otherwise FALSE.
int HasListItem(string sList, string sListItem);
/// @brief Delete the item at an index in a CSV list.
/// @param sList The CSV list to delete the item from.
/// @param nIndex The index of the item to delete (0-based).
/// @returns A modified copy of sList with the item deleted.
string DeleteListItem(string sList, int nIndex = 0);
/// @brief Delete the first occurrence of an item in a CSV list.
/// @param sList The CSV list to remove the item from.
/// @param sListItem The value to remove from the list.
/// @param nNth The nth repetition of sListItem.
/// @returns A modified copy of sList with the item removed.
string RemoveListItem(string sList, string sListItem, int nNth = 0);
/// @brief Copy items from one CSV list to another.
/// @param sSource The CSV list to copy items from.
/// @param sTarget The CSV list to copy items to.
/// @param nIndex The index to begin copying from.
/// @param nRange The number of items to copy.
/// @param bAddUnique If TRUE, will only copy items to sTarget if they are not
/// already there.
/// @returns A modified copy of sTarget with the items added to the end.
string CopyListItem(string sSource, string sTarget, int nIndex, int nRange = 1, int bAddUnique = FALSE);
/// @brief Merge the contents of two CSV lists.
/// @param sList1 The first CSV list.
/// @param sList2 The second CSV list.
/// @param bAddUnique If TRUE, will only put items in the returned list if they
/// are not already there.
/// @returns A CSV list containing the items from each list.
string MergeLists(string sList1, string sList2, int bAddUnique = FALSE);
/// @brief Add an item to a CSV list saved as a local variable on an object.
/// @param oObject The object on which the local variable is saved.
/// @param sListName The varname for the local variable.
/// @param sListItem The item to add to the list.
/// @param bAddUnique If TRUE, will only add the item to the list if it is not
/// already there.
/// @returns The updated copy of the list with sListItem added.
string AddLocalListItem(object oObject, string sListName, string sListItem, int bAddUnique = FALSE);
/// @brief Delete an item in a CSV list saved as a local variable on an object.
/// @param oObject The object on which the local variable is saved.
/// @param sListName The varname for the local variable.
/// @param nIndex The index of the item to delete (0-based).
/// @returns The updated copy of the list with the item at nIndex deleted.
string DeleteLocalListItem(object oObject, string sListName, int nIndex = 0);
/// @brief Remove an item in a CSV list saved as a local variable on an object.
/// @param oObject The object on which the local variable is saved.
/// @param sListName The varname for the local variable.
/// @param sListItem The value to remove from the list.
/// @param nNth The nth repetition of sListItem.
/// @returns The updated copy of the list with the first instance of sListItem
/// removed.
string RemoveLocalListItem(object oObject, string sListName, string sListItem, int nNth = 0);
/// @brief Merge the contents of a CSV list with those of a CSV list stored as a
/// local variable on an object.
/// @param oObject The object on which the local variable is saved.
/// @param sListName The varname for the local variable.
/// @param sListToMerge The CSV list to merge into the saved list.
/// @param bAddUnique If TRUE, will only put items in the returned list if they
/// are not already there.
/// @returns The updated copy of the list with all items from sListToMerge
/// added.
string MergeLocalList(object oObject, string sListName, string sListToMerge, int bAddUnique = FALSE);
/// @brief Convert a comma-separated value list to a JSON array.
/// @param sList Source CSV list.
/// @param bNormalize TRUE to remove excess spaces and values. See NormalizeList().
/// @returns JSON array representation of CSV list.
json ListToJson(string sList, int bNormalize = TRUE);
/// @brief Convert a JSON array to a comma-separate value list.
/// @param jList JSON array list.
/// @param bNormalize TRUE to remove excess spaces and values. See NormalizeList().
/// @returns CSV list of JSON array values.
string JsonToList(json jList, int bNormalize = TRUE);
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
string NormalizeList(string sList, int bRemoveEmpty = TRUE)
{
string sRegex = "(?:[\\s]*,[\\s]*)" + (bRemoveEmpty ? "+" : "");
sList = RegExpReplace(sRegex, sList, ",");
return TrimString(bRemoveEmpty ? RegExpReplace("^[\\s]*,|,[\\s]*$", sList, "") : sList);
}
int CountList(string sList)
{
if (sList == "")
return 0;
return GetSubStringCount(sList, ",") + 1;
}
string AddListItem(string sList, string sListItem, int bAddUnique = FALSE)
{
sList = NormalizeList(sList);
sListItem = TrimString(sListItem);
if (bAddUnique && HasListItem(sList, sListItem))
return sList;
if (sList != "")
return sList + "," + sListItem;
return sListItem;
}
string InsertListItem(string sList, string sListItem, int nIndex = -1, int bAddUnique = FALSE)
{
if (nIndex == -1 || sList == "" || nIndex > CountList(sList) - 1)
return AddListItem(sList, sListItem, bAddUnique);
if (nIndex < 0) nIndex = 0;
json jList = JsonArrayInsert(ListToJson(sList), JsonString(sListItem), nIndex);
if (bAddUnique == TRUE)
jList = JsonArrayTransform(jList, JSON_ARRAY_UNIQUE);
return JsonToList(jList);
}
string SetListItem(string sList, string sListItem, int nIndex = -1, int bAddUnique = FALSE)
{
if (nIndex < 0 || nIndex > (CountList(sList) - 1))
return sList;
json jList = JsonArraySet(ListToJson(sList), nIndex, JsonString(sListItem));
if (bAddUnique == TRUE)
jList = JsonArrayTransform(jList, JSON_ARRAY_UNIQUE);
return JsonToList(jList);
}
string GetListItem(string sList, int nIndex = 0)
{
if (nIndex < 0 || sList == "" || nIndex > (CountList(sList) - 1))
return "";
return JsonGetString(JsonArrayGet(ListToJson(sList), nIndex));
}
int FindListItem(string sList, string sListItem, int nNth = 0)
{
json jIndex = JsonFind(ListToJson(sList), JsonString(TrimString(sListItem)), nNth);
return jIndex == JSON_NULL ? -1 : JsonGetInt(jIndex);
}
int HasListItem(string sList, string sListItem)
{
return (FindListItem(sList, sListItem) > -1);
}
string DeleteListItem(string sList, int nIndex = 0)
{
if (nIndex < 0 || sList == "" || nIndex > (CountList(sList) - 1))
return sList;
return JsonToList(JsonArrayDel(ListToJson(sList), nIndex));
}
string RemoveListItem(string sList, string sListItem, int nNth = 0)
{
return DeleteListItem(sList, FindListItem(sList, sListItem, nNth));
}
string CopyListItem(string sSource, string sTarget, int nIndex, int nRange = 1, int bAddUnique = FALSE)
{
int i, nCount = CountList(sSource);
if (nIndex < 0 || nIndex >= nCount || !nCount)
return sSource;
nRange = clamp(nRange, 1, nCount - nIndex);
for (i = 0; i < nRange; i++)
sTarget = AddListItem(sTarget, GetListItem(sSource, nIndex + i), bAddUnique);
return sTarget;
}
string MergeLists(string sList1, string sList2, int bAddUnique = FALSE)
{
if (sList1 != "" && sList2 == "")
return sList1;
else if (sList1 == "" && sList2 != "")
return sList2;
else if (sList1 == "" && sList2 == "")
return "";
string sList = sList1 + "," + sList2;
if (bAddUnique)
sList = JsonToList(JsonArrayTransform(ListToJson(sList), JSON_ARRAY_UNIQUE));
return bAddUnique ? sList : NormalizeList(sList);
}
string AddLocalListItem(object oObject, string sListName, string sListItem, int bAddUnique = FALSE)
{
string sList = GetLocalString(oObject, sListName);
sList = AddListItem(sList, sListItem, bAddUnique);
SetLocalString(oObject, sListName, sList);
return sList;
}
string DeleteLocalListItem(object oObject, string sListName, int nIndex = 0)
{
string sList = GetLocalString(oObject, sListName);
sList = DeleteListItem(sList, nIndex);
SetLocalString(oObject, sListName, sList);
return sList;
}
string RemoveLocalListItem(object oObject, string sListName, string sListItem, int nNth = 0)
{
string sList = GetLocalString(oObject, sListName);
sList = RemoveListItem(sList, sListItem, nNth);
SetLocalString(oObject, sListName, sList);
return sList;
}
string MergeLocalList(object oObject, string sListName, string sListToMerge, int bAddUnique = FALSE)
{
string sList = GetLocalString(oObject, sListName);
sList = MergeLists(sList, sListToMerge, bAddUnique);
SetLocalString(oObject, sListName, sList);
return sList;
}
json ListToJson(string sList, int bNormalize = TRUE)
{
if (sList == "")
return JSON_ARRAY;
if (bNormalize)
sList = NormalizeList(sList);
sList = RegExpReplace("\"", sList, "\\\"");
return JsonParse("[\"" + RegExpReplace(",", sList, "\",\"") + "\"]");
}
string JsonToList(json jList, int bNormalize = TRUE)
{
if (JsonGetType(jList) != JSON_TYPE_ARRAY)
return "";
string sList;
int n; for (n; n < JsonGetLength(jList); n++)
sList += (sList == "" ? "" : ",") + JsonGetString(JsonArrayGet(jList, n));
return bNormalize ? NormalizeList(sList) : sList;
}

View File

@@ -0,0 +1,132 @@
/// ----------------------------------------------------------------------------
/// @file util_i_datapoint.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for creating and interacting with datapoints, which are
/// invisible objects used to hold variables specific to a system.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
const string DATA_PREFIX = "Datapoint: ";
const string DATA_POINT = "x1_hen_inv"; ///< Resref for data points
const string DATA_ITEM = "nw_it_msmlmisc22"; ///< Resref for data items
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Creates a datapoint (placeable) that stores variables for a
/// specified system
/// @param sSystem Name of system associated with this datapoint
/// @param oOwner (optional) Parent object of this datapoint; if omitted,
/// defaults to GetModule();
/// @note A datapoint is created at oOwner's location; if oOwner is invalid or
/// is an area object, the datapoint is created at the module starting
/// location.
/// @returns sSystem's datapoint object
object CreateDatapoint(string sSystem, object oOwner = OBJECT_INVALID);
/// @brief Retrieves a datapoint (placeable) that stores variables for a
/// specified system
/// @param sSystem Name of system associated with this datapoint
/// @param oOwner (optional) Parent object of this datapoint; if omitted,
/// defaults to GetModule()
/// @param bCreate If TRUE and the datapoint cannot be found, a new datapoint
/// will be created at oOwner's location; if oOwner is invalid or is an
/// area object, the datapoint is created at the module starting location
/// @returns sSystem's datapoint object
object GetDatapoint(string sSystem, object oOwner = OBJECT_INVALID, int bCreate = TRUE);
/// @brief Sets a datapoint (game object) as the object that stores variables
/// for a specified system
/// @param sSystem Name of system associated with this datapoint
/// @param oTarget Object to be used as a datapoint
/// @param oOwner (optional) Parent object of this datapoint; if omitted,
/// default to GetModule()
/// @note Allows any valid game object to be used as a datapoint
void SetDatapoint(string sSystem, object oTarget, object oOwner = OBJECT_INVALID);
/// @brief Creates a data item (item) that stores variables for a specified
/// sub-system
/// @param oDatapoint Datapoint object on which to place the data item
/// @param sSubSystem Name of sub-system associated with this data item
/// @returns sSubSystem's data item object
object CreateDataItem(object oDatapoint, string sSubSystem);
/// @brief Retrieves a data item (item) that stores variables for a specified
/// sub-system
/// @param oDatapoint Datapoint object from which to retrieve the data item
/// @param sSubSystem Name of sub-system associated with the data item
/// @returns sSubSystem's data item object
object GetDataItem(object oDatapoint, string sSubSystem);
/// @brief Sets a data item (item) as the object that stores variables for a
/// specified sub-system
/// @param oDatapoint Datapoint object on which to place the data item
/// @param sSubSystem Name of sub-system assocaited with the data item
/// @param oItem Item to be used as a data item
/// @note oItem must a valid game item that can be placed into an object's
/// inventory
void SetDataItem(object oDatapoint, string sSubSystem, object oItem);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
object CreateDatapoint(string sSystem, object oOwner = OBJECT_INVALID)
{
if (oOwner == OBJECT_INVALID)
oOwner = GetModule();
location lLoc = GetLocation(oOwner);
if (!GetObjectType(oOwner))
lLoc = GetStartingLocation();
object oData = CreateObject(OBJECT_TYPE_PLACEABLE, DATA_POINT, lLoc);
SetName(oData, DATA_PREFIX + sSystem);
SetUseableFlag(oData, FALSE);
SetDatapoint(sSystem, oData, oOwner);
return oData;
}
object GetDatapoint(string sSystem, object oOwner = OBJECT_INVALID, int bCreate = TRUE)
{
if (oOwner == OBJECT_INVALID)
oOwner = GetModule();
object oData = GetLocalObject(oOwner, DATA_PREFIX + sSystem);
if (!GetIsObjectValid(oData) && bCreate)
oData = CreateDatapoint(sSystem, oOwner);
return oData;
}
void SetDatapoint(string sSystem, object oTarget, object oOwner = OBJECT_INVALID)
{
if (oOwner == OBJECT_INVALID)
oOwner = GetModule();
SetLocalObject(oOwner, DATA_PREFIX + sSystem, oTarget);
}
object CreateDataItem(object oDatapoint, string sSubSystem)
{
object oItem = CreateItemOnObject(DATA_ITEM, oDatapoint);
SetLocalObject(oDatapoint, sSubSystem, oItem);
SetName(oItem, sSubSystem);
return oItem;
}
object GetDataItem(object oDatapoint, string sSubSystem)
{
return GetLocalObject(oDatapoint, sSubSystem);
}
void SetDataItem(object oDatapoint, string sSubSystem, object oItem)
{
SetLocalObject(oDatapoint, sSubSystem, oItem);
}

View File

@@ -0,0 +1,324 @@
/// ----------------------------------------------------------------------------
/// @file util_i_debug.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for generating debug messages.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
// VarNames
const string DEBUG_COLOR = "DEBUG_COLOR";
const string DEBUG_LEVEL = "DEBUG_LEVEL";
const string DEBUG_LOG = "DEBUG_LOG";
const string DEBUG_OVERRIDE = "DEBUG_OVERRIDE";
const string DEBUG_PREFIX = "DEBUG_PREFIX";
const string DEBUG_DISPATCH = "DEBUG_DISPATCH";
// Debug levels
const int DEBUG_LEVEL_NONE = 0; ///< No debug level set
const int DEBUG_LEVEL_CRITICAL = 1; ///< Errors severe enough to stop the script
const int DEBUG_LEVEL_ERROR = 2; ///< Indicates the script malfunctioned in some way
const int DEBUG_LEVEL_WARNING = 3; ///< Indicates that unexpected behavior may occur
const int DEBUG_LEVEL_NOTICE = 4; ///< Information to track the flow of the function
const int DEBUG_LEVEL_DEBUG = 5; ///< Data dumps used for debugging
// Debug logging
const int DEBUG_LOG_NONE = 0x0; ///< Do not log debug messages
const int DEBUG_LOG_FILE = 0x1; ///< Send debug messages to the log file
const int DEBUG_LOG_DM = 0x2; ///< Send debug messages to online DMs
const int DEBUG_LOG_PC = 0x4; ///< Send debug messages to the first PC
const int DEBUG_LOG_LIST = 0x8; ///< Send debug messages to the dispatch list
const int DEBUG_LOG_ALL = 0xf; ///< Send messages to the log file, DMs, and first PC or dispatch list
#include "util_c_debug"
#include "util_i_color"
#include "util_i_varlists"
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Temporarily override the debug level for all objects.
/// @param nLevel The maximum verbosity of messages to show. Use FALSE to stop
/// overriding the debug level.
void OverrideDebugLevel(int nLevel);
/// @brief Return the verbosity of debug messages displayed for an object.
/// @param oTarget The object to check the debug level of. If no debug level has
/// been set on oTarget, will use the module instead.
/// @returns A `DEBUG_LEVEL_*` constant representing the maximum verbosity of
/// messages that will be displayed when debugging oTarget.
int GetDebugLevel(object oTarget = OBJECT_SELF);
/// @brief Set the verbosity of debug messages displayed for an object.
/// @param nLevel A `DEBUG_LEVEL_*` constant representing the maximum verbosity
/// of messages that will be displayed when debugging oTarget. If set to
/// `DEBUG_LEVEL_NONE`, oTarget will use the module's debug level instead.
/// @param oTarget The object to set the debug level of. If no debug level has
/// been set on oTarget, will use the module instead.
void SetDebugLevel(int nLevel, object oTarget = OBJECT_SELF);
/// @brief Return the color of debug messages of a given level.
/// @param nLevel A `DEBUG_LEVEL_*` constant representing the verbosity of debug
/// messsages to get the color for.
/// @returns A color code (in <cRGB> form).
string GetDebugColor(int nLevel);
/// @brief Set the color of debug messages of a given level.
/// @param nLevel A `DEBUG_LEVEL_*` constant representing the verbosity of debug
/// messsages to get the color for.
/// @param sColor A color core (in <cRBG> form) for the debug messages. If "",
/// will use the default color code for the level.
void SetDebugColor(int nLevel, string sColor = "");
/// @brief Return the prefix an object uses before its debug messages.
/// @param oTarget The target to check for a prefix.
/// @returns The user-defined prefix if one has been set. If it has not, will
/// return the object's tag (or name, if the object has no tag) in square
/// brackets.
string GetDebugPrefix(object oTarget = OBJECT_SELF);
/// @brief Set the prefix an object uses before its debug messages.
/// @param sPrefix The prefix to set. You can include color codes in the prefix,
/// but you can also set thedefault color code for all prefixes using
/// `SetDebugColor(DEBUG_COLOR_NONE, sColor);`.
/// @param oTarget The target to set the prefix for.
void SetDebugPrefix(string sPrefix, object oTarget = OBJECT_SELF);
/// @brief Return the enabled debug logging destinations.
/// @returns A bitmask of `DEBUG_LOG_*` values.
int GetDebugLogging();
/// @brief Set the enabled debug logging destinations.
/// @param nEnabled A bitmask of `DEBUG_LOG_*` destinations to enable.
void SetDebugLogging(int nEnabled);
/// @brief Add a player object to the debug message dispatch list. Player
/// objects on the dispatch list will receive debug messages if the
/// module's DEBUG_LOG_LEVEL includes DEBUG_LOG_LIST.
/// @param oPC Player object to add.
void AddDebugLoggingPC(object oPC);
/// @brief Remove a player object from the debug dispatch list.
/// @param oPC Player object to remove.
void RemoveDebugLoggingPC(object oPC);
/// @brief Return whether debug messages of a given level will be logged on a
/// target. Useful for avoiding spending cycles computing extra debug
/// information if it will not be shown.
/// @param nLevel A `DEBUG_LEVEL_*` constant representing the message verbosity.
/// @param oTarget The object that would be debugged.
/// @returns TRUE if messages of nLevel would be logged on oTarget; FALSE
/// otherwise.
int IsDebugging(int nLevel, object oTarget = OBJECT_SELF);
/// If oTarget has a debug level of nLevel or higher, logs sMessages to all
/// destinations set with SetDebugLogging(). If no debug level is set on
/// oTarget,
/// will debug using the module's debug level instead.
/// @brief Display a debug message.
/// @details If the target has a debug level of nLevel or higher, sMessage will
/// be sent to all destinations enabled by SetDebugLogging(). If no debug
/// level is set on oTarget, will debug using the module's debug level
/// instead.
/// @param sMessage The message to display.
/// @param nLevel A `DEBUG_LEVEL_*` constant representing the message verbosity.
/// @param oTarget The object originating the message.
void Debug(string sMessage, int nLevel = DEBUG_LEVEL_DEBUG, object oTarget = OBJECT_SELF);
/// @brief Display a general notice message. Alias for Debug().
/// @param sMessage The message to display.
/// @param oTarget The object originating the message.
void Notice(string sMessage, object oTarget = OBJECT_SELF);
/// @brief Display a warning message. Alias for Debug().
/// @param sMessage The message to display.
/// @param oTarget The object originating the message.
void Warning(string sMessage, object oTarget = OBJECT_SELF);
/// @brief Display an error message. Alias for Debug().
/// @param sMessage The message to display.
/// @param oTarget The object originating the message.
void Error(string sMessage, object oTarget = OBJECT_SELF);
/// @brief Display a critical error message. Alias for Debug().
/// @param sMessage The message to display.
/// @param oTarget The object originating the message.
void CriticalError(string sMessage, object oTarget = OBJECT_SELF);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
void OverrideDebugLevel(int nLevel)
{
nLevel = clamp(nLevel, DEBUG_LEVEL_NONE, DEBUG_LEVEL_DEBUG);
SetLocalInt(GetModule(), DEBUG_OVERRIDE, nLevel);
}
int GetDebugLevel(object oTarget = OBJECT_SELF)
{
object oModule = GetModule();
int nOverride = GetLocalInt(oModule, DEBUG_OVERRIDE);
if (nOverride)
return nOverride;
int nModule = GetLocalInt(oModule, DEBUG_LEVEL);
if (oTarget == oModule || !GetIsObjectValid(oTarget))
return nModule;
int nLevel = GetLocalInt(oTarget, DEBUG_LEVEL);
return (nLevel ? nLevel : nModule ? nModule : DEBUG_LEVEL_CRITICAL);
}
void SetDebugLevel(int nLevel, object oTarget = OBJECT_SELF)
{
SetLocalInt(oTarget, DEBUG_LEVEL, nLevel);
}
string GetDebugColor(int nLevel)
{
string sColor = GetLocalString(GetModule(), DEBUG_COLOR + IntToString(nLevel));
if (sColor == "")
{
int nColor;
switch (nLevel)
{
case DEBUG_LEVEL_CRITICAL: nColor = COLOR_RED; break;
case DEBUG_LEVEL_ERROR: nColor = COLOR_ORANGE_DARK; break;
case DEBUG_LEVEL_WARNING: nColor = COLOR_ORANGE_LIGHT; break;
case DEBUG_LEVEL_NOTICE: nColor = COLOR_YELLOW; break;
case DEBUG_LEVEL_NONE: nColor = COLOR_GREEN_LIGHT; break;
default: nColor = COLOR_GRAY_LIGHT; break;
}
sColor = HexToColor(nColor);
SetDebugColor(nLevel, sColor);
}
return sColor;
}
void SetDebugColor(int nLevel, string sColor = "")
{
SetLocalString(GetModule(), DEBUG_COLOR + IntToString(nLevel), sColor);
}
string GetDebugPrefix(object oTarget = OBJECT_SELF)
{
string sColor = GetDebugColor(DEBUG_LEVEL_NONE);
string sPrefix = GetLocalString(oTarget, DEBUG_PREFIX);
if (sPrefix == "")
{
if (!GetIsObjectValid(oTarget))
{
sColor = GetDebugColor(DEBUG_LEVEL_WARNING);
sPrefix = "Invalid Object: #" + ObjectToString(oTarget);
}
else
sPrefix = (sPrefix = GetTag(oTarget)) == "" ? GetName(oTarget) : sPrefix;
sPrefix = "[" + sPrefix + "]";
}
return ColorString(sPrefix, sColor);
}
void SetDebugPrefix(string sPrefix, object oTarget = OBJECT_SELF)
{
SetLocalString(oTarget, DEBUG_PREFIX, sPrefix);
}
int GetDebugLogging()
{
return GetLocalInt(GetModule(), DEBUG_LOG);
}
void SetDebugLogging(int nEnabled)
{
SetLocalInt(GetModule(), DEBUG_LOG, nEnabled);
}
void AddDebugLoggingPC(object oPC)
{
if (GetIsPC(oPC))
AddListObject(GetModule(), oPC, DEBUG_DISPATCH, TRUE);
}
void RemoveDebugLoggingPC(object oPC)
{
RemoveListObject(GetModule(), oPC, DEBUG_DISPATCH);
}
int IsDebugging(int nLevel, object oTarget = OBJECT_SELF)
{
return (nLevel <= GetDebugLevel(oTarget));
}
void Debug(string sMessage, int nLevel = DEBUG_LEVEL_DEBUG, object oTarget = OBJECT_SELF)
{
if (IsDebugging(nLevel, oTarget))
{
string sColor = GetDebugColor(nLevel);
string sPrefix = GetDebugPrefix(oTarget) + " ";
switch (nLevel)
{
case DEBUG_LEVEL_CRITICAL: sPrefix += "[Critical Error] "; break;
case DEBUG_LEVEL_ERROR: sPrefix += "[Error] "; break;
case DEBUG_LEVEL_WARNING: sPrefix += "[Warning] "; break;
}
if (!HandleDebug(sPrefix, sMessage, nLevel, oTarget))
return;
sMessage = sPrefix + sMessage;
int nLogging = GetLocalInt(GetModule(), DEBUG_LOG);
if (nLogging & DEBUG_LOG_FILE)
WriteTimestampedLogEntry(UnColorString(sMessage));
sMessage = ColorString(sMessage, sColor);
if (nLogging & DEBUG_LOG_DM)
SendMessageToAllDMs(sMessage);
if (nLogging & DEBUG_LOG_PC)
SendMessageToPC(GetFirstPC(), sMessage);
if (nLogging & DEBUG_LOG_LIST)
{
json jDispatchList = GetObjectList(GetModule(), DEBUG_DISPATCH);
int n; for (n; n < JsonGetLength(jDispatchList); n++)
{
object oPC = GetListObject(GetModule(), n, DEBUG_DISPATCH);
if (GetIsPC(oPC) && !((nLogging & DEBUG_LOG_PC) && oPC == GetFirstPC()))
SendMessageToPC(oPC, sMessage);
}
}
}
}
void Notice(string sMessage, object oTarget = OBJECT_SELF)
{
Debug(sMessage, DEBUG_LEVEL_NOTICE, oTarget);
}
void Warning(string sMessage, object oTarget = OBJECT_SELF)
{
Debug(sMessage, DEBUG_LEVEL_WARNING, oTarget);
}
void Error(string sMessage, object oTarget = OBJECT_SELF)
{
Debug(sMessage, DEBUG_LEVEL_ERROR, oTarget);
}
void CriticalError(string sMessage, object oTarget = OBJECT_SELF)
{
Debug(sMessage, DEBUG_LEVEL_CRITICAL, oTarget);
}

View File

@@ -0,0 +1,481 @@
/// ----------------------------------------------------------------------------
/// @file util_i_libraries.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief This file holds functions for packing scripts into libraries. This
/// allows the builder to dramatically reduce the module script count by
/// keeping related scripts in the same file.
/// @details
/// Libraries allow the builder to encapsulate many scripts into one,
/// dramatically reducing the script count in the module. In a library, each
/// script is a function bound to a unique name and/or number. When the library
/// is called, the name is routed to the proper function.
///
/// Since each script defined by a library has a unique name to identify it, the
/// builder can execute a library script without having to know the file it is
/// located in. This makes it easy to create script systems to override behavior
/// of another system; you don't have to edit the other system's code, you just
/// implement your own function to override it.
///
/// ## Anatomy of a Library
/// This is an example of a simple library:
///
/// ``` nwscript
/// #include "util_i_libraries"
///
/// void MyFunction()
/// {
/// // ...
/// }
///
/// void MyOtherFunction()
/// {
/// // ...
/// }
///
/// void OnLibraryLoad()
/// {
/// RegisterLibraryScript("MyFunction");
/// RegisterLibraryScript("MyOtherFunction");
/// }
/// ```
///
/// This script contains custom functions (`MyFunction()` and `MyOtherFunction()`)
/// as well as an `OnLibraryLoad()` function. `OnLibraryLoad()` is executed
/// whenever the library is loaded by `LoadLibrary()`; it calls
/// `RegisterLibraryScript()` to expose the names of the custom functions as
/// library scripts. When a library script is called with `RunLibraryScript()`,
/// the custom functions are called.
///
/// If you want to do something more complicated that can't be handled by a
/// single function call, you can pass a unique number to
/// `RegisterLibraryScript()` as its second parameter, which will cause
/// `RunLibraryScript()` to call a special customizable dispatching function
/// called `OnLibraryScript()`. This function takes the name and number of the
/// desired function and executes the desired code. For example:
///
/// ``` nwscript
/// #include "util_i_libraries"
///
/// void OnLibraryLoad()
/// {
/// RegisterLibraryScript("Give50GP", 1);
/// RegisterLibraryScript("Give100GP", 2);
/// }
///
/// void OnLibraryScript(string sScript, int nEntry)
/// {
/// switch (nEntry)
/// {
/// case 1: GiveGoldToCreature(OBJECT_SELF, 50); break;
/// case 2: GiveGoldToCreature(OBJECT_SELF, 100); break;
/// }
/// }
/// ```
///
/// **Note:** A library does not need to have a `main()` function, because this
/// will be automatically generated by the `LoadLibrary()` and
/// `RunLibraryScript()` functions.
///
/// ## Using a Library
/// `util_i_libraries.nss` is needed to load or run library scripts.
///
/// To use a library, you must first load it. This will activate the library's
/// `OnLibraryLoad()` function and register each desired function.
///
/// ``` nwscript
/// // Loads a single library
/// LoadLibrary("my_l_library");
///
/// // Loads a CSV list of library scripts
/// LoadLibraries("pw_l_plugin, dlg_l_example, prr_l_main");
///
/// // Loads all libraries matching a glob pattern
/// LoadLibrariesByPattern("*_l_*");
///
/// // Loads all libraries matching a prefix
/// LoadLibrariesByPrefix("pw_l_");
/// ```
///
/// If a library implements a script that has already been implemented in
/// another library, a warning will be issued and the newer script will take
/// precedence.
///
/// Calling a library script is done using `RunLibraryScript()`. The name
/// supplied should be the name bound to the function in the library's
/// `OnLibraryLoad()`. If the name supplied is implemented by a library, the
/// library will be JIT compiled and the desired function will be called with
/// `ExecuteScriptChunk()`. Otherwise, the name will be assumed to match a
/// normal script, which will be executed with `ExecuteScript()`.
///
/// ``` nwscript
/// // Executes a single library script on OBJECT_SELF
/// RunLibraryScript("MyFunction");
///
/// // Executes a CSV list of library scripts, for which oPC will be OBJECT_SELF
/// object oPC = GetFirstPC();
/// RunLibraryScripts("MyFunction, MyOtherFunction", oPC);
/// ```
///
/// ## Pre-Compiled Libraries
/// By default, libraries are run using `ExecuteScriptChunk()`, which JIT
/// compiles the script and runs it each time the library script is called. If
/// you wish to have your script pre-compiled, you can include the script
/// `util_i_library.nss` in your file in place of `util_i_libraries.nss`. This
/// script contains a `main()` function that will call either your
/// `OnLibraryLoad()` or `OnLibraryScript()` function as appropriate; thus, if
/// you use this method, you *must* provide an `OnLibraryScript()` dispatch
/// function.
///
/// **Note**: `util_i_library.nss` uses the nwnsc `default_function` pragma to
/// prevent compilation errors and will not compile with the toolset compiler.
/// If this is not desired, you can either comment those lines out or implement
/// the `main()` function yourself.
/// ----------------------------------------------------------------------------
#include "util_i_debug"
#include "util_i_csvlists"
#include "util_i_sqlite"
#include "util_i_nss"
#include "util_i_matching"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
const string LIB_RETURN = "LIB_RETURN"; ///< The return value of the library
const string LIB_LIBRARY = "LIB_LIBRARY"; ///< The library being processed
const string LIB_SCRIPT = "LIB_SCRIPT"; ///< The library script name
const string LIB_ENTRY = "LIB_ENTRY"; ///< The library script entry number
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Create a library table in the module's volatile sqlite database.
/// @param bReset if TRUE, the table will be dropped if already present.
/// @note This is called automatically by the library functions.
void CreateLibraryTable(int bReset = FALSE);
/// @brief Add a database record associating a script with a library.
/// @param sLibrary The script to source from.
/// @param sScript The name to associate with the library script.
/// @param nEntry A number unique to sLibrary to identify this script. If this
/// is 0 and the library has not been pre-compiled, RunLibraryScript() will
/// call sScript directly. Otherwise, RunLibraryScript() will run a dispatch
/// function that can use this number to execute the correct code. Thus,
/// nEntry must be set if sScript does not exactly match the desired
/// function name or the function requires parameters.
void AddLibraryScript(string sLibrary, string sScript, int nEntry = 0);
/// @brief Return the name of the library containing a script from the database.
/// @param sScript The name of the library script.
string GetScriptLibrary(string sScript);
/// @brief Return the entry number associated with a library script.
/// @param sScript The name of the library script.
int GetScriptEntry(string sScript);
/// @brief Return a prepared query with the with the library and entry data
/// associated with a library script.
/// @param sScript The name of the library script.
/// @note This allows users to retrive the same data returned by
/// GetScriptLibrary() and GetScriptEntry() with one function.
sqlquery GetScriptData(string sScript);
/// @brief Return whether a script library has been loaded.
/// @param sLibrary The name of the script library file.
int GetIsLibraryLoaded(string sLibrary);
/// @brief Load a script library by executing its OnLibraryLoad() function.
/// @param sLibrary The name of the script library file.
/// @param bForce If TRUE, will re-load the library if it was already loaded.
void LoadLibrary(string sLibrary, int bForce = FALSE);
/// @brief Load a list of script libraries in sequence.
/// @param sLibraries A CSV list of libraries to load.
/// @param bForce If TRUE, will re-load the library if it was already loaded.
void LoadLibraries(string sLibraries, int bForce = FALSE);
/// @brief Return a json array of script names with a prefix.
/// @param sPrefix The prefix matching the scripts to find.
/// @returns A sorted json array of script names, minus the extensions.
/// @note The search includes both nss and ncs files, with duplicates removed.
json GetScriptsByPrefix(string sPrefix);
/// @brief Load all scripts matching the given glob pattern(s).
/// @param sPattern A CSV list of glob patterns to match with. Supported syntax:
/// - `*`: match zero or more characters
/// - `?`: match a single character
/// - `[abc]`: match any of a, b, or c
/// - `[a-z]`: match any character from a-z
/// - other text is matched literally
/// @param bForce If TRUE, will-reload the library if it was already loaded.
void LoadLibrariesByPattern(string sPattern, int bForce = FALSE);
/// @brief Load all scripts with a given prefix as script libraries.
/// @param sPrefix A prefix for the desired script libraries.
/// @param bForce If TRUE, will re-load the library if it was already loaded.
/// @see GetMatchesPattern() for the rules on glob syntax.
void LoadLibrariesByPrefix(string sPrefix, int bForce = FALSE);
/// @brief Execute a registered library script.
/// @param sScript The unique name of the library script.
/// @param oSelf The object that should execute the script as OBJECT_SELF.
/// @returns The integer value set with LibraryReturn() by sScript.
/// @note If sScript is not registered as a library script, it will be executed
/// as a regular script instead.
int RunLibraryScript(string sScript, object oSelf = OBJECT_SELF);
/// @brief Execute a list of registered library scripts in sequence.
/// @param sScripts A CSV list of library script names.
/// @param oSelf The object that should execute the scripts as OBJECT_SELF.
/// @note If any script in sScripts is not registered as a library script, it
/// will be executed as a regular script instead.
void RunLibraryScripts(string sScripts, object oSelf = OBJECT_SELF);
/// @brief Register a script to a library. The script can later be called using
/// RunLibraryScript().
/// @param sScript A name for the script. Must be unique in the module. If a
/// second script with the same name is registered, it will overwrite the
/// first one. This value does not have to match the function or script name.
/// @param nEntry A number unique to this library to identify this script. If
/// this is 0 and the library has not been pre-compiled, RunLibraryScript()
/// will call sScript directly. Otherwise, RunLibraryScript() will run a
/// dispatch function that can use this number to execute the correct code.
/// Thus, nEntry must be set if sScript does not exactly match the desired
/// function name or the function requires parameters.
/// @note Must be called within a script library's OnLibraryLoad() function. For
/// uses in other places, use AddLibraryScript().
void RegisterLibraryScript(string sScript, int nEntry = 0);
/// @brief Set the return value of the currently executing library script.
/// @param nValue The value to return to the calling script.
void LibraryReturn(int nValue);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
void CreateLibraryTable(int bReset = FALSE)
{
SqlCreateTableModule("library_scripts",
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"sLibrary TEXT NOT NULL, " +
"sScript TEXT NOT NULL UNIQUE ON CONFLICT REPLACE, " +
"nEntry INTEGER NOT NULL);",
bReset);
}
void AddLibraryScript(string sLibrary, string sScript, int nEntry = 0)
{
CreateLibraryTable();
string sQuery = "INSERT INTO library_scripts (sLibrary, sScript, nEntry) " +
"VALUES (@sLibrary, @sScript, @nEntry);";
sqlquery sql = SqlPrepareQueryModule(sQuery);
SqlBindString(sql, "@sLibrary", sLibrary);
SqlBindString(sql, "@sScript", sScript);
SqlBindInt(sql, "@nEntry", nEntry);
SqlStep(sql);
}
string GetScriptFieldData(string sField, string sScript)
{
CreateLibraryTable();
string sQuery = "SELECT " + sField + " FROM library_scripts " +
"WHERE sScript = @sScript;";
sqlquery sql = SqlPrepareQueryModule(sQuery);
SqlBindString(sql, "@sScript", sScript);
return SqlStep(sql) ? SqlGetString(sql, 0) : "";
}
string GetScriptLibrary(string sScript)
{
return GetScriptFieldData("sLibrary", sScript);
}
int GetScriptEntry(string sScript)
{
return StringToInt(GetScriptFieldData("nEntry", sScript));
}
sqlquery GetScriptData(string sScript)
{
CreateLibraryTable();
string sQuery = "SELECT sLibrary, nEntry FROM library_scripts " +
"WHERE sScript = @sScript;";
sqlquery sql = SqlPrepareQueryModule(sQuery);
SqlBindString(sql, "@sScript", sScript);
return sql;
}
int GetIsLibraryLoaded(string sLibrary)
{
CreateLibraryTable();
string sQuery = "SELECT COUNT(sLibrary) FROM library_scripts " +
"WHERE sLibrary = @sLibrary LIMIT 1;";
sqlquery sql = SqlPrepareQueryModule(sQuery);
SqlBindString(sql, "@sLibrary", sLibrary);
return SqlStep(sql) ? SqlGetInt(sql, 0) : FALSE;
}
void LoadLibrary(string sLibrary, int bForce = FALSE)
{
Debug("Attempting to " + (bForce ? "force " : "") + "load library " + sLibrary);
if (bForce || !GetIsLibraryLoaded(sLibrary))
{
SetScriptParam(LIB_LIBRARY, sLibrary);
if (ResManGetAliasFor(sLibrary, RESTYPE_NCS) == "")
{
Debug(sLibrary + ".ncs not present; loading library as chunk");
string sChunk = NssInclude(sLibrary) + NssVoidMain(NssFunction("OnLibraryLoad"));
string sError = ExecuteScriptChunk(sChunk, GetModule(), FALSE);
if (sError != "")
CriticalError("Could not load " + sLibrary + ": " + sError);
}
else
ExecuteScript(sLibrary, GetModule());
}
else
Error("Library " + sLibrary + " already loaded!");
}
void LoadLibraries(string sLibraries, int bForce = FALSE)
{
Debug("Attempting to " + (bForce ? "force " : "") + "load libraries " + sLibraries);
int i, nCount = CountList(sLibraries);
for (i = 0; i < nCount; i++)
LoadLibrary(GetListItem(sLibraries, i), bForce);
}
// Private function for GetScriptsByPrefix*(). Adds all scripts of nResType
// matching a prefix to a json array and returns it.
json _GetScriptsByPrefix(json jArray, string sPrefix, int nResType)
{
int i;
string sScript;
while ((sScript = ResManFindPrefix(sPrefix, nResType, ++i)) != "")
jArray = JsonArrayInsert(jArray, JsonString(sScript));
return jArray;
}
json GetScriptsByPrefix(string sPrefix)
{
json jScripts = _GetScriptsByPrefix(JsonArray(), sPrefix, RESTYPE_NCS);
jScripts = _GetScriptsByPrefix(jScripts, sPrefix, RESTYPE_NSS);
jScripts = JsonArrayTransform(jScripts, JSON_ARRAY_UNIQUE);
jScripts = JsonArrayTransform(jScripts, JSON_ARRAY_SORT_ASCENDING);
return jScripts;
}
void LoadLibrariesByPattern(string sPatterns, int bForce = FALSE)
{
if (sPatterns == "")
return;
Debug("Finding libraries matching \"" + sPatterns + "\"");
json jPatterns = ListToJson(sPatterns);
json jLibraries = FilterByPatterns(GetScriptsByPrefix(""), jPatterns, TRUE);
LoadLibraries(JsonToList(jLibraries), bForce);
}
void LoadLibrariesByPrefix(string sPrefix, int bForce = FALSE)
{
Debug("Finding libraries with prefix \"" + sPrefix + "\"");
json jLibraries = GetScriptsByPrefix(sPrefix);
LoadLibraries(JsonToList(jLibraries), bForce);
}
void LoadPrefixLibraries(string sPrefix, int bForce = FALSE)
{
Debug("LoadPrefixLibraries() is deprecated; use LoadLibrariesByPrefix()");
LoadLibrariesByPrefix(sPrefix, bForce);
}
int RunLibraryScript(string sScript, object oSelf = OBJECT_SELF)
{
if (sScript == "") return -1;
string sLibrary;
int nEntry;
sqlquery sqlScriptData = GetScriptData(sScript);
if (SqlStep(sqlScriptData))
{
sLibrary = SqlGetString(sqlScriptData, 0);
nEntry = SqlGetInt(sqlScriptData, 1);
}
DeleteLocalInt(oSelf, LIB_RETURN);
if (sLibrary != "")
{
Debug("Library script " + sScript + " found in " + sLibrary +
(nEntry != 0 ? " at entry " + IntToString(nEntry) : ""));
SetScriptParam(LIB_LIBRARY, sLibrary);
SetScriptParam(LIB_SCRIPT, sScript);
SetScriptParam(LIB_ENTRY, IntToString(nEntry));
if (ResManGetAliasFor(sLibrary, RESTYPE_NCS) == "")
{
Debug(sLibrary + ".ncs not present; running library script as chunk");
string sChunk = NssInclude(sLibrary) + NssVoidMain(nEntry ?
NssFunction("OnLibraryScript", NssQuote(sScript) + ", " + IntToString(nEntry)) :
NssFunction(sScript));
string sError = ExecuteScriptChunk(sChunk, oSelf, FALSE);
if (sError != "")
CriticalError("RunLibraryScript(" + sScript +") failed: " + sError);
}
else
ExecuteScript(sLibrary, oSelf);
}
else
{
Debug(sScript + " is not a library script; executing directly");
ExecuteScript(sScript, oSelf);
}
return GetLocalInt(oSelf, LIB_RETURN);
}
void RunLibraryScripts(string sScripts, object oSelf = OBJECT_SELF)
{
int i, nCount = CountList(sScripts);
for (i = 0; i < nCount; i++)
RunLibraryScript(GetListItem(sScripts, i), oSelf);
}
void RegisterLibraryScript(string sScript, int nEntry = 0)
{
string sLibrary = GetScriptParam(LIB_LIBRARY);
string sExist = GetScriptLibrary(sScript);
if (sLibrary != sExist && sExist != "")
Warning(sLibrary + " is overriding " + sExist + "'s implementation of " + sScript);
int nOldEntry = GetScriptEntry(sScript);
if (nOldEntry)
Warning(sLibrary + " already declared " + sScript +
" Old Entry: " + IntToString(nOldEntry) +
" New Entry: " + IntToString(nEntry));
AddLibraryScript(sLibrary, sScript, nEntry);
}
void LibraryReturn(int nValue)
{
SetLocalInt(OBJECT_SELF, LIB_RETURN, nValue);
}

View File

@@ -0,0 +1,78 @@
/// ----------------------------------------------------------------------------
/// @file util_i_library.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @brief Boilerplace code for creating a library dispatcher. Should only be
/// included in library scripts as it implements main().
/// ----------------------------------------------------------------------------
#include "util_i_libraries"
// -----------------------------------------------------------------------------
// Function Protoypes
// -----------------------------------------------------------------------------
// This is a user-defined function that registers function names to a unique (to
// this library) number. When the function name is run using RunLibraryScript(),
// this number will be passed to the user-defined function OnLibraryScript(),
// which resolves the call to the correct function.
//
// Example usage:
// void OnLibraryLoad()
// {
// RegisterLibraryScript("MyFunction");
// RegisterLibraryScript("MyOtherFunction");
// }
//
// or, if using nEntry...
// void OnLibraryLoad()
// {
// RegisterLibraryScript("MyFunction", 1);
// RegisterLibraryScript("MyOtherFunction", 2);
// }
void OnLibraryLoad();
// This is a user-defined function that routes a unique (to the module) script
// name (sScript) or a unique (to this library) number (nEntry) to a function.
//
// Example usage:
// void OnLibraryScript(string sScript, int nEntry)
// {
// if (sScript == "MyFunction") MyFunction();
// else if (sScript == "MyOtherFunction") MyOtherFunction();
// }
//
// or, using nEntry...
// void OnLibraryScript(string sScript, int nEntry)
// {
// switch (nEntry)
// {
// case 1: MyFunction(); break;
// case 2: MyOtherFunction(); break;
// }
// }
//
// For advanced usage, see the libraries included in the Core Framework.
void OnLibraryScript(string sScript, int nEntry);
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
// These are dummy implementations to prevent nwnsc from complaining that they
// do not exist. If you want to compile in the toolset rather than using nwnsc,
// comment these lines out.
// #pragma default_function(OnLibraryLoad)
// #pragma default_function(OnLibraryScript)
// -----------------------------------------------------------------------------
// Main Routine
// -----------------------------------------------------------------------------
void main()
{
if (GetScriptParam(LIB_ENTRY) == "")
OnLibraryLoad();
else
OnLibraryScript(GetScriptParam(LIB_SCRIPT),
StringToInt(GetScriptParam(LIB_ENTRY)));
}

View File

@@ -0,0 +1,98 @@
/// ----------------------------------------------------------------------------
/// @file util_i_lists.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Compatibility functions to convert between CSV and localvar lists.
/// ----------------------------------------------------------------------------
#include "util_i_csvlists"
#include "util_i_varlists"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
// Acceptable values for nListType in SplitList() and JoinList().
const int LIST_TYPE_FLOAT = 0;
const int LIST_TYPE_INT = 1;
const int LIST_TYPE_STRING = 2;
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Splits a comma-separated value list into a local variable list of the
/// given type.
/// @param oTarget Object on which to create the list
/// @param sList Source CSV list
/// @param sListName Name of the list to create or add to
/// @param bAddUnique If TRUE, prevents duplicate list items
/// @param nListType Type of list to create
/// LIST_TYPE_STRING (default)
/// LIST_TYPE_FLOAT
/// LIST_TYPE_INT
/// @returns JSON array of split CSV list
json SplitList(object oTarget, string sList, string sListName = "", int bAddUnique = FALSE, int nListType = LIST_TYPE_STRING);
/// @brief Joins a local variable list of a given type into a comma-separated
/// value list
/// @param oTarget Object from which to source the local variable list
/// @param sListName Name of the local variable list
/// @param bAddUnique If TRUE, prevents duplicate list items
/// @param nListType Type of local variable list
/// LIST_TYPE_STRING (default)
/// LIST_TYPE_FLOAT
/// LIST_TYPE_INT
/// @returns Joined CSV list of local variable list
string JoinList(object oTarget, string sListName = "", int bAddUnique = FALSE, int nListType = LIST_TYPE_STRING);
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
json SplitList(object oTarget, string sList, string sListName = "", int bAddUnique = FALSE, int nListType = LIST_TYPE_STRING)
{
json jList = JSON_ARRAY;
if (nListType == LIST_TYPE_STRING)
jList = ListToJson(sList, TRUE);
else
jList = JsonParse("[" + sList + "]");
string sListType = (nListType == LIST_TYPE_STRING ? VARLIST_TYPE_STRING :
nListType == LIST_TYPE_INT ? VARLIST_TYPE_INT :
VARLIST_TYPE_FLOAT);
if (bAddUnique == TRUE)
jList = JsonArrayTransform(jList, JSON_ARRAY_UNIQUE);
if (oTarget != OBJECT_INVALID)
_SetList(oTarget, sListType, sListName, jList);
return jList;
}
string JoinList(object oTarget, string sListName = "", int bAddUnique = FALSE, int nListType = LIST_TYPE_STRING)
{
string sListType = (nListType == LIST_TYPE_STRING ? VARLIST_TYPE_STRING :
nListType == LIST_TYPE_INT ? VARLIST_TYPE_INT :
VARLIST_TYPE_FLOAT);
json jList = _GetList(oTarget, sListType, sListName);
if (jList == JsonNull() || JsonGetLength(jList) == 0)
return "";
if (bAddUnique == TRUE)
jList = JsonArrayTransform(jList, JSON_ARRAY_UNIQUE);
string sList;
if (nListType == LIST_TYPE_STRING)
sList = JsonToList(jList);
else
{
sList = JsonDump(jList);
sList = GetStringSlice(sList, 1, GetStringLength(sList) - 2);
}
return sList;
}

View File

@@ -0,0 +1,128 @@
/// ----------------------------------------------------------------------------
/// @file util_i_matching.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Utilities for pattern matching.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Return whether a string matches a glob pattern.
/// @param sString The string to check.
/// @param sPattern A glob pattern. Supported syntax:
/// - `*`: match zero or more characters
/// - `?`: match a single character
/// - `[abc]`: match any of a, b, or c
/// - `[a-z]`: match any character from a-z
/// - other text is matched literally
/// @returns TRUE if sString matches sPattern; FALSE otherwise.
int GetMatchesPattern(string sString, string sPattern);
/// @brief Return whether a string matches any of an array of glob patterns.
/// @param sString The string to check.
/// @param sPattern A json array of glob patterns.
/// @returns TRUE if sString matches sPattern; FALSE otherwise.
/// @see GetMatchesPattern() for supported glob syntax.
int GetMatchesPatterns(string sString, json jPatterns);
/// @brief Return if any element of a json array matches a glob pattern.
/// @param jArray A json array of strings to check.
/// @param sPattern A glob pattern.
/// @param bNot If TRUE, will invert the selection, returning whether any
/// element does not match the glob pattern.
/// @returns TRUE if any element of jArray matches sPattern; FALSE otherwise.
/// @see GetMatchesPattern() for supported glob syntax.
int GetAnyMatchesPattern(json jArray, string sPattern, int bNot = FALSE);
/// @brief Return if all elements of a json array match a glob pattern.
/// @param jArray A json array of strings to check.
/// @param sPattern A glob pattern.
/// @param bNot If TRUE, will invert the selection, returning whether all
/// elements do not match the glob pattern.
/// @returns TRUE if all elements of jArray match sPattern; FALSE otherwise.
/// @see GetMatchesPattern() for supported glob syntax.
int GetAllMatchesPattern(json jArray, string sPattern, int bNot = FALSE);
/// @brief Filter out all elements of an array that do not match a glob pattern.
/// @param jArray A json array of strings to filter.
/// @param sPattern A glob pattern.
/// @param bNot If TRUE, will invert the selection, only keeping elements that
/// do not match the glob pattern.
/// @returns A modified copy of jArray with all non-matching elements removed.
/// @see GetMatchesPattern() for supported glob syntax.
json FilterByPattern(json jArray, string sPattern, int bNot = FALSE);
/// @brief Filter out all elements of an array that do not match any of an array
/// of glob patterns.
/// @param jArray A json array of strings to filter.
/// @param jPatterns A json array of glob patterns.
/// @param bOrderByPatterns If TRUE, will order the results by the pattern they
/// matched with rather than by their placement in jArray.
/// @returns A modified copy of jArray with all non-matching elements removed.
/// @see GetMatchesPattern() for supported glob syntax.
json FilterByPatterns(json jArray, json jPatterns, int bOrderByPatterns = FALSE);
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
int GetMatchesPattern(string sString, string sPattern)
{
sqlquery q = SqlPrepareQueryObject(GetModule(),
"SELECT @string GLOB @pattern;");
SqlBindString(q, "@string", sString);
SqlBindString(q, "@pattern", sPattern);
return SqlStep(q) ? SqlGetInt(q, 0) : FALSE;
}
int GetMatchesPatterns(string sString, json jPatterns)
{
sqlquery q = SqlPrepareQueryObject(GetModule(),
"SELECT 1 FROM json_each(@patterns) WHERE @value GLOB json_each.value;");
SqlBindString(q, "@value", sString);
SqlBindJson(q, "@patterns", jPatterns);
return SqlStep(q) ? SqlGetInt(q, 0) : FALSE;
}
int GetAnyMatchesPattern(json jArray, string sPattern, int bNot = FALSE)
{
jArray = FilterByPattern(jArray, sPattern, bNot);
return JsonGetLength(jArray) != 0;
}
int GetAllMatchesPattern(json jArray, string sPattern, int bNot = FALSE)
{
return jArray == FilterByPattern(jArray, sPattern, bNot);
}
json FilterByPattern(json jArray, string sPattern, int bNot = FALSE)
{
if (!JsonGetLength(jArray))
return jArray;
sqlquery q = SqlPrepareQueryObject(GetModule(),
"SELECT json_group_array(value) FROM json_each(@array) " +
"WHERE value " + (bNot ? "NOT " : "") + "GLOB @pattern;");
SqlBindJson(q, "@array", jArray);
SqlBindString(q, "@pattern", sPattern);
return SqlStep(q) ? SqlGetJson(q, 0) : JsonArray();
}
json FilterByPatterns(json jArray, json jPatterns, int bOrderByPattern = FALSE)
{
if (!JsonGetLength(jArray) || ! JsonGetLength(jPatterns))
return jArray;
sqlquery q = SqlPrepareQueryObject(GetModule(),
"SELECT json_group_array(value) FROM " +
"(SELECT DISTINCT v.key, v.value FROM " +
"json_each(@values) v JOIN " +
"json_each(@patterns) p " +
"WHERE v.value GLOB p.value " +
(bOrderByPattern ? "ORDER BY p.key);" : ");"));
SqlBindJson(q, "@values", jArray);
SqlBindJson(q, "@patterns", jPatterns);
return SqlStep(q) ? SqlGetJson(q, 0) : JsonArray();
}

177
_module/nss/util_i_math.nss Normal file
View File

@@ -0,0 +1,177 @@
/// ----------------------------------------------------------------------------
/// @file util_i_math.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @brief Useful math utility functions.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Return the closest integer to the binary logarithm of a number.
int log2(int n);
/// @brief Restrict an integer to a range.
/// @param nValue The number to evaluate.
/// @param nMin The minimum value for the number.
/// @param nMax The maximum value for the number.
/// @returns nValue if it is between nMin and nMax. Otherwise returns the
/// closest of nMin or nMax.
int clamp(int nValue, int nMin, int nMax);
/// @brief Restrict a float to a range.
/// @param fValue The number to evaluate.
/// @param fMin The minimum value for the number.
/// @param fMax The maximum value for the number.
/// @returns fValue if it is between fMin and fMax. Otherwise returns the
/// closest of fMin or fMax.
float fclamp(float fValue, float fMin, float fMax);
/// @brief Return the larger of two integers.
int max(int a, int b);
/// @brief Return the smaller of two integers.
int min(int a, int b);
/// @brief Return the sign of an integer.
/// @returns -1 if n is negative, 0 if 0, or 1 if positive.
int sign(int n);
/// @brief Return the larger of two floats.
float fmax(float a, float b);
/// @brief Return the smaller of two floats.
float fmin(float a, float b);
/// @brief Return the sign of a float.
/// @returns -1 if f is negative, 0 if 0, or 1 if positive.
int fsign(float f);
/// @brief Truncate a float (i.e., remove numbers to the right of the decimal
/// point).
float trunc(float f);
/// @brief Return the fractional part of a float (i.e., numbers to the right of
/// the decimal point).
float frac(float f);
/// @brief Return a % b (modulo function).
/// @param a The dividend
/// @param b The divisor
/// @note For consistency with NWN's integer modulo operator, the result has the
/// same sign as a (i.e., fmod(-1, 2) == -1).
float fmod(float a, float b);
/// @brief Round a float down to the nearest whole number.
float floor(float f);
/// @brief Round a float up to the nearest whole number.
float ceil(float f);
/// @brief Round a float towards to the nearest whole number.
/// @note In case of a tie (i.e., +/- 0.5), rounds away from 0.
float round(float f);
/// @brief Determine if x is in [a..b]
/// @param x Value to compare
/// @param a Low end of range
/// @param b High end of range
int between(int x, int a, int b);
/// @brief Determine if x is in [a..b]
/// @param x Value to compare
/// @param a Low end of range
/// @param b High end of range
int fbetween(float x, float a, float b);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
int log2(int n)
{
int nResult;
while (n >>= 1)
nResult++;
return nResult;
}
int clamp(int nValue, int nMin, int nMax)
{
return (nValue < nMin) ? nMin : ((nValue > nMax) ? nMax : nValue);
}
float fclamp(float fValue, float fMin, float fMax)
{
return (fValue < fMin) ? fMin : ((fValue > fMax) ? fMax : fValue);
}
int max(int a, int b)
{
return (b > a) ? b : a;
}
int min(int a, int b)
{
return (b > a) ? a : b;
}
int sign(int n)
{
return (n > 0) ? 1 : (n < 0) ? -1 : 0;
}
float fmax(float a, float b)
{
return (b > a) ? b : a;
}
float fmin(float a, float b)
{
return (b > a) ? a : b;
}
int fsign(float f)
{
return f > 0.0 ? 1 : f < 0.0 ? -1 : 0;
}
float trunc(float f)
{
return IntToFloat(FloatToInt(f));
}
float frac(float f)
{
return f - trunc(f);
}
float fmod(float a, float b)
{
return a - b * trunc(a / b);
}
float floor(float f)
{
return IntToFloat(FloatToInt(f) - (f < 0.0));
}
float ceil(float f)
{
return IntToFloat(FloatToInt(f) + (trunc(f) < f));
}
float round(float f)
{
return IntToFloat(FloatToInt(f + (f < 0.0 ? -0.5 : 0.5)));
}
int between(int x, int a, int b)
{
return ((x - a) * (x - b)) <= 0;
}
int fbetween(float x, float a, float b)
{
return ((x - a) * (x - b)) <= 0.0;
}

224
_module/nss/util_i_nss.nss Normal file
View File

@@ -0,0 +1,224 @@
/// ----------------------------------------------------------------------------
/// @file util_i_nss.nss
/// @author Daz <daztek@gmail.com>
/// @brief Functions to assemble scripts for use with `ExecuteScriptChunk()`.
/// @note Borrowed from https://github.com/Daztek/EventSystem
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Return a `void main()` block.
/// @param sContents The contents of the block.
string NssVoidMain(string sContents);
/// @brief Return an `int StartingConditional()` block.
/// @param sContents The contents of the block.
string NssStartingConditional(string sContents);
/// @brief Return an include directive.
/// @param sIncludeFile The file to include.
string NssInclude(string sIncludeFile);
/// @brief Return an if statement with a comparison.
/// @param sLeft The left side of the comparison. If sComparison or sRight are
/// blank, will be evalated as a boolean expression.
/// @param sComparison The comparison operator.
/// @param sRight The right side of the comparison.
string NssIf(string sLeft, string sComparison = "", string sRight = "");
/// @brief Return an else statement.
string NssElse();
/// @brief Return an else-if statement with a comparison.
/// @param sLeft The left side of the comparison. If sComparison or sRight are
/// blank, will be evalated as a boolean expression.
/// @param sComparison The comparison operator.
/// @param sRight The right side of the comparison.
string NssElseIf(string sLeft, string sComparison = "", string sRight = "");
/// @brief Create a while statement with a comparison.
/// @param sLeft The left side of the comparison. If sComparison or sRight are
/// blank, will be evalated as a boolean expression.
/// @param sComparison The comparison operator.
/// @param sRight The right side of the comparison.
string NssWhile(string sLeft, string sComparison = "", string sRight = "");
/// @brief Return a script block bounded by curly brackets.
/// @param sContents The contents of the block.
string NssBrackets(string sContents);
/// @brief Return a string wrapped in double quotes.
/// @param sString The string to wrap.
string NssQuote(string sString);
/// @brief Return a switch statement.
/// @param sVariable The variable to evaluate in the switch statement.
/// @param sCases A series of case statements the switch should dispatch to.
/// @see NssCase().
string NssSwitch(string sVariable, string sCases);
/// @brief Return a case statement.
/// @param nCase The value matching the switch statement.
/// @param sContents The contents of the case block.
/// @param bBreak If TRUE, will add a break statement after sContents.
string NssCase(int nCase, string sContents, int bBreak = TRUE);
/// @brief Return an object variable declaration and/or assignment.
/// @param sVarName The name for the variable.
/// @param sValue The value to assign to the variable. If blank, no value will
/// be assigned.
/// @param bIncludeType If TRUE, the variable will be declared as well.
string NssObject(string sVarName, string sValue = "", int bIncludeType = TRUE);
/// @brief Return a string variable declaration and/or assignment.
/// @param sVarName The name for the variable.
/// @param sValue The value to assign to the variable. If blank, no value will
/// be assigned.
/// @param bIncludeType If TRUE, the variable will be declared as well.
string NssString(string sVarName, string sValue = "", int bIncludeType = TRUE);
/// @brief Return an int variable declaration and/or assignment.
/// @param sVarName The name for the variable.
/// @param sValue The value to assign to the variable. If blank, no value will
/// be assigned.
/// @param bIncludeType If TRUE, the variable will be declared as well.
string NssInt(string sVarName, string sValue = "", int bIncludeType = TRUE);
/// @brief Return a float variable declaration and/or assignment.
/// @param sVarName The name for the variable.
/// @param sValue The value to assign to the variable. If blank, no value will
/// be assigned.
/// @param bIncludeType If TRUE, the variable will be declared as well.
string NssFloat(string sVarName, string sValue = "", int bIncludeType = TRUE);
/// @brief Return a vector variable declaration and/or assignment.
/// @param sVarName The name for the variable.
/// @param sValue The value to assign to the variable. If blank, no value will
/// be assigned.
/// @param bIncludeType If TRUE, the variable will be declared as well.
string NssVector(string sVarName, string sValue = "", int bIncludeType = TRUE);
/// @brief Return a location variable declaration and/or assignment.
/// @param sVarName The name for the variable.
/// @param sValue The value to assign to the variable. If blank, no value will
/// be assigned.
/// @param bIncludeType If TRUE, the variable will be declared as well.
string NssLocation(string sVarName, string sValue = "", int bIncludeType = TRUE);
/// @brief Return a call, prototype, or definition of a function.
/// @param sFunction The name of the function.
/// @param sArguments The list of arguments for the function.
/// @param bAddSemicolon If TRUE, a semicolon will be assed to the end of the
/// statement.
string NssFunction(string sFunction, string sArguments = "", int bAddSemicolon = TRUE);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
string NssVoidMain(string sContents)
{
return "void main() { " + sContents + " }";
}
string NssStartingConditional(string sContents)
{
return "int StartingConditional() { return " + sContents + " }";
}
string NssInclude(string sIncludeFile)
{
return sIncludeFile == "" ? sIncludeFile : "#" + "include \"" + sIncludeFile + "\" ";
}
string NssCompare(string sLeft, string sComparison, string sRight)
{
return (sComparison == "" || sRight == "") ? sLeft : sLeft + " " + sComparison + " " + sRight;
}
string NssIf(string sLeft, string sComparison = "", string sRight = "")
{
return "if (" + NssCompare(sLeft, sComparison, sRight) + ") ";
}
string NssElse()
{
return "else ";
}
string NssElseIf(string sLeft, string sComparison = "", string sRight = "")
{
return "else if (" + NssCompare(sLeft, sComparison, sRight) + ") ";
}
string NssWhile(string sLeft, string sComparison = "", string sRight = "")
{
return "while (" + NssCompare(sLeft, sComparison, sRight) + ") ";
}
string NssBrackets(string sContents)
{
return "{ " + sContents + " } ";
}
string NssQuote(string sString)
{
return "\"" + sString + "\"";
}
string NssSwitch(string sVariable, string sCases)
{
return "switch (" + sVariable + ") { " + sCases + " }";
}
string NssCase(int nCase, string sContents, int bBreak = TRUE)
{
return "case " + IntToString(nCase) + ": { " + sContents + (bBreak ? " break;" : "") + " } ";
}
string NssSemicolon(string sString)
{
return (GetStringRight(sString, 1) == ";" || GetStringRight(sString, 2) == "; ") ? sString + " " : sString + "; ";
}
string NssVariable(string sType, string sVarName, string sValue)
{
return sType + " " + sVarName + (sValue == "" ? "; " : " = " + NssSemicolon(sValue));
}
string NssObject(string sVarName, string sValue = "", int bIncludeType = TRUE)
{
return NssVariable(bIncludeType ? "object" : "", sVarName, sValue);
}
string NssString(string sVarName, string sValue = "", int bIncludeType = TRUE)
{
return NssVariable(bIncludeType ? "string" : "", sVarName, sValue);
}
string NssInt(string sVarName, string sValue = "", int bIncludeType = TRUE)
{
return NssVariable(bIncludeType ? "int" : "", sVarName, sValue);
}
string NssFloat(string sVarName, string sValue = "", int bIncludeType = TRUE)
{
return NssVariable(bIncludeType ? "float" : "", sVarName, sValue);
}
string NssVector(string sVarName, string sValue = "", int bIncludeType = TRUE)
{
return NssVariable(bIncludeType ? "vector" : "", sVarName, sValue);
}
string NssLocation(string sVarName, string sValue = "", int bIncludeType = TRUE)
{
return NssVariable(bIncludeType ? "location" : "", sVarName, sValue);
}
string NssFunction(string sFunction, string sArguments = "", int bAddSemicolon = TRUE)
{
return sFunction + "(" + sArguments + (bAddSemicolon ? ");" : ")") + " ";
}

View File

@@ -0,0 +1,175 @@
/// ----------------------------------------------------------------------------
/// @file util_i_sqlite.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @brief Helper functions for NWN:EE SQLite databases.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Alias for `SqlPreparerQueryObject(GetModule(), sSQL)`.
sqlquery SqlPrepareQueryModule(string sSQL);
/// @brief Prepares and executes a query on a PC's peristent database.
/// @param oPC The PC that stores the database.
/// @param sQuery The SQL statement to execute.
/// @returns Whether the query was successful.
int SqlExecPC(object oPC, string sQuery);
/// @brief Prepares and executes a query on the module's volatile database.
/// @param sQuery The SQL statement to execute.
/// @returns Whether the query was successful.
int SqlExecModule(string sQuery);
/// @brief Prepares and executes a query on a persistent campaign database.
/// @param sDatabase The name of the campaign database file (minus extension).
/// @param sQuery The SQL statement to execute.
/// @returns Whether the query was successful.
int SqlExecCampaign(string sDatabase, string sQuery);
/// @brief Creates a table in a PC's persistent database.
/// @param oPC The PC that stores the database.
/// @param sTable The name of the table.
/// @param sStructure The SQL describing the structure of the table (i.e.,
/// everything that would go between the parentheses).
/// @param bForce Whether to drop an existing table.
void SqlCreateTablePC(object oPC, string sTable, string sStructure, int bForce = FALSE);
/// @brief Creates a table in the module's volatile database.
/// @param sTable The name of the table.
/// @param sStructure The SQL describing the structure of the table (i.e.,
/// everything that would go between the parentheses).
/// @param bForce Whether to drop an existing table.
void SqlCreateTableModule(string sTable, string sStructure, int bForce = FALSE);
/// @brief Creates a table in a persistent campaign database.
/// @param sDatabase The name of the campaign database file (minus extension).
/// @param sTable The name of the table.
/// @param sStructure The SQL describing the structure of the table (i.e.,
/// everything that would go between the parentheses).
/// @param bForce Whether to drop an existing table.
void SqlCreateTableCampaign(string sDatabase, string sTable, string sStructure, int bForce = FALSE);
/// @brief Checks if a table exists the PC's persistent database.
/// @param oPC The PC that stores the database.
/// @param sTable The name of the table to check for.
/// @returns Whether the table exists.
int SqlGetTableExistsPC(object oPC, string sTable);
/// @brief Checks if a table exists in the module's volatile database.
/// @param sTable The name of the table to check for.
/// @returns Whether the table exists.
int SqlGetTableExistsModule(string sTable);
/// @brief Checks if a table exists in a peristent campaign database.
/// @param sDatabase The name of the campaign database file (minus extension).
/// @param sTable The name of the table to check for.
/// @returns Whether the table exists.
int SqlGetTableExistsCampaign(string sDatabase, string sTable);
/// @brief Gets the ID of the last row inserted into a PC's persistent database.
/// @param oPC The PC that stores the database.
/// @returns The ID of the last inserted row or -1 on error.
int SqlGetLastInsertIdPC(object oPC);
/// @brief Gets the ID of the last row inserted into the module's volatile
/// database.
/// @returns The ID of the last inserted row or -1 on error.
int SqlGetLastInsertIdModule();
/// @brief Gets the ID of the last row inserted into a persistent campaign
/// database.
/// @param sDatabase The name of the campaign database file (minus extension).
/// @returns The ID of the last inserted row or -1 on error.
int SqlGetLastInsertIdCampaign(string sDatabase);
// -----------------------------------------------------------------------------
// Function Definitions
// -----------------------------------------------------------------------------
sqlquery SqlPrepareQueryModule(string sSQL)
{
return SqlPrepareQueryObject(GetModule(), sSQL);
}
int SqlExecPC(object oPC, string sQuery)
{
return SqlStep(SqlPrepareQueryObject(oPC, sQuery));
}
int SqlExecModule(string sQuery)
{
return SqlStep(SqlPrepareQueryModule(sQuery));
}
int SqlExecCampaign(string sDatabase, string sQuery)
{
return SqlStep(SqlPrepareQueryCampaign(sDatabase, sQuery));
}
void SqlCreateTablePC(object oPC, string sTable, string sStructure, int bForce = FALSE)
{
if (bForce)
SqlExecPC(oPC, "DROP TABLE IF EXISTS " + sTable + ";");
SqlExecPC(oPC, "CREATE TABLE IF NOT EXISTS " + sTable + "(" + sStructure + ");");
}
void SqlCreateTableModule(string sTable, string sStructure, int bForce = FALSE)
{
if (bForce)
SqlExecModule("DROP TABLE IF EXISTS " + sTable + ";");
SqlExecModule("CREATE TABLE IF NOT EXISTS " + sTable + "(" + sStructure + ");");
}
void SqlCreateTableCampaign(string sDatabase, string sTable, string sStructure, int bForce = FALSE)
{
if (bForce)
SqlExecCampaign(sDatabase, "DROP TABLE IF EXISTS " + sTable + ";");
SqlExecCampaign(sDatabase, "CREATE TABLE IF NOT EXISTS " + sTable + "(" + sStructure + ");");
}
int SqlGetTableExistsPC(object oPC, string sTable)
{
string sQuery = "SELECT name FROM sqlite_master WHERE type='table' AND name = @table;";
sqlquery qQuery = SqlPrepareQueryObject(oPC, sQuery);
SqlBindString(qQuery, "@table", sTable);
return SqlStep(qQuery);
}
int SqlGetTableExistsModule(string sTable)
{
string sQuery = "SELECT name FROM sqlite_master WHERE type='table' AND name = @table;";
sqlquery qQuery = SqlPrepareQueryModule(sQuery);
SqlBindString(qQuery, "@table", sTable);
return SqlStep(qQuery);
}
int SqlGetTableExistsCampaign(string sDatabase, string sTable)
{
string sQuery = "SELECT name FROM sqlite_master WHERE type='table' AND name = @table;";
sqlquery qQuery = SqlPrepareQueryCampaign(sDatabase, sQuery);
SqlBindString(qQuery, "@table", sTable);
return SqlStep(qQuery);
}
int SqlGetLastInsertIdPC(object oPC)
{
sqlquery qQuery = SqlPrepareQueryObject(oPC, "SELECT last_insert_rowid();");
return SqlStep(qQuery) ? SqlGetInt(qQuery, 0) : -1;
}
int SqlGetLastInsertIdModule()
{
sqlquery qQuery = SqlPrepareQueryModule("SELECT last_insert_rowid();");
return SqlStep(qQuery) ? SqlGetInt(qQuery, 0) : -1;
}
int SqlGetLastInsertIdCampaign(string sDatabase)
{
sqlquery qQuery = SqlPrepareQueryCampaign(sDatabase, "SELECT last_insert_rowid();");
return SqlStep(qQuery) ? SqlGetInt(qQuery, 0) : -1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,422 @@
/// ----------------------------------------------------------------------------
/// @file util_i_strings.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for manipulating strings.
/// ----------------------------------------------------------------------------
/// @details This file holds utility functions for manipulating strings.
/// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
const string CHARSET_NUMERIC = "0123456789";
const string CHARSET_ALPHA = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string CHARSET_ALPHA_LOWER = "abcdefghijklmnopqrstuvwxyz";
const string CHARSET_ALPHA_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Return the number of occurrences of a substring within a string.
/// @param sString The string to search.
/// @param sSubString The substring to search for.
int GetSubStringCount(string sString, string sSubString);
/// @brief Return the position of a given occurrence of a substring within a
/// string.
/// @param sString The string to search.
/// @param sSubString The substring to search for.
/// @param nNth The occurrence to search for. Uses a zero-based index.
/// @returns The position of the start of the nNth occurrence of the substring,
/// or -1 if the substring did not occur at least nNth + 1 times.
int FindSubStringN(string sString, string sSubString, int nNth = 0);
/// @brief Return the character at a position in a string.
/// @param sString The string to search.
/// @param nPos The position to check.
/// @returns "" if sString is not nPos + 1 characters long.
string GetChar(string sString, int nPos);
/// @brief Return the substring of a string bounded by a start and end position.
/// @param sString The string to search.
/// @param nStart The starting position of the substring to return.
/// @param nEnd The ending position of the substring to return. If -1, will
/// return to the end of the string.
/// @returns "" if nStart is not at least nStart + 1 characters long or if nEnd
/// is < nStart and not -1.
/// @note Both nStart and nEnd are inclusive, so if nStart == nEnd, the
/// character at that index will be returned.
string GetStringSlice(string sString, int nStart, int nEnd = -1);
/// @brief Replace the substring bounded by a string slice with another string.
/// @param sString The string to search.
/// @param sSub The substring to replace with.
/// @param nStart The starting position in sString of the substring to replace.
/// @param nEnd The ending position in sString of the substring to replace.
string ReplaceSubString(string sString, string sSub, int nStart, int nEnd);
/// @brief Replace a substring in a string with another string.
/// @param sString The string to search.
/// @param sToken The substring to search for.
/// @param sSub The substring to replace with.
string SubstituteSubString(string sString, string sToken, string sSub);
/// @brief Replace all substrings in a string with another string.
/// @param sString The string to search.
/// @param sToken The substring to search for.
/// @param sSub The substring to replace with.
string SubstituteSubStrings(string sString, string sToken, string sSub);
/// @brief Return whether a string contains a substring.
/// @param sString The string to search.
/// @param sSubString The substring to search for.
/// @param nStart The position in sString to begin searching from (0-based).
/// @returns TRUE if sSubString is in sString, FALSE otherwise.
int HasSubString(string sString, string sSubString, int nStart = 0);
/// @brief Return whether any of a string's characters are in a character set.
/// @param sString The string to search.
/// @param sSet The set of characters to search for.
/// @returns TRUE if any characters are in the set; FALSE otherwise.
int GetAnyCharsInSet(string sString, string sSet);
/// @brief Return whether all of a string's characters are in a character set.
/// @param sString The string to search.
/// @param sSet The set of characters to search for.
/// @returns TRUE if all characters are in the set; FALSE otherwise.
int GetAllCharsInSet(string sString, string sSet);
/// @brief Return whether all letters in a string are upper-case.
/// @param sString The string to check.
int GetIsUpperCase(string sString);
/// @brief Return whether all letters in a string are lower-case.
/// @param sString The string to check.
int GetIsLowerCase(string sString);
/// @brief Return whether all characters in sString are letters.
/// @param sString The string to check.
int GetIsAlpha(string sString);
/// @brief Return whether all characters in sString are digits.
/// @param sString The string to check.
int GetIsNumeric(string sString);
/// @brief Return whether all characters in sString are letters or digits.
/// @param sString The string to check.
int GetIsAlphaNumeric(string sString);
/// @brief Trim characters from the left side of a string.
/// @param sString The string to trim.
/// @param sRemove The set of characters to remove.
string TrimStringLeft(string sString, string sRemove = " ");
/// @brief Trim characters from the right side of a string.
/// @param sString The string to trim.
/// @param sRemove The set of characters to remove.
string TrimStringRight(string sString, string sRemove = " ");
/// @brief Trim characters from both sides of a string.
/// @param sString The string to trim.
/// @param sRemove The set of characters to remove.
string TrimString(string sString, string sRemove = " ");
/// @brief Interpolate values from a json array into a string using sqlite's
/// printf().
/// @param jArray A json array containing float, int, or string elements to
/// interpolate. The number of elements must match the number of format
/// specifiers in sFormat.
/// @param sFormat The string to interpolate the values into. Must contain
/// format specifiers that correspond to the elements in jArray. For details
/// on format specifiers, see https://sqlite.org/printf.html.
/// @example
/// FormatValues(JsonParse("[\"Blue\", 255]"), "%s: #%06X"); // "Blue: #0000FF"
string FormatValues(json jArray, string sFormat);
/// @brief Interpolate a float into a string using sqlite's printf().
/// @param f A float to interpolate. Will be passed as an argument to the query
/// as many times as necessary to cover all format specifiers.
/// @param sFormat The string to interpolate the value into. For details on
/// format specifiers, see https://sqlite.org/printf.html.
/// @example
/// FormatFloat(15.0, "%d"); // "15"
/// FormatFloat(15.0, "%.2f"); // "15.00"
/// FormatFloat(15.0, "%05.1f"); // "015.0"
string FormatFloat(float f, string sFormat);
/// @brief Interpolate an int into a string using sqlite's printf().
/// @param n An int to interpolate. Will be passed as an argument to the query
/// as many times as necessary to cover all format specifiers.
/// @param sFormat The string to interpolate the value into. For details on
/// format specifiers, see https://sqlite.org/printf.html.
/// @example
/// FormatInt(15, "%d"); // "15"
/// FormatInt(15, "%04d"); // "0015"
/// FormatInt(15, "In hexadecimal, %d is %#x"); // "In hexadecimal, 15 is 0xf"
/// FormatInt(1000, "%,d"); // "1,000"
string FormatInt(int n, string sFormat);
/// @brief Interpolate a string into another string using sqlite's printf().
/// @param s A string to interpolate. Will be passed as an argument to the query
/// as many times as necessary to cover all format specifiers.
/// @param sFormat The string to interpolate the value into. For details on
/// format specifiers, see https://sqlite.org/printf.html.
/// @example
/// FormatString("foo", "%sbar"); // "foobar"
/// FormatString("foo", "%5sbar"); // " foobar"
/// FormatString("foo", "%-5sbar"); // "foo bar"
string FormatString(string s, string sFormat);
/// @brief Substitute tokens in a string with values from a json array.
/// @param s The string to interpolate the values into. Should have tokens wich
/// contain sDesignator followed by a number denoting the position of the
/// value in jArray (1-based index).
/// @param jArray An array of values to interpolate. May be any combination of
/// strings, floats, decimals, or booleans.
/// @param sDesignator The character denoting the beginning of a token.
/// @example
/// // Assumes jArray = ["Today", 34, 2.5299999999, true];
/// SubstituteString("$1, I ran $2 miles.", jArray); // "Today, I ran 34 miles."
/// SubstituteString("The circle's radius is $3.", jArray); // "The circle's radius is 2.53."
/// SubstituteString("The applicant answered: $4", jArray); // "The applicant answered: true"
string SubstituteString(string s, json jArray, string sDesignator = "$");
/// @brief Repeats a string multiple times.
/// @param s The string to repeat.
/// @param n The number of times to repeat s.
/// @returns The repeated string.
string RepeatString(string s, int n);
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
int GetSubStringCount(string sString, string sSubString)
{
if (sString == "" || sSubString == "")
return 0;
int nLength = GetStringLength(sSubString);
int nCount, nPos = FindSubString(sString, sSubString);
while (nPos != -1)
{
nCount++;
nPos = FindSubString(sString, sSubString, nPos + nLength);
}
return nCount;
}
int FindSubStringN(string sString, string sSubString, int nNth = 0)
{
if (nNth < 0 || sString == "" || sSubString == "")
return -1;
int nLength = GetStringLength(sSubString);
int nPos = FindSubString(sString, sSubString);
while (--nNth >= 0 && nPos != -1)
nPos = FindSubString(sString, sSubString, nPos + nLength);
return nPos;
}
string GetChar(string sString, int nPos)
{
return GetSubString(sString, nPos, 1);
}
string GetStringSlice(string sString, int nStart, int nEnd = -1)
{
int nLength = GetStringLength(sString);
if (nEnd < 0 || nEnd > nLength)
nEnd = nLength;
if (nStart < 0 || nStart > nEnd)
return "";
return GetSubString(sString, nStart, nEnd - nStart + 1);
}
string ReplaceSubString(string sString, string sSub, int nStart, int nEnd)
{
int nLength = GetStringLength(sString);
if (nStart < 0 || nStart >= nLength || nStart > nEnd)
return sString;
return GetSubString(sString, 0, nStart) + sSub +
GetSubString(sString, nEnd + 1, nLength - nEnd);
}
string SubstituteSubString(string sString, string sToken, string sSub)
{
int nPos;
if ((nPos = FindSubString(sString, sToken)) == -1)
return sString;
return ReplaceSubString(sString, sSub, nPos, nPos + GetStringLength(sToken) - 1);
}
string SubstituteSubStrings(string sString, string sToken, string sSub)
{
while (FindSubString(sString, sToken) >= 0)
sString = SubstituteSubString(sString, sToken, sSub);
return sString;
}
int HasSubString(string sString, string sSubString, int nStart = 0)
{
return FindSubString(sString, sSubString, nStart) >= 0;
}
int GetAnyCharsInSet(string sString, string sSet)
{
int i, nLength = GetStringLength(sString);
for (i = 0; i < nLength; i++)
{
if (HasSubString(sSet, GetChar(sString, i)))
return TRUE;
}
return FALSE;
}
int GetAllCharsInSet(string sString, string sSet)
{
int i, nLength = GetStringLength(sString);
for (i = 0; i < nLength; i++)
{
if (!HasSubString(sSet, GetChar(sString, i)))
return FALSE;
}
return TRUE;
}
int GetIsUpperCase(string sString)
{
return GetAllCharsInSet(sString, CHARSET_ALPHA_UPPER + CHARSET_NUMERIC);
}
int GetIsLowerCase(string sString)
{
return GetAllCharsInSet(sString, CHARSET_ALPHA_LOWER + CHARSET_NUMERIC);
}
int GetIsAlpha(string sString)
{
return GetAllCharsInSet(sString, CHARSET_ALPHA);
}
int GetIsNumeric(string sString)
{
return GetAllCharsInSet(sString, CHARSET_NUMERIC);
}
int GetIsAlphaNumeric(string sString)
{
return GetAllCharsInSet(sString, CHARSET_ALPHA + CHARSET_NUMERIC);
}
string TrimStringLeft(string sString, string sRemove = " ")
{
return RegExpReplace("^(?:" + sRemove + ")*", sString, "");
}
string TrimStringRight(string sString, string sRemove = " ")
{
return RegExpReplace("(:?" + sRemove + ")*$", sString, "");
}
string TrimString(string sString, string sRemove = " ")
{
return RegExpReplace("^(:?" + sRemove + ")*|(?:" + sRemove + ")*$", sString, "");
}
string FormatValues(json jArray, string sFormat)
{
if (JsonGetType(jArray) != JSON_TYPE_ARRAY)
return "";
string sArgs;
int i, nLength = JsonGetLength(jArray);
for (i = 0; i < nLength; i++)
sArgs += ", @" + IntToString(i);
sqlquery q = SqlPrepareQueryObject(GetModule(), "SELECT printf(@format" + sArgs + ");");
SqlBindString(q, "@format", sFormat);
for (i = 0; i < nLength; i++)
{
string sParam = "@" + IntToString(i);
json jValue = JsonArrayGet(jArray, i);
switch (JsonGetType(jValue))
{
case JSON_TYPE_FLOAT: SqlBindFloat (q, sParam, JsonGetFloat (jValue)); break;
case JSON_TYPE_INTEGER: SqlBindInt (q, sParam, JsonGetInt (jValue)); break;
case JSON_TYPE_STRING: SqlBindString(q, sParam, JsonGetString(jValue)); break;
default: break;
}
}
return SqlStep(q) ? SqlGetString(q, 0) : "";
}
string FormatFloat(float f, string sFormat)
{
json jArray = JsonArray();
int i, nCount = GetSubStringCount(sFormat, "%");
for (i = 0; i < nCount; i++)
JsonArrayInsertInplace(jArray, JsonFloat(f));
return FormatValues(jArray, sFormat);
}
string FormatInt(int n, string sFormat)
{
json jArray = JsonArray();
int i, nCount = GetSubStringCount(sFormat, "%");
for (i = 0; i < nCount; i++)
JsonArrayInsertInplace(jArray, JsonInt(n));
return FormatValues(jArray, sFormat);
}
string FormatString(string s, string sFormat)
{
json jArray = JsonArray();
int i, nCount = GetSubStringCount(sFormat, "%");
for (i = 0; i < nCount; i++)
JsonArrayInsertInplace(jArray, JsonString(s));
return FormatValues(jArray, sFormat);
}
string SubstituteString(string s, json jArray, string sDesignator = "$")
{
if (JsonGetType(jArray) != JSON_TYPE_ARRAY)
return s;
int n; for (n = JsonGetLength(jArray) - 1; n >= 0; n--)
{
string sValue;
json jValue = JsonArrayGet(jArray, n);
int nType = JsonGetType(jValue);
if (nType == JSON_TYPE_STRING) sValue = JsonGetString(jValue);
else if (nType == JSON_TYPE_INTEGER) sValue = IntToString(JsonGetInt(jValue));
else if (nType == JSON_TYPE_FLOAT) sValue = FormatFloat(JsonGetFloat(jValue), "%!f");
else if (nType == JSON_TYPE_BOOL) sValue = JsonGetInt(jValue) == 1 ? "true" : "false";
else continue;
s = SubstituteSubStrings(s, sDesignator + IntToString(n + 1), sValue);
}
return s;
}
string RepeatString(string s, int n)
{
string sResult;
while (n-- > 0)
sResult += s;
return sResult;
}

View File

@@ -0,0 +1,908 @@
/// ----------------------------------------------------------------------------
/// @file util_i_targeting.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for managing forced targeting.
/// ----------------------------------------------------------------------------
/// @details
/*
This system is designed to take advantage of NWN:EE's ability to forcibly enter
Targeting Mode for any given PC. It is designed to add a single-use, multi-use,
or unlimited-use hook to the specified PC. Once the PC has satisfied the
conditions of the hook, or manually exited targeting mode, the targeted
objects/locations will be saved and a specified script will be run.
## Setup
1. You must attach a targeting event script to the module. For example, in your
module load script, you can add this line:
SetEventScript(GetModule(), EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET, "module_opt");
where "module_opt" is the script that will handle all forced targeting.
2. The chosen script ("module_opt") must contain reference to the
util_i_targeting function SatisfyTargetingHook(). An example of this follows.
```nwscript
#include "util_i_targeting"
void main()
{
object oPC = GetLastPlayerToSelectTarget();
if (SatisfyTargetingHook(oPC))
{
// This PC was marked as a targeter, do something here.
}
}
```
Alternately, if you want the assigned targeting hook scripts to handle
everything, you can just let the system know a targeting event happened:
```nwscript
void main()
{
object oPC = GetLastPlayerToSelectTarget();
SatisfyTargetingHook(oPC);
}
```
If oPC didn't have a targeting hook specified, nothing happens.
## Usage
The design of this system centers around a module-wide list of "Targeting Hooks"
that are accessed by util_i_targeting when a player targets any object or
manually exits targeting mode. These hooks are stored in the module's organic
sqlite database. All targeting hook information is volatile and will be reset
when the server/module is reset.
This is the prototype for the `AddTargetingHook()` function:
```nwscript
int AddTargetingHook(object oPC, string sVarName, int nObjectType = OBJECT_TYPE_ALL, string sScript = "", int nUses = 1);
```
- `oPC` is the PC object that will be associated with this hook. This PC will be
the player that will be entered into Targeting Mode. Additionally, the results
of his targeting will be saved to the PC object.
- `sVarName` is the variable name to save the results of targeting to. This
allows for targeting hooks to be added that can be saved to different
variables for several purposes.
- `nObjectType` is the limiting variable for the types of objects the PC can
target when they are in targeting mode forced by this hook. It is an optional
parameter and can be bitmasked with any visible `OBJECT_TYPE_*` constant.
- `sScript` is the resref of the script that will run once the targeting
conditions have been satisfied. For example, if you create a multi-use
targeting hook, this script will run after all uses have been exhausted. This
script will also run if the player manually exits targeting mode without
selecting a target. Optional. A script-run is not always desirable. The
targeted object may be required for later use, so a script entry is not a
requirement.
- `nUses` is the number of times this target hook can be used before it is
deleted. This is designed to allow multiple targets to be selected and saved
to the same variable name sVarName. Multi-selection could be useful for DMs in
defining DM Experience members, even from different parties, or selecting
multiple NPCs to accomplish a specific action. Optional, defaulted to 1.
Note: Targeting mode uses specified by `nUses` will be decremented every time
a player selects a target. Uses will also be decremented when a user manually
exits targeting mode. Manually exiting targeting mode will delete the
targeting hook, but any selected targets before exiting targeting mode will be
saved to the specified variable.
To add a single-use targeting hook that enters the PC into targeting mode, allows
for the selection of a single placeable | creature, then runs the script
"temp_target" upon exiting target mode or selecting a single target:
```nwscript
int nObjectType = OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_CREATURE;
AddTargetingHook(oPC, "spell_target", nObjectType, "temp_target");
```
To add a multi-use targeting hook that enters the PC into targeting mode, allows
for the selection of a specified number of placeables | creatures, then runs the
script "DM_Party" upon exiting targeting mode or selecting the specified number
of targets:
```nwscript
int nObjectType = OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_CREATURE;
AddTargetingHook(oPC, "DM_Party", nObjectType, "DM_Party", 3);
```
> Note: In this case, the player can select up to three targets to save to the
"DM_Party" variable.
To add an unlmited-use targeting hook that enters the PC into targeting mode,
allows for the selection of an unspecified number of creatures, then runs the
script "temp_target" upon exiting targeting mode or selection of an invalid
target:
```nwscript
int nObjectType = OBJECT_TYPE_CREATURE;
AddTargetingHook(oPC, "NPC_Townspeople", nObjectType, "temp_target", -1);
```
Here is an example "temp_target" post-targeting script that will access each of
the targets saved to the specified variable and send their data to the chat log:
```nwscript
#include "util_i_targeting"
void main()
{
object oPC = OBJECT_SELF;
int n, nCount = CountTargetingHookTargets(oPC, "NPC_Townspeople");
for (n = 0; n < nCount; n++)
{
object oTarget = GetTargetingHookObject(oPC, "NPC_Townspeople", n);
location lTarget = GetTargetingHookLocation(oPC, "NPC_Townspeople", n);
vector vTarget = GetTargetingHookPosition(oPC, "NPC_Townspeople", n);
}
}
```
Note: Target objects and positions saved to the variables are persistent while
the server is running, but are not persistent (though they can be made so). If
you wish to overwrite a set of target data with a variable you've already used,
ensure you first delete the current target data with the function
`DeleteTargetingHookTargets();`.
*/
#include "util_c_targeting"
#include "util_i_debug"
#include "util_i_varlists"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
// VarList names for the global targeting hook lists
const string TARGET_HOOK_ID = "TARGET_HOOK_ID";
const string TARGET_HOOK_BEHAVIOR = "TARGET_HOOK_BEHAVIOR";
// List Behaviors
const int TARGET_BEHAVIOR_ADD = 1;
const int TARGET_BEHAVIOR_DELETE = 2;
// Targeting Hook Data Structure
struct TargetingHook
{
int nHookID;
int nObjectType;
int nUses;
object oPC;
string sVarName;
string sScript;
int nValidCursor;
int nInvalidCursor;
};
struct TargetingHook TARGETING_HOOK_INVALID;
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Creates targeting hook data tables in the module's sqlite database.
/// @param bReset If TRUE, attempts to drop the tables before creation.
void CreateTargetingDataTables(int bReset = FALSE);
/// @brief Retrieve targeting hook data.
/// @param nHookID The targeting hook's ID.
/// @returns A TargetingHook containing all targeting hook data associated with
/// nHookID.
struct TargetingHook GetTargetingHookDataByHookID(int nHookID);
/// @brief Retrieve targeting hook data.
/// @param oPC The PC object associated with the targeting hook.
/// @param sVarName The varname associated with the targeting hook.
/// @returns A TargetingHook containing all targeting hook data associated with
/// nHookID.
struct TargetingHook GetTargetingHookDataByVarName(object oPC, string sVarName);
/// @brief Retrieve a list of targets.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nIndex The index of the target to retrieve from the list. If omitted,
/// the entire target list will be returned.
/// @returns A prepared sqlquery containing the target list associated with
/// oPC's sVarName.
sqlquery GetTargetList(object oPC, string sVarName, int nIndex = -1);
/// @brief Add a target to a target list.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param oTarget The target object to be added to the target list.
/// @param oArea The area object where oTarget is located.
/// @param vTarget The position of oTarget within oArea.
/// @returns The number of targets on oPC's target list sVarName after insertion.
int AddTargetToTargetList(object oPC, string sVarName, object oTarget, object oArea, vector vTarget);
/// @brief Delete oPC's sVarName target list.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
void DeleteTargetList(object oPC, string sVarName);
/// @brief Delete a targeting hook and all associated targeting hook data.
/// @param nHookID The targeting hook's ID.
void DeleteTargetingHook(int nHookID);
/// @brief Force the PC object associated with targeting hook nHookID to enter
/// targeting mode using properties set by AddTargetingHook().
/// @param nHookID The targeting hook's ID.
/// @param nBehavior The behavior desired from the targeting session. Must be
/// a TARGET_BEHAVIOR_* constant.
void EnterTargetingModeByHookID(int nHookID, int nBehavior = TARGET_BEHAVIOR_ADD);
/// @brief Force the PC object associated with targeting hook nHookID to enter
/// targeting mode using properties set by AddTargetingHook().
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nBehavior The behavior desired from the targeting session. Must be
/// a TARGET_BEHAVIOR_* constant.
void EnterTargetingModeByVarName(object oPC, string sVarName, int nBehavior = TARGET_BEHAVIOR_ADD);
/// @brief Retrieve a targeting hook id.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @returns The targeting hook id assocaited with oPC's sVarName target list.
int GetTargetingHookID(object oPC, string sVarName);
/// @brief Retrieve a targeting hook's sVarName.
/// @param nHookID The targeting hook's ID.
/// @returns The target list name sVarName associated with nHookID.
string GetTargetingHookVarName(int nHookID);
/// @brief Retrieve a targeting hook's allowed object types.
/// @param nHookID The targeting hook's ID.
/// @returns A bitmap containing the allowed target types associated with
/// nHookID.
int GetTargetingHookObjectType(int nHookID);
/// @brief Retrieve a targeting hook's remaining uses.
/// @param nHookID The targeting hook's ID.
/// @returns The number of uses remaining for targeting hook nHookID.
int GetTargetingHookUses(int nHookID);
/// @brief Retrieve a targeting hook's script.
/// @param nHookID The targeting hook's ID.
/// @returns The script associated with targeting hook nHookID.
string GetTargetingHookScript(int nHookID);
/// @brief Add a targeting hook to the global targeting hook list and save
/// targeting hook parameters for later use.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nObjectType A bitmasked value containing all object types allowed
/// to be targeted by this hook.
/// @param sScript The script that will be run when this target hook is
/// satisfied.
/// @param nUses The number of times this targeting hook is allowed to be used
/// before it is automatically deleted. Omitting this value will yield a
/// single use hook. Use -1 for an infinite-use hook.
/// @param nValidCursor A MOUSECURSOR_* cursor indicating a valid target.
/// @param nInvalidCursor A MOUSECURSOR_* cursor indicating an invalid target.
/// @returns A unique ID associated with the new targeting hook.
int AddTargetingHook(object oPC, string sVarName, int nObjectType = OBJECT_TYPE_ALL, string sScript = "",
int nUses = 1, int nValidCursor = MOUSECURSOR_MAGIC, int nInvalidCursor = MOUSECURSOR_NOMAGIC);
/// @brief Save target data to the PC object as an object and location variable
/// defined by sVarName in AddTargetingHook(). Decrements remaining targeting
/// hook uses and, if required, deletes the targeting hook.
/// @param oPC The PC object associated with the target list.
/// @returns TRUE if OpC has a current targeting hook, FALSE otherwise.
int SatisfyTargetingHook(object oPC);
/// @brief Retrieve a targeting list's object at index nIndex.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nIndex The index at which to retrieve the target object.
/// @returns The targeting's lists target at index nIndex, or the first
/// target on the list if nIndex is omitted.
object GetTargetingHookObject(object oPC, string sVarName, int nIndex = 1);
/// @brief Retrieve a targeting list's location at index nIndex.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nIndex The index at which to retrieve the target location.
/// @returns The targeting's lists location at index nIndex, or the first
/// location on the list if nIndex is omitted.
location GetTargetingHookLocation(object oPC, string sVarName, int nIndex = 1);
/// @brief Retrieve a targeting list's position at index nIndex.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nIndex The index at which to retrieve the target position.
/// @returns The targeting's lists position at index nIndex, or the first
/// position on the list if nIndex is omitted.
vector GetTargetingHookPosition(object oPC, string sVarName, int nIndex = 1);
/// @brief Determine how many targets are on a target list.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @returns The number of targets associated with the saved as sVarName
/// on oPC.
// ---< CountTargetingHookTargets >---
int CountTargetingHookTargets(object oPC, string sVarName);
/// @brief Delete a targeting hook target.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nIndex The index at which to delete the target data. If omitted,
/// the first target on the list will be deleted.
/// @returns The number of targets remaining on oPC's sVarName target list
/// after deletion.
int DeleteTargetingHookTarget(object oPC, string sVarName, int nIndex = 1);
/// @brief Retrieve the target list object's internal index.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param oObject The object to find on oPC's sVarName target list.
int GetTargetingHookIndex(object oPC, string sVarName, object oTarget);
/// @brief Delete target list target data by internal index.
/// @param oPC The PC object associated with the target list.
/// @param sVarName The VarName associated with the target list.
/// @param nIndex The internal index of the target data to be deleted. This
/// index can be retrieved from GetTargetingHookIndex().
/// @returns The number of targets remaining on oPC's sVarName target list
/// after deletion.
int DeleteTargetingHookTargetByIndex(object oPC, string sVarName, int nIndex);
// -----------------------------------------------------------------------------
// Private Function Definitions
// -----------------------------------------------------------------------------
sqlquery _PrepareTargetingQuery(string s)
{
return SqlPrepareQueryObject(GetModule(), s);
}
string _GetTargetingHookFieldData(int nHookID, string sField)
{
string s = "SELECT " + sField + " " +
"FROM targeting_hooks " +
"WHERE nHookID = @nHookID;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindInt(q, "@nHookID", nHookID);
return SqlStep(q) ? SqlGetString(q, 0) : "";
}
int _GetLastTargetingHookID()
{
string s = "SELECT seq FROM sqlite_sequence WHERE name = @name;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@name", "targeting_hooks");
return SqlStep(q) ? SqlGetInt(q, 0) : 0;
}
string _GetTargetData(object oPC, string sVarName, string sField, int nIndex = 1)
{
string s = "SELECT " + sField + " " +
"FROM targeting_targets " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName " +
"LIMIT 1 OFFSET " + IntToString(nIndex) + ";";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
return SqlStep(q) ? SqlGetString(q, 0) : "";
}
void _EnterTargetingMode(struct TargetingHook th, int nBehavior)
{
SetLocalInt(th.oPC, TARGET_HOOK_ID, th.nHookID);
SetLocalInt(th.oPC, TARGET_HOOK_BEHAVIOR, nBehavior);
EnterTargetingMode(th.oPC, th.nObjectType, th.nValidCursor, th.nInvalidCursor);
}
void _DeleteTargetingHookData(int nHookID)
{
string s = "DELETE FROM targeting_hooks " +
"WHERE nHookID = @nHookID;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindInt(q, "@nHookID", nHookID);
SqlStep(q);
}
void _ExitTargetingMode(int nHookID)
{
struct TargetingHook th = GetTargetingHookDataByHookID(nHookID);
if (th.sScript != "")
{
Debug("Running post-targeting script " + th.sScript + " from Targeting Hook ID " +
IntToString(nHookID) + " on " + GetName(th.oPC) + " with varname " + th.sVarName);
RunTargetingHookScript(th.sScript, th.oPC);
}
else
Debug("No post-targeting script specified for Targeting Hook ID " + IntToString(nHookID) + " " +
"on " + GetName(th.oPC) + " with varname " + th.sVarName);
DeleteTargetingHook(nHookID);
DeleteLocalInt(th.oPC, TARGET_HOOK_ID);
DeleteLocalInt(th.oPC, TARGET_HOOK_BEHAVIOR);
}
// Reduces the number of targeting hooks remaining. When the remaining number is
// 0, the hook is automatically deleted.
int _DecrementTargetingHookUses(struct TargetingHook th, int nBehavior)
{
int nUses = GetTargetingHookUses(th.nHookID);
if (--nUses == 0)
{
if (IsDebugging(DEBUG_LEVEL_DEBUG))
Debug("Decrementing target hook uses for ID " + HexColorString(IntToString(th.nHookID), COLOR_CYAN) +
"\n Uses remaining -> " + (nUses ? HexColorString(IntToString(nUses), COLOR_CYAN) : HexColorString(IntToString(nUses), COLOR_RED_LIGHT)) + "\n");
_ExitTargetingMode(th.nHookID);
}
else
{
string s = "UPDATE targeting_hooks " +
"SET nUses = nUses - 1 " +
"WHERE nHookID = @nHookID;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindInt(q, "@nHookID", th.nHookID);
SqlStep(q);
_EnterTargetingMode(th, nBehavior);
}
return nUses;
}
// -----------------------------------------------------------------------------
// Public Function Definitions
// -----------------------------------------------------------------------------
// Temporary function for feedback purposes only
string ObjectTypeToString(int nObjectType)
{
string sResult;
if (nObjectType & OBJECT_TYPE_CREATURE)
sResult += (sResult == "" ? "" : ", ") + "Creatures";
if (nObjectType & OBJECT_TYPE_ITEM)
sResult += (sResult == "" ? "" : ", ") + "Items";
if (nObjectType & OBJECT_TYPE_TRIGGER)
sResult += (sResult == "" ? "" : ", ") + "Triggers";
if (nObjectType & OBJECT_TYPE_DOOR)
sResult += (sResult == "" ? "" : ", ") + "Doors";
if (nObjectType & OBJECT_TYPE_AREA_OF_EFFECT)
sResult += (sResult == "" ? "" : ", ") + "Areas of Effect";
if (nObjectType & OBJECT_TYPE_WAYPOINT)
sResult += (sResult == "" ? "" : ", ") + "Waypoints";
if (nObjectType & OBJECT_TYPE_PLACEABLE)
sResult += (sResult == "" ? "" : ", ") + "Placeables";
if (nObjectType & OBJECT_TYPE_STORE)
sResult += (sResult == "" ? "" : ", ") + "Stores";
if (nObjectType & OBJECT_TYPE_ENCOUNTER)
sResult += (sResult == "" ? "" : ", ") + "Encounters";
if (nObjectType & OBJECT_TYPE_TILE)
sResult += (sResult == "" ? "" : ", ") + "Tiles";
return sResult;
}
void CreateTargetingDataTables(int bReset = FALSE)
{
object oModule = GetModule();
if (bReset)
{
string sDropHooks = "DROP TABLE IF EXISTS targeting_hooks;";
string sDropTargets = "DROP TABLE IF EXISTS targeting_targets;";
sqlquery q;
q = _PrepareTargetingQuery(sDropHooks); SqlStep(q);
q = _PrepareTargetingQuery(sDropTargets); SqlStep(q);
DeleteLocalInt(oModule, "TARGETING_INITIALIZED");
Warning(HexColorString("Targeting database tables have been dropped", COLOR_RED_LIGHT));
}
if (GetLocalInt(oModule, "TARGETING_INITIALIZED"))
return;
string sData = "CREATE TABLE IF NOT EXISTS targeting_hooks (" +
"nHookID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"sUUID TEXT, " +
"sVarName TEXT, " +
"nObjectType INTEGER, " +
"nUses INTEGER default '1', " +
"sScript TEXT, " +
"nValidCursor INTEGER, " +
"nInvalidCursor INTEGER, " +
"UNIQUE (sUUID, sVarName));";
string sTargets = "CREATE TABLE IF NOT EXISTS targeting_targets (" +
"nTargetID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"sUUID TEXT, " +
"sVarName TEXT, " +
"sTargetObject TEXT, " +
"sTargetArea TEXT, " +
"vTargetLocation TEXT);";
sqlquery q;
q = _PrepareTargetingQuery(sData); SqlStep(q);
q = _PrepareTargetingQuery(sTargets); SqlStep(q);
Debug(HexColorString("Targeting database tables have been created", COLOR_GREEN_LIGHT));
SetLocalInt(oModule, "TARGETING_INITIALIZED", TRUE);
}
struct TargetingHook GetTargetingHookDataByHookID(int nHookID)
{
string s = "SELECT sUUID, sVarName, nObjectType, nUses, sScript, nValidCursor, nInvalidCursor " +
"FROM targeting_hooks " +
"WHERE nHookID = @nHookID;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindInt(q, "@nHookID", nHookID);
struct TargetingHook th;
if (SqlStep(q))
{
th.nHookID = nHookID;
th.oPC = GetObjectByUUID(SqlGetString(q, 0));
th.sVarName = SqlGetString(q, 1);
th.nObjectType = SqlGetInt(q, 2);
th.nUses = SqlGetInt(q, 3);
th.sScript = SqlGetString(q, 4);
th.nValidCursor = SqlGetInt(q, 5);
th.nInvalidCursor = SqlGetInt(q, 6);
}
else
Warning("Targeting data for target hook " + IntToString(nHookID) + " not found");
return th;
}
struct TargetingHook GetTargetingHookDataByVarName(object oPC, string sVarName)
{
int nHookID = GetTargetingHookID(oPC, sVarName);
return GetTargetingHookDataByHookID(nHookID);
}
sqlquery GetTargetList(object oPC, string sVarName, int nIndex = -1)
{
string s = "SELECT sTargetObject, sTargetArea, vTargetLocation " +
"FROM targeting_targets " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName" +
(nIndex == -1 ? ";" : "LIMIT 1 OFFSET " + IntToString(nIndex)) + ";";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
return q;
}
int AddTargetToTargetList(object oPC, string sVarName, object oTarget, object oArea, vector vTarget)
{
string s = "INSERT INTO targeting_targets (sUUID, sVarName, sTargetObject, sTargetArea, vTargetLocation) " +
"VALUES (@sUUID, @sVarName, @sTargetObject, @sTargetArea, @vTargetLocation);";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
SqlBindString(q, "@sTargetObject", ObjectToString(oTarget));
SqlBindString(q, "@sTargetArea", ObjectToString(oArea));
SqlBindVector(q, "@vTargetLocation", vTarget);
SqlStep(q);
return CountTargetingHookTargets(oPC, sVarName);
}
void DeleteTargetList(object oPC, string sVarName)
{
string s = "DELETE FROM targeting_targets " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
SqlStep(q);
}
void EnterTargetingModeByHookID(int nHookID, int nBehavior = TARGET_BEHAVIOR_ADD)
{
struct TargetingHook th = GetTargetingHookDataByHookID(nHookID);
if (th == TARGETING_HOOK_INVALID)
{
Warning("EnterTargetingModeByHookID::Unable to retrieve valid targeting data for " +
"targeting hook " + IntToString(nHookID));
return;
}
if (GetIsObjectValid(th.oPC))
_EnterTargetingMode(th, nBehavior);
}
void EnterTargetingModeByVarName(object oPC, string sVarName, int nBehavior = TARGET_BEHAVIOR_ADD)
{
struct TargetingHook th = GetTargetingHookDataByVarName(oPC, sVarName);
if (th == TARGETING_HOOK_INVALID)
{
Warning("EnterTargetingModeByVarName::Unable to retrieve valid targeting data for " +
"targeting hook " + sVarName + " on " + GetName(oPC));
return;
}
if (GetIsObjectValid(th.oPC))
_EnterTargetingMode(th, nBehavior);
}
int GetTargetingHookID(object oPC, string sVarName)
{
string s = "SELECT nHookID " +
"FROM targeting_hooks " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
return SqlStep(q) ? SqlGetInt(q, 0) : 0;
}
string GetTargetingHookVarName(int nHookID)
{
return _GetTargetingHookFieldData(nHookID, "sVarName");
}
int GetTargetingHookObjectType(int nHookID)
{
return StringToInt(_GetTargetingHookFieldData(nHookID, "nObjectType"));
}
int GetTargetingHookUses(int nHookID)
{
return StringToInt(_GetTargetingHookFieldData(nHookID, "nUses"));
}
string GetTargetingHookScript(int nHookID)
{
return _GetTargetingHookFieldData(nHookID, "sScript");
}
int AddTargetingHook(object oPC, string sVarName, int nObjectType = OBJECT_TYPE_ALL, string sScript = "",
int nUses = 1, int nValidCursor = MOUSECURSOR_MAGIC, int nInvalidCursor = MOUSECURSOR_NOMAGIC)
{
CreateTargetingDataTables();
string s = "REPLACE INTO targeting_hooks (sUUID, sVarName, nObjectType, nUses, sScript, nValidCursor, nInvalidCursor) " +
"VALUES (@sUUID, @sVarName, @nObjectType, @nUses, @sScript, @nValidCursor, @nInvalidCursor);";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
SqlBindInt (q, "@nObjectType", nObjectType);
SqlBindInt (q, "@nUses", nUses);
SqlBindString(q, "@sScript", sScript);
SqlBindInt (q, "@nValidCursor", nValidCursor);
SqlBindInt (q, "@nInvalidCursor", nInvalidCursor);
SqlStep(q);
if (IsDebugging(DEBUG_LEVEL_DEBUG))
{
Debug("Adding targeting hook ID " + HexColorString(IntToString(_GetLastTargetingHookID()), COLOR_CYAN) +
"\n sVarName -> " + HexColorString(sVarName, COLOR_CYAN) +
"\n nObjectType -> " + HexColorString(ObjectTypeToString(nObjectType), COLOR_CYAN) +
"\n sScript -> " + (sScript == "" ? HexColorString("[None]", COLOR_RED_LIGHT) :
HexColorString(sScript, COLOR_CYAN)) +
"\n nUses -> " + (nUses == -1 ? HexColorString("Unlimited", COLOR_CYAN) :
(nUses > 0 ? HexColorString(IntToString(nUses), COLOR_CYAN) :
HexColorString(IntToString(nUses), COLOR_RED_LIGHT))) +
"\n nValidCursor -> " + IntToString(nValidCursor) +
"\n nInvalidCursor -> " + IntToString(nInvalidCursor) + "\n");
}
return _GetLastTargetingHookID();
}
void DeleteTargetingHook(int nHookID)
{
if (IsDebugging(DEBUG_LEVEL_DEBUG))
Debug("Deleting targeting hook ID " + HexColorString(IntToString(nHookID), COLOR_CYAN) + "\n");
_DeleteTargetingHookData(nHookID);
}
int SatisfyTargetingHook(object oPC)
{
int nHookID = GetLocalInt(oPC, TARGET_HOOK_ID);
if (nHookID == 0)
return FALSE;
int nBehavior = GetLocalInt(oPC, TARGET_HOOK_BEHAVIOR);
struct TargetingHook th = GetTargetingHookDataByHookID(nHookID);
if (th == TARGETING_HOOK_INVALID)
{
Warning("SatisfyTargetingHook::Unable to retrieve valid targeting data for " +
"targeting hook " + IntToString(nHookID));
return FALSE;
}
string sVarName = th.sVarName;
object oTarget = GetTargetingModeSelectedObject();
vector vTarget = GetTargetingModeSelectedPosition();
int bValid = TRUE;
if (IsDebugging(DEBUG_LEVEL_DEBUG))
{
Debug("Targeted Object -> " + (GetIsObjectValid(oTarget) ? (GetIsPC(oTarget) ? HexColorString(GetName(oTarget), COLOR_GREEN_LIGHT) : HexColorString(GetTag(oTarget), COLOR_CYAN)) : HexColorString("OBJECT_INVALID", COLOR_RED_LIGHT)) +
"\n Type -> " + HexColorString(ObjectTypeToString(GetObjectType(oTarget)), COLOR_CYAN));
Debug("Targeted Position -> " + (vTarget == Vector() ? HexColorString("POSITION_INVALID", COLOR_RED_LIGHT) :
HexColorString("(" + FloatToString(vTarget.x, 3, 1) + ", " +
FloatToString(vTarget.y, 3, 1) + ", " +
FloatToString(vTarget.z, 3, 1) + ")", COLOR_CYAN)) + "\n");
}
if (GetIsObjectValid(oTarget))
{
if (nBehavior == TARGET_BEHAVIOR_ADD)
{
if (IsDebugging(DEBUG_LEVEL_DEBUG))
{
object oArea = GetArea(oTarget);
Debug(HexColorString("Saving targeted object and position to list [" + th.sVarName + "]:", COLOR_CYAN) +
"\n Tag -> " + HexColorString(GetTag(oTarget), COLOR_CYAN) +
"\n Location -> " + HexColorString(JsonDump(LocationToJson(Location(oArea, vTarget, 0.0))), COLOR_CYAN) +
"\n Area -> " + HexColorString((GetIsObjectValid(oArea) ? GetTag(oArea) : "AREA_INVALID"), COLOR_CYAN) + "\n");
}
AddTargetToTargetList(oPC, sVarName, oTarget, GetArea(oPC), vTarget);
}
else if (nBehavior == TARGET_BEHAVIOR_DELETE)
{
if (GetArea(oTarget) == oTarget)
Warning("Location/Tile targets cannot be deleted; select a game object");
else
{
Debug(HexColorString("Attempting to delete targeted object and position from list [" + th.sVarName + "]:", COLOR_CYAN));
int nIndex = GetTargetingHookIndex(oPC, sVarName, oTarget);
if (nIndex == 0 && IsDebugging(DEBUG_LEVEL_DEBUG))
Debug(" > " + HexColorString("Target " + (GetIsPC(oTarget) ? GetName(oTarget) : GetTag(oTarget)) + " not found " +
"on list [" + th.sVarName + "]; removal aborted", COLOR_RED_LIGHT));
else
{
DeleteTargetingHookTargetByIndex(oPC, sVarName, nIndex);
if (IsDebugging(DEBUG_LEVEL_DEBUG))
Debug(" > " + HexColorString("Target " + (GetIsPC(oTarget) ? GetName(oTarget) : GetTag(oTarget)) + " removed from " +
"list [" + th.sVarName + "]", COLOR_GREEN_LIGHT));
}
}
}
}
else
bValid = FALSE;
if (!bValid)
_ExitTargetingMode(nHookID);
else
{
if (th.nUses == -1)
_EnterTargetingMode(th, nBehavior);
else
_DecrementTargetingHookUses(th, nBehavior);
}
return TRUE;
}
int DeleteTargetingHookTargetByIndex(object oPC, string sVarName, int nIndex)
{
string s = "DELETE FROM targeting_targets " +
"WHERE nTargetID = @nTargetID;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindInt(q, "@nTargetID", nIndex);
SqlStep(q);
return CountTargetingHookTargets(oPC, sVarName);
}
int GetTargetingHookIndex(object oPC, string sVarName, object oTarget)
{
string s = "SELECT nTargetID " +
"FROM targeting_targets " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName " +
"AND sTargetObject = @sTargetObject;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
SqlBindString(q, "@sTargetObject", ObjectToString(oTarget));
return SqlStep(q) ? SqlGetInt(q, 0) : 0;
}
object GetTargetingHookObject(object oPC, string sVarName, int nIndex = 1)
{
return StringToObject(_GetTargetData(oPC, sVarName, "sTargetObject", nIndex));
}
location GetTargetingHookLocation(object oPC, string sVarName, int nIndex = 1)
{
sqlquery q = GetTargetList(oPC, sVarName, 1);
if (SqlStep(q))
{
object oArea = StringToObject(SqlGetString(q, 1));
vector vTarget = SqlGetVector(q, 2);
return Location(oArea, vTarget, 0.0);
}
return Location(OBJECT_INVALID, Vector(), 0.0);
}
vector GetTargetingHookPosition(object oPC, string sVarName, int nIndex = 1)
{
sqlquery q = GetTargetList(oPC, sVarName, 1);
if (SqlStep(q))
return SqlGetVector(q, 2);
return Vector();
}
int CountTargetingHookTargets(object oPC, string sVarName)
{
string s = "SELECT COUNT (nTargetID) " +
"FROM targeting_targets " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName;";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
return SqlStep(q) ? SqlGetInt(q, 0) : 0;
}
int DeleteTargetingHookTarget(object oPC, string sVarName, int nIndex = 1)
{
string s = "DELETE FROM targeting_targets " +
"WHERE sUUID = @sUUID " +
"AND sVarName = @sVarName " +
"LIMIT 1 OFFSET " + IntToString(nIndex) + ";";
sqlquery q = _PrepareTargetingQuery(s);
SqlBindString(q, "@sUUID", GetObjectUUID(oPC));
SqlBindString(q, "@sVarName", sVarName);
SqlStep(q);
return CountTargetingHookTargets(oPC, sVarName);
}

View File

@@ -0,0 +1,475 @@
/// ----------------------------------------------------------------------------
/// @file util_i_timers.nss
/// @author Michael A. Sinclair (Squatting Monk) <squattingmonk@gmail.com>
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for running scripts on an interval.
/// ----------------------------------------------------------------------------
/// @details
/// ## Concept
/// Timers are a way of running a script repeatedly on an interval. A timer can
/// be created on an object. Once started, it will continue to run until it is
/// finished iterating or until killed manually. Each time the timer elapses,
/// its action will run. By default, this action is to simply run a script.
///
/// ## Basic Usage
///
/// ### Creating a Timer
/// You can create a timer using `CreateTimer()`. This function takes the object
/// that should run the timer, the script that should execute when the timer
/// elapses, the interval between ticks, and the total number of iterations. It
/// returns the ID for the timer, which is used to reference it in the database.
/// You should save this timer for later use.
///
/// ```nwscript
/// // The following creates a timer on oPC that will run the script "foo" every
/// // 6 seconds for 4 iterations.
/// int nTimerID = CreateTimer(oPC, "foo", 6.0, 4);
/// ```
///
/// A timer created with 0 iterations will run until stopped or killed.
///
/// ## Starting a Timer
/// Timers will not run until they are started wiuth `StartTimer()`. This
/// function takes the ID of the timer returned from `CreateTimer()`. If the
/// second parameter, `bInstant`, is TRUE, the timer will elapse immediately;
/// otherwise, it will elapse when its interval is complete:
///
/// ```nwscript
/// StartTimer(nTimerID);
/// ```
///
/// ### Stopping a Timer
/// Stopping a timer with `StopTimer()` will suspend its execution:
/// ```nwscript
/// StopTimer(nTimerID);
/// ```
/// You can restart the timer later using `StartTimer()` to resume any remaining
/// iterations. If you want to start again from the beginning, you can call
/// `ResetTimer()` first:
/// ```nwscript
/// ResetTimer(nTimerID);
/// StartTimer(nTimerID);
/// ```
///
/// ### Destroying a Timer
/// Calling `KillTimer()` will clean up all data associated with the timer. A
/// timer cannot be restarted after it is killed; you will have to create and
/// start a new one.
/// ```nwscript
/// KillTimer(nTimerID);
/// ```
///
/// Timers automatically kill themselves when they are finished iterating or
/// when the object they are executed on is no longer valid. You only need to
/// use `KillTimer()` if you want to destroy it before it is done iterating or
/// if the timer is infinite.
///
/// ## Advanced Usage
/// By default, timer actions are handled by passing them to `ExecuteScript()`.
/// However, the final parameter of the `CreateTimer()` function allows you to
/// specify a handler script. If this parameter is not blank, the handler will
/// be called using `ExecuteScript()` and the action will be available to it as
/// a script parameter.
///
/// For example, the Core Framework allows timers to run event hooks by calling
/// the handler script `core_e_timerhook`, which is as follows:
/// ```nwscript
/// #include "core_i_framework"
///
/// void main()
/// {
/// string sEvent = GetScriptParam(TIMER_ACTION);
/// string sSource = GetScriptParam(TIMER_SOURCE);
/// object oSource = StringToObject(sSource);
/// RunEvent(sEvent, oSource);
/// }
/// ```
///
/// To make this easier, `core_i_framework` contains an alias to `CreateTimer()`
/// called `CreateEventTimer()` that sets the handler script. You can create
/// your own aliases in the same way.
#include "util_i_sqlite"
#include "util_i_debug"
#include "util_i_datapoint"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
const string TIMER_DATAPOINT = "*Timers";
const string TIMER_INIT = "*TimersInitialized";
const string TIMER_LAST = "*TimerID";
const string TIMER_ACTION = "*TimerAction";
const string TIMER_SOURCE = "*TimerSource";
// -----------------------------------------------------------------------------
// Global Variables
// -----------------------------------------------------------------------------
// Running timers are AssignCommand()'ed to this datapoint. This ensures that
// even if the object that issued the StartTimer() becomes invalid, the timer
// will continue to run.
object TIMERS = GetDatapoint(TIMER_DATAPOINT, GetModule(), FALSE);
// -----------------------------------------------------------------------------
// Public Function Declarations
// -----------------------------------------------------------------------------
/// @brief Create a table for timers in the module's volatile database.
/// @param bReset If TRUE, will drop the existing timers table.
/// @note This function will be run automatically the first timer one of the
/// functions in this file is called. You only need to call this if you need
/// the table created earlier (e.g., because another table references it).
void CreateTimersTable(int bReset = FALSE);
/// @brief Create a timer that fires on a target at regular intervals.
/// @details After a timer is created, you will need to start it to get it to
/// run. You cannot create a timer on an invalid target or with a
/// non-positive interval value.
/// @param oTarget The object the action will run on.
/// @param sAction The action to execute when the timer elapses.
/// @param fInterval The number of seconds between iterations.
/// @param nIterations the number of times the timer can elapse. 0 means no
/// limit. If nIterations is 0, fInterval must be greater than or equal to
/// 6.0.
/// @param fJitter A random number of seconds between 0.0 and fJitter to add to
/// fInterval between executions. Leave at 0.0 for no jitter.
/// @param sHandler A handler script to execute sAction. If "", sAction will be
/// called using ExecuteScript() instead.
/// @returns the ID of the timer. Save this so it can be used to start, stop, or
/// kill the timer later.
int CreateTimer(object oTarget, string sAction, float fInterval, int nIterations = 0, float fJitter = 0.0, string sHandler = "");
/// @brief Return if a timer exists.
/// @param nTimerID The ID of the timer in the database.
int GetIsTimerValid(int nTimerID);
/// @brief Start a timer, executing its action each interval until finished
/// iterating, stopped, or killed.
/// @param nTimerID The ID of the timer in the database.
/// @param bInstant If TRUE, execute the timer's action immediately.
void StartTimer(int nTimerID, int bInstant = TRUE);
/// @brief Suspend execution of a timer.
/// @param nTimerID The ID of the timer in the database.
/// @note This does not destroy the timer, only stops it from iterating or
/// executing its action.
void StopTimer(int nTimerID);
/// @brief Reset the number or remaining iterations on a timer.
/// @param nTimerID The ID of the timer in the database.
void ResetTimer(int nTimerID);
/// @brief Delete a timer.
/// @details This results in all information about the given timer being
/// deleted. Since the information is gone, the action associated with that
/// timer ID will not get executed again.
/// @param nTimerID The ID of the timer in the database.
void KillTimer(int nTimerID);
/// @brief Return whether a timer will run infinitely.
/// @param nTimerID The ID of the timer in the database.
int GetIsTimerInfinite(int nTimerID);
/// @brief Return the remaining number of iterations for a timer.
/// @details If called during a timer script, will not include the current
/// iteration. Returns -1 if nTimerID is not a valid timer ID. Returns 0 if
/// the timer is set to run indefinitely, so be sure to check for this with
/// GetIsTimerInfinite().
/// @param nTimerID The ID of the timer in the database.
int GetTimerRemaining(int nTimerID);
/// @brief Sets the remaining number of iterations for a timer.
/// @param nTimerID The ID of the timer in the database.
/// @param nRemaining The remaining number of iterations.
void SetTimerRemaining(int nTimerID, int nRemaining);
// -----------------------------------------------------------------------------
// Private Function Implementations
// -----------------------------------------------------------------------------
// Private function used by StartTimer().
void _TimerElapsed(int nTimerID, int nRunID, int bFirstRun = FALSE)
{
// Timers are fired on a delay, so it's possible that the timer was stopped
// and restarted before the delayed call could fail due to the timer being
// stopped. We increment the run_id whenever the timer is started and pass
// it along to the delayed calls so they can check if they are still valid.
sqlquery q = SqlPrepareQueryModule("SELECT * FROM timers " +
"WHERE timer_id = @timer_id AND run_id = @run_id AND running = 1;");
SqlBindInt(q, "@timer_id", nTimerID);
SqlBindInt(q, "@run_id", nRunID);
// The timer was killed or stopped
if (!SqlStep(q))
return;
string sTimerID = IntToString(nTimerID);
string sAction = SqlGetString(q, 3);
string sHandler = SqlGetString(q, 4);
string sTarget = SqlGetString(q, 5);
string sSource = SqlGetString(q, 6);
float fInterval = SqlGetFloat (q, 7);
float fJitter = SqlGetFloat (q, 8);
int nIterations = SqlGetInt (q, 9);
int nRemaining = SqlGetInt (q, 10);
int bIsPC = SqlGetInt (q, 11);
object oTarget = StringToObject(sTarget);
object oSource = StringToObject(sSource);
string sMsg =
"\n Target: " + sTarget +
" (" + (GetIsObjectValid(oTarget) ? GetName(oTarget) : "INVALID") + ")" +
"\n Source: " + sSource +
" (" + (GetIsObjectValid(oTarget) ? GetName(oSource) : "INVALID") + ")" +
"\n Action: " + sAction +
"\n Handler: " + sHandler;
if (!GetIsObjectValid(oTarget) || (bIsPC && !GetIsPC(oTarget)))
{
Warning("Target for timer " + sTimerID + " no longer valid:" + sMsg);
KillTimer(nTimerID);
return;
}
// If we're running infinitely or we have more runs remaining...
if (!nIterations || nRemaining)
{
string sIterations = (nIterations ? IntToString(nIterations) : "Infinite");
if (!bFirstRun)
{
Notice("Timer " + sTimerID + " elapsed" + sMsg +
"\n Iteration: " +
(nIterations ? IntToString(nIterations - nRemaining + 1) : "INFINITE") +
"/" + sIterations);
// If we're not running an infinite number of times, decrement the
// number of iterations we have remaining
if (nIterations)
SetTimerRemaining(nTimerID, nRemaining - 1);
// Run the timer handler
SetScriptParam(TIMER_LAST, IntToString(nTimerID));
SetScriptParam(TIMER_ACTION, sAction);
SetScriptParam(TIMER_SOURCE, sSource);
ExecuteScript(sHandler != "" ? sHandler : sAction, oTarget);
// In case one of those scripts we just called reset the timer...
if (nIterations)
nRemaining = GetTimerRemaining(nTimerID);
}
// If we have runs left, call our timer's next iteration.
if (!nIterations || nRemaining)
{
// Account for any jitter
fJitter = IntToFloat(Random(FloatToInt(fJitter * 10) + 1)) / 10.0;
fInterval += fJitter;
Notice("Scheduling next iteration for timer " + sTimerID + ":" + sMsg +
"\n Delay: " + FloatToString(fInterval, 0, 1) +
"\n Remaining: " +
(nIterations ? (IntToString(nRemaining)) : "INFINITE") +
"/" + sIterations);
DelayCommand(fInterval, _TimerElapsed(nTimerID, nRunID));
return;
}
}
// We have no more runs left! Kill the timer to clean up.
Debug("Timer " + sTimerID + " expired:" + sMsg);
KillTimer(nTimerID);
}
// -----------------------------------------------------------------------------
// Public Function Implementations
// -----------------------------------------------------------------------------
void CreateTimersTable(int bReset = FALSE)
{
if (GetLocalInt(TIMERS, TIMER_INIT) && !bReset)
return;
// StartTimer() assigns the timer tick to TIMERS, so by deleting it, we are
// able to cancel all currently running timers.
DestroyObject(TIMERS);
SqlCreateTableModule("timers",
"timer_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"run_id INTEGER NOT NULL DEFAULT 0, " +
"running BOOLEAN NOT NULL DEFAULT 0, " +
"action TEXT NOT NULL, " +
"handler TEXT NOT NULL, " +
"target TEXT NOT NULL, " +
"source TEXT NOT NULL, " +
"interval REAL NOT NULL, " +
"jitter REAL NOT NULL, " +
"iterations INTEGER NOT NULL, " +
"remaining INTEGER NOT NULL, " +
"is_pc BOOLEAN NOT NULL DEFAULT 0", bReset);
TIMERS = CreateDatapoint(TIMER_DATAPOINT);
SetDebugPrefix(HexColorString("[Timers]", COLOR_CYAN), TIMERS);
SetLocalInt(TIMERS, TIMER_INIT, TRUE);
}
int CreateTimer(object oTarget, string sAction, float fInterval, int nIterations = 0, float fJitter = 0.0, string sHandler = "")
{
string sSource = ObjectToString(OBJECT_SELF);
string sTarget = ObjectToString(oTarget);
string sDebug =
"\n OBJECT_SELF: " + sSource + " (" + GetName(OBJECT_SELF) + ")" +
"\n oTarget: " + sTarget +
" (" + (GetIsObjectValid(oTarget) ? GetName(oTarget) : "INVALID") + ")" +
"\n sAction: " + sAction +
"\n sHandler: " + sHandler +
"\n nIterations: " + (nIterations ? IntToString(nIterations) : "Infinite") +
"\n fInterval: " + FloatToString(fInterval, 0, 1) +
"\n fJitter: " + FloatToString(fJitter, 0, 1);
// Sanity checks: don't create the timer if...
// 1. the target is invalid
// 2. the interval is not greater than 0.0
// 3. the number of iterations is non-positive
// 4. the interval is more than once per round and the timer is infinite
string sError;
if (!GetIsObjectValid(oTarget))
sError = "oTarget is invalid";
else if (fInterval <= 0.0)
sError = "fInterval must be positive";
else if (fInterval + fJitter <= 0.0)
sError = "fJitter is too low for fInterval";
else if (nIterations < 0)
sError = "nIterations is negative";
else if (fInterval < 6.0 && !nIterations)
sError = "fInterval is too short for infinite executions";
if (sError != "")
{
CriticalError("CreateTimer() failed:\n Error: " + sError + sDebug);
return 0;
}
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule("INSERT INTO timers " +
"(action, handler, target, source, interval, jitter, iterations, remaining, is_pc) " +
"VALUES (@action, @handler, @target, @source, @interval, @jitter, @iterations, @remaining, @is_pc) " +
"RETURNING timer_id;");
SqlBindString(q, "@action", sAction);
SqlBindString(q, "@handler", sHandler);
SqlBindString(q, "@target", sTarget);
SqlBindString(q, "@source", sSource);
SqlBindFloat (q, "@interval", fInterval);
SqlBindFloat (q, "@jitter", fJitter);
SqlBindInt (q, "@iterations", nIterations);
SqlBindInt (q, "@remaining", nIterations);
SqlBindInt (q, "@is_pc", GetIsPC(oTarget));
int nTimerID = SqlStep(q) ? SqlGetInt(q, 0) : 0;
if (nTimerID > 0)
Notice("Created timer " + IntToString(nTimerID) + sDebug);
return nTimerID;
}
int GetIsTimerValid(int nTimerID)
{
// Timer IDs less than or equal to 0 are always invalid.
if (nTimerID <= 0)
return FALSE;
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"SELECT 1 FROM timers WHERE timer_id = @timer_id;");
SqlBindInt(q, "@timer_id", nTimerID);
return SqlStep(q) ? SqlGetInt(q, 0) : FALSE;
}
void StartTimer(int nTimerID, int bInstant = TRUE)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"UPDATE timers SET running = 1, run_id = run_id + 1 " +
"WHERE timer_id = @timer_id AND running = 0 RETURNING run_id;");
SqlBindInt(q, "@timer_id", nTimerID);
if (SqlStep(q))
{
Notice("Started timer " + IntToString(nTimerID));
AssignCommand(TIMERS, _TimerElapsed(nTimerID, SqlGetInt(q, 0), !bInstant));
}
else
{
string sDebug = "StartTimer(" + IntToString(nTimerID) + ")";
if (GetIsTimerValid(nTimerID))
Error(sDebug + "failed: timer is already running");
else
Error(sDebug + " failed: timer id does not exist");
}
}
void StopTimer(int nTimerID)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"UPDATE timers SET running = 0 " +
"WHERE timer_id = @timer_id RETURNING 1;");
SqlBindInt(q, "@timer_id", nTimerID);
if (SqlStep(q))
Notice("Stopping timer " + IntToString(nTimerID));
}
void ResetTimer(int nTimerID)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"UPDATE timers SET remaining = timers.iterations " +
"WHERE timer_id = @timer_id AND iterations > 0 RETURNING remaining;");
SqlBindInt(q, "@timer_id", nTimerID);
if (SqlStep(q))
{
Notice("ResetTimer(" + IntToString(nTimerID) + ") successful: " +
IntToString(SqlGetInt(q, 0)) + " iterations remaining");
}
}
void KillTimer(int nTimerID)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"DELETE FROM timers WHERE timer_id = @timer_id RETURNING 1;");
SqlBindInt(q, "@timer_id", nTimerID);
if (SqlStep(q))
Notice("Killing timer " + IntToString(nTimerID));
}
int GetIsTimerInfinite(int nTimerID)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"SELECT iterations FROM timers WHERE timer_id = @timer_id;");
SqlBindInt(q, "@timer_id", nTimerID);
return SqlStep(q) ? !SqlGetInt(q, 0) : FALSE;
}
int GetTimerRemaining(int nTimerID)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"SELECT remaining FROM timers WHERE timer_id = @timer_id;");
SqlBindInt(q, "@timer_id", nTimerID);
return SqlStep(q) ? SqlGetInt(q, 0) : -1;
}
void SetTimerRemaining(int nTimerID, int nRemaining)
{
CreateTimersTable();
sqlquery q = SqlPrepareQueryModule(
"UPDATE timers SET remaining = @remaining " +
"WHERE timer_id = @timer_id AND iterations > 0;");
SqlBindInt(q, "@timer_id", nTimerID);
SqlBindInt(q, "@remaining", nRemaining);
SqlStep(q);
}

1139
_module/nss/util_i_times.nss Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,375 @@
/// ----------------------------------------------------------------------------
/// @file util_i_unittest.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Functions for managing unit test reporting.
/// ----------------------------------------------------------------------------
/// @details
///
/// Variable Conventions:
///
/// Tests can be written in just about any format, however since tests tend to be
/// repetitive, having a variable and formatting convention can make building
/// multiple tests quick and easy. Following are variable naming conventions
/// and an example that showcases how to use them.
///
/// Variable Naming:
/// ix - Function Input Variables
/// ex - Expected Function Result Variables
/// rx - Actual Function Result Variables
/// bx - Boolean Test Result Variables
/// tx - Timer Variables
///
/// Convenience Functions:
/// _i : IntToString
/// _f : FloatToString; Rounds to significant digits
/// _b : Returns `True` or `False` (literals)
///
/// _q : Returns string wrapped in single quotes
/// _qq : Returns string wrapped in double quotes
/// _p : Returns string wrapped in parenthesis
///
/// Timers:
/// To start a timer:
/// t1 = Timer(); : Sets timer variable `t1` to GetMicrosecondCounter()
///
/// To end a timer and save the results:
/// t1 = Timer(t1); : Sets timer variable `t1` to GetMicrosecondCounter() - t1
///
/// The following example shows how to create a grouped assertion and display
/// only relevant results, assuming only assertion failures are of
/// interest. If you always want to see expanded results regardless of test
/// outcome, set UNITTEST_ALWAYS_EXPAND to TRUE in `util_c_unittest`.
///
/// For example purposes only, this unit test sample code will run a unittest
/// against the following function, which will return:
/// -1, if n <= 0
/// 20 * n, if 0 < n <= 3
/// 100, if n > 3
///
/// ```nwscript
/// int unittest_demo_ConvertValue(int n)
/// {
/// return n <= 0 ? -1 : n > 3 ? 100 : 20 * n;
/// }
/// ```
///
/// The following unit test will run against the function above for three test cases:
/// - Out of bounds (low) -> n <= 0;
/// - In bounds -> 0 < n <= 3;
/// - Out of bounds (high) -> n > 3;
///
/// ```nwscript
/// int unittest_ConvertValue()
/// {
/// int i1, i2, i3;
/// int e1, e2, e3;
/// int r1, r2, r3;
/// int b1, b2, b3, b;
/// int t1, t2, t3, t;
///
/// // Setup the input values
/// i1 = -10;
/// i2 = 2;
/// i3 = 12;
///
/// // Setup the expected return values
/// e1 = -1;
/// e2 = 40;
/// e3 = 100;
///
/// // Run the unit tests with timers
/// t = Timer();
/// t1 = Timer(); r1 = unittest_demo_ConvertValue(i1); t1 = Timer(t1);
/// t2 = Timer(); r2 = unittest_demo_ConvertValue(i2); t2 = Timer(t2);
/// t3 = Timer(); r3 = unittest_demo_ConvertValue(i3); t3 = Timer(t3);
/// t = Timer(t);
///
/// // Populate the results
/// b = (b1 = r1 == e1) &
/// (b2 = r2 == e2) &
/// (b3 = r3 == e3);
///
/// // Display the result
/// if (!AssertGroup("ConvertValue()", b))
/// {
/// if (!Assert("Out of bounds (low)", b1))
/// DescribeTestParameters(_i(i1), _i(e1), _i(r1));
/// DescribeTestTime(t1);
///
/// if (!Assert("In bounds", b2))
/// DescribeTestParameters(_i(i2), _i(e2), _i(r2));
/// DescribeTestTime(t2);
///
/// if (!Assert("Out of bounds (high)", b3))
/// DescribeTestParameters(_i(i3), _i(e3), _i(r3));
/// DescribeTestTime(t3);
/// } DescribeGroupTime(t); Outdent();
/// }
/// Note: Use of ResetIndent() or another indentation function, such as
/// Outdent(), may be required if moving to another group assertion.
#include "util_c_unittest"
#include "util_i_strings"
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
string TEST_INDENT = "TEST_INDENT";
string TEST_PASS = HexColorString("PASS", COLOR_GREEN_LIGHT);
string TEST_FAIL = HexColorString("FAIL", COLOR_RED_LIGHT);
string TEST_DELIMITER = HexColorString(" | ", COLOR_WHITE);
// -----------------------------------------------------------------------------
// Function Prototypes
// -----------------------------------------------------------------------------
/// @brief Establishes or calculates a timer or elapsed value.
/// @param t Previous timer value derived from this function.
/// @note Calling this function without parameter `t` specified will
/// return a starting value in microseconds. When the code in
/// question has been run, call this function again and pass
/// the previously returned value as parameter `t` to calculate
/// the total elapsed time for between calls to this function.
int Timer(int t = 0);
/// @brief Reset the indentation level used in displaying test results.
/// @returns The indenation string used to pad test result output.
string ResetIndent();
/// @brief Indent test results display by one indentation level.
/// @param bReset If TRUE, will reset the indentation level to 0 before
/// adding an indentation level.
/// @returns The indenation string used to pad test result output.
string Indent(int bReset = FALSE);
/// @brief Outdent test results display by one indentation level.
/// @returns The indenation string used to pad test result output.
string Outdent();
/// @brief Provide a test suite description.
/// @param sDescription The description to display.
/// @note Test suite description will always display at indentation level
/// 0 and will reset the indentation level for the subsequest assertions.
void DescribeTestSuite(string sDescription);
/// @brief Provide a test group description.
/// @param sDescription The description to display.
/// @note Test groups are used to minimize unit test output if all tests
/// within a group pass. This function only provides a header for the
/// test group. To provide a test group description combined with
/// test group ouput, use AssertGroup().
void DescribeTestGroup(string sDescription);
/// @brief Display the parameter used in a test.
/// @param sInput The input data.
/// @param sExpected The expected test result.
/// @param sReceived The actual test result.
/// @note Each paramater is optional. If any parameter is an empty string,
/// that parameter will not be output.
void DescribeTestParameters(string sInput = "", string sExpected = "", string sReceived = "");
/// @brief Display function timer result.
/// @param nTime Function timer result, in microseconds.
/// @note This function is intended to use output from GetMicrosecondCounter().
void DescribeTestTime(int nTime);
/// @brief Display function timer result.
/// @param nTime Function timer result, in microseconds.
/// @note This function is intended to use output from GetMicrosecondCounter().
void DescribeGroupTime(int nTime);
/// @brief Display the results of a unit test.
/// @param sTest The name of the unit test.
/// @param bAssertion The results of the unit test.
/// @returns The results of the unit test.
int Assert(string sTest, int bAssertion);
/// @brief Display the results of a group test.
/// @param sTest The name of the group test.
/// @param bAssertion The results of the group test.
/// @returns The results of the group test.
int AssertGroup(string sGroup, int bAssertion);
// -----------------------------------------------------------------------------
// Private Function Implementations
// -----------------------------------------------------------------------------
string _GetIndent(int bReset = FALSE)
{
if (bReset)
ResetIndent();
string sIndent;
int nIndent = GetLocalInt(GetModule(), TEST_INDENT);
if (nIndent == 0)
return "";
while (nIndent-- > 0)
sIndent += " ";
return sIndent;
}
// -----------------------------------------------------------------------------
// Public Function Implementations
// -----------------------------------------------------------------------------
string _i(int n) { return IntToString(n); }
string _f(float f) { return FormatFloat(f, "%!f"); }
string _b(int b) { return b ? "True" : "False"; }
string _q(string s) { return "'" + s + "'"; }
string _qq(string s) { return "\"" + s + "\""; }
string _p(string s) { return "(" + s + ")"; }
int Timer(int t = 0)
{
return GetMicrosecondCounter() - t;
}
string ResetIndent()
{
DeleteLocalInt(GetModule(), TEST_INDENT);
return _GetIndent();
}
string Indent(int bReset = FALSE)
{
if (bReset)
ResetIndent();
int nIndent = GetLocalInt(GetModule(), TEST_INDENT);
SetLocalInt(GetModule(), TEST_INDENT, ++nIndent);
return _GetIndent();
}
string Outdent()
{
int nIndent = GetLocalInt(GetModule(), TEST_INDENT);
SetLocalInt(GetModule(), TEST_INDENT, max(0, --nIndent));
return _GetIndent();
}
void DescribeTestSuite(string sDescription)
{
sDescription = HexColorString("Test Suite ", UNITTEST_TITLE_COLOR) +
HexColorString(sDescription, UNITTEST_NAME_COLOR);
Indent(TRUE);
HandleUnitTestOutput(sDescription);
}
void DescribeTestGroup(string sDescription)
{
sDescription = HexColorString("Test Group ", UNITTEST_TITLE_COLOR) +
HexColorString(sDescription, UNITTEST_NAME_COLOR);
HandleUnitTestOutput(_GetIndent() + sDescription);
Indent();
}
void DescribeTestParameters(string sInput, string sExpected, string sReceived)
{
Indent();
if (sInput != "")
{
json jInput = JsonParse(sInput);
if (jInput != JSON_NULL && JsonGetLength(jInput) > 0)
{
if (JsonGetType(jInput) == JSON_TYPE_ARRAY)
{
string s = "WITH atoms AS (SELECT atom FROM json_each(@json)) " +
"SELECT group_concat(atom, ' | ') FROM atoms;";
sqlquery q = SqlPrepareQueryObject(GetModule(), s);
SqlBindJson(q, "@json", jInput);
sInput = SqlStep(q) ? SqlGetString(q, 0) : sInput;
}
else if (JsonGetType(jInput) == JSON_TYPE_OBJECT)
{
string s = "WITH kvps AS (SELECT key, value FROM json_each(@json)) " +
"SELECT group_concat(key || ' = ' || (IFNULL(value, '\"\"\"\"')), ' | ') FROM kvps;";
sqlquery q = SqlPrepareQueryObject(GetModule(), s);
SqlBindJson(q, "@json", jInput);
sInput = SqlStep(q) ? SqlGetString(q, 0) : sInput;
}
sInput = RegExpReplace("(?:^|\\| )(.*?)(?= =)", sInput, HexToColor(COLOR_BLUE_STEEL) + "$&</c>");
sInput = RegExpReplace("\\||=", sInput, HexToColor(COLOR_WHITE) + "$&</c>");
}
sInput = _GetIndent() + HexColorString("Input: ", UNITTEST_PARAMETER_COLOR) +
HexColorString(sInput, UNITTEST_PARAMETER_INPUT);
HandleUnitTestOutput(sInput);
}
if (sExpected != "")
{
sExpected = _GetIndent() + HexColorString("Expected: ", UNITTEST_PARAMETER_COLOR) +
HexColorString(sExpected, UNITTEST_PARAMETER_INPUT);
HandleUnitTestOutput(sExpected);
}
if (sReceived != "")
{
sReceived = _GetIndent() + HexColorString("Received: ", UNITTEST_PARAMETER_COLOR) +
HexColorString(sReceived, UNITTEST_PARAMETER_RECEIVED);
HandleUnitTestOutput(sReceived);
}
Outdent();
}
void DescribeTestTime(int nTime)
{
if (nTime <= 0)
return;
Indent();
string sTimer = _f(nTime / 1000000.0);
string sTime = _GetIndent() + HexColorString("Test Time: ", UNITTEST_PARAMETER_COLOR) +
HexColorString(sTimer + "s", UNITTEST_PARAMETER_INPUT);
Outdent();
HandleUnitTestOutput(sTime);
}
void DescribeGroupTime(int nTime)
{
if (nTime <= 0)
return;
string sTimer = _f(nTime / 1000000.0);
string sTime = _GetIndent() + HexColorString("Group Time: ", UNITTEST_PARAMETER_COLOR) +
HexColorString(sTimer + "s", UNITTEST_PARAMETER_INPUT);
HandleUnitTestOutput(sTime);
}
int Assert(string sTest, int bAssertion)
{
sTest = HexColorString("Test ", UNITTEST_TITLE_COLOR) +
HexColorString(sTest, UNITTEST_NAME_COLOR);
HandleUnitTestOutput(_GetIndent() + sTest + TEST_DELIMITER + (bAssertion ? TEST_PASS : TEST_FAIL));
if (!bAssertion)
HandleUnitTestFailure(sTest);
return UNITTEST_ALWAYS_EXPAND ? FALSE : bAssertion;
}
int AssertGroup(string sGroup, int bAssertion)
{
sGroup = HexColorString("Test Group ", UNITTEST_TITLE_COLOR) +
HexColorString(sGroup, UNITTEST_NAME_COLOR);
HandleUnitTestOutput(_GetIndent() + sGroup + TEST_DELIMITER + (bAssertion ? TEST_PASS : TEST_FAIL));
Indent();
if (!bAssertion)
HandleUnitTestFailure(sGroup);
return UNITTEST_ALWAYS_EXPAND ? FALSE : bAssertion;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@ void main ()
AddJournalQuestEntry("xprules", 1, oPlayer, FALSE, FALSE, FALSE);
AddJournalQuestEntry("lvl_adj", 1, oPlayer, FALSE, FALSE, FALSE);
AddJournalQuestEntry("JRNL_PRC8", 1, oPlayer, FALSE, FALSE, FALSE);
cs_DestroyStolenItems(oPlayer); // Destroy any stolen items
cs_LimitGoldInBank(oPlayer, 20000000); // Limit gold in bank