Custom Events Framework

Scripted ALFA systems & related tech discussions (ACR)

Moderators: ALFA Administrators, Staff - Technical

Locked
Ronan
Dungeon Master
Posts: 4611
Joined: Sun Feb 20, 2005 9:48 am

Custom Events Framework

Post by Ronan »

Here is what I have so far. Its currently implemented with arrays, but I'll be switching it over to linked lists (duh, don't know why I ever bothered with arrays...).

We are going to need some parallels between some events. For example, a spawning NPC should throw the same even as a PC entering a server for the first time. I think this will just be an issue of nomenclature, though. We also need to know if it will be possible to catch NPC events on PCs in NWN2, and if it will be any easier than using the default.nss hack.

I don't know that the default BW events will be used. The custom ones start at 100.

Code: Select all

////////////////////////////////////////////////////////////////////////////////
//
//  System Name : ALFA Core Scripts
//     Filename : _event_i.nss
//      Version : 0.1
//         Date : 3/31/06
//       Author : Ronan
//
//  Local Variable Prefix = COR_EVN_
//
//  Description
//  For information on how to use the events system, please see:
//  cor_event_exmple.nss.
//  This file contains the ALFA Events system, which is called directly by
//  core ALFA scripts. In a nutshell, this system allows us to define our own
//  event hooks, and defines a system by which scripts can be easily added
//  to run on these events. Events can be attached to any "caller", items,
//  feats, areas, creatures, etc.
//  Revision History
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Includes ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Constants ///////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// NWN Events:
//  Module Events:
const int NW_MOD_EVENT_ON_CLIENT_ENTER = 1;             // Players ONLY.
const int NW_MOD_EVENT_ON_CLIENT_LEAVE = 2;             // Players ONLY.
const int NW_MOD_EVENT_ON_EQUIP = 3;                    // Players ONLY.
const int NW_MOD_EVENT_ON_UNEQUIP = 4;                  // Players ONLY.
const int NW_MOD_EVENT_CUT_SCENE_ABORT = 5;             // Players ONLY.
const int NW_MOD_EVENT_ON_AQUIRE_ITEM = 6;              // Players ONLY.
const int NW_MOD_EVENT_ON_UNAQUIRE_ITEM = 7;            // Players ONLY.
const int NW_MOD_EVENT_ON_ACTIVATE_ITEM = 8;
const int NW_MOD_EVENT_ON_PLAYER_DEATH = 9;             // Players ONLY.
const int NW_MOD_EVENT_ON_PLAYER_DYING = 10;            // Players ONLY.
const int NW_MOD_EVENT_ON_HEART_BEAT = 11;              // Unused.
const int NW_MOD_EVENT_ON_LEVEL_UP = 12;                // Players ONLY.
const int NW_MOD_EVENT_ON_MODULE_LOAD = 13;             // Unused.
const int NW_MOD_EVENT_ON_RESPAWN = 14;                 // Players ONLY.
const int NW_MOD_EVENT_ON_REST = 15;                    // Players ONLY.
// Area Events:
const int NW_AREA_EVENT_ON_AREA_ENTER = 16;
const int NW_AREA_EVENT_ON_AREA_EXIT = 17;
const int NW_AREA_EVENT_ON_HEART_BEAT = 18;
//  Creature Events: These ONLY act on NPC creatures and objects, never PCs.
const int NW_CREATURE_EVENT_ON_PERCEPTION = 19;
const int NW_CREATURE_EVENT_ON_SPELL_CAST_AT = 20;        // Covered by ALFA_EVENT_SPELL_CAST_AT.
const int NW_CREATURE_EVENT_ON_PHYSICAL_ATTACKED = 21;    // Covered by ALFA_EVENT_PHYSICAL_ATTACKED.
const int NW_CREATURE_EVENT_ON_DAMAGED = 22;              // Covered by ALFA_EVENT_DAMAGED.
const int NW_CREATURE_EVENT_ON_DISTURBED = 23;            // Covered by ALFA_EVENT_(UN)AQUIRE_ITEM.
const int NW_CREATURE_EVENT_ON_COMBAT_ROUND_END = 24;
const int NW_CREATURE_EVENT_ON_CONVERSATION = 25;         // Covered by ALFA_EVENT_ON_CONVERSATION.
const int NW_CREATURE_EVENT_ON_RESTED = 26;               // Covered by ALFA_EVENT_ON_RESTED.
const int NW_CREATURE_EVENT_ON_DEATH = 27;                // Covered by ALFA_EVENT_ON_DEATH.
const int NW_CREATURE_EVENT_ON_BLOCKED = 28;
const int NW_CREATURE_EVENT_ON_HEART_BEAT = 29;           // Covered by ALFA_EVENT_ON_HEART_BEAT.

//const int NW_CREATURE_EVENT_ON_DAMAGED = 31;
//const int NW_CREATURE_EVENT_ON_DEATH = 32;
//const int NW_CREATURE_EVENT_ON_HEART_BEAT = 33;
//  Placable Events:
const int NW_PLACE_EVENT_ON_DISTURBED = 34;
const int NW_PLACE_EVENT_ON_LOCK = 35;
const int NW_PLACE_EVENT_ON_PHYSICAL_ATTACKED = 36;
const int NW_PLACE_EVENT_ON_OPEN = 37;
const int NW_PLACE_EVENT_ON_SPELL_CAST_AT = 38;
const int NW_PLACE_EVENT_ON_UNLOCK = 39;
const int NW_PLACE_EVENT_ON_USED = 40;
// Trigger Events:
const int NW_TRIGGER_EVENT_ON_CLICK = 41;
const int NW_TRIGGER_EVENT_ON_ENTER = 42;
const int NW_TRIGGER_EVENT_ON_EXIT = 43;
const int NW_TRIGGER_EVENT_ON_HEART_BEAT = 44;
// Door Events:
const int NW_DOOR_EVENT_ON_OPEN = 45;
const int NW_DOOR_EVENT_ON_CLOSE = 46;
const int NW_DOOR_EVENT_ON_FAIL_TO_OPEN = 47;
const int NW_DOOR_EVENT_ON_LOCK = 48;
const int NW_DOOR_EVENT_ON_UNLOCK = 49;
const int NW_DOOR_EVENT_ON_PHYSICAL_ATTACKED = 50;
const int NW_DOOR_EVENT_ON_DAMAGED = 51;
const int NW_DOOR_EVENT_SPELL_CAST_AT = 52;
const int NW_DOOR_EVENT_ON_HEART_BEAT = 53;

// ALFA Events:

// Caller: Module.
// Called when: A PC enters the server, but has entered before since last reset.
const int CUST_EVENT_CLIENT_ENTER = 100;

// Caller: Module.
// Called when: A PC leaves.
const int CUST_EVENT_CLIENT_LEAVE = 101;

// Caller: A creature.
// Called when: A PC equips an item, or an NPC's AI script equips an item.
const int CUST_EVENT_ON_EQUIP = 102;

// Caller: A creature.
// Called when: A PC unequips an item, or an NPC's AI script unequips an item.
const int CUST_EVENT_ON_UNEQUIP = 103;

// Caller: Module.
// Called when: A player aborts cut-scene.
const int CUST_EVENT_CUT_SCENE_ABORT = 104;

// Caller: Object which gained the item.
// Called when: A PC or object gains an item, or a non-DM-posessed NPC gains an item.
const int CUST_EVENT_AQUIRE_ITEM = 105;

// Caller: Object which lost the item.
// Called when: A PC or object looses an item, or a non-DM-posessed NPC looses an item.
const int CUST_EVENT_UNAQUIRE_ITEM = 106;

// Caller: Item activator.
// Called when: A PC or NPC activates an item or placable.
const int CUST_EVENT_ACTIVATE_ITEM = 107;

// Caller: Dead object.
// Called when: Anything is killed or destroyed.
const int CUST_EVENT_DEATH = 108;

// Caller: Dying creature.
// Called when: Anything is given enough lethal damage to be dying, but not yet dead.
const int CUST_EVENT_DYING = 109;

// Caller: Leveling-up creature.
// Called when: A PC levels-up
const int CUST_EVENT_LEVEL_UP = 110;

// Caller: The module.
// Called when: The module loads.
const int CUST_EVENT_MODULE_LOAD = 111;

// Caller: The module.
// Called when: The player hits the "respawn" button after death.
const int CUST_EVENT_RESPAWN = 112;

// Caller: The resting creature.
// Called when: Anything is done resting.
const int CUST_EVENT_REST = 113;

// Caller: The area or trigger entered.
// Called when: Something enters a trigger or area.
const int CUST_EVENT_ENTER = 114;

// Caller: The area or trigger exited.
// Called when: Something exits a trigger or area.
const int CUST_EVENT_EXIT = 115;

// Caller: The creature who could perceive something.
// Called when: A creature has a chance to see or hear something.
const int CUST_EVENT_PERCEPTION = 116;

// Caller: Target of the spell.
// Called when: Any object is direct targeted by a spell.
const int CUST_EVENT_SPELL_CAST_AT = 117;

// Caller: The attacked object.
// Called when: Any non-PC object is attacked physically.
const int CUST_EVENT_PHYSICAL_ATTACKED = 118;

// Caller: The damaged object.
// Called when: Any object is damaged.
const int CUST_EVENT_DAMAGED = 119;

// Caller: The NPC who hears speach.
// Called when: An NPC hears speach, or is clicked on to talk to.
const int CUST_EVENT_CONVERSES = 120;

// Caller: The creature.
// Called when: A creature's pathfinding is blocked by a door.
const int CUST_EVENT_BLOCKED = 121;

// Caller: The locked object.
// Called when: Something is locked in any way other than the DM using force-lock.
const int CUST_EVENT_LOCK = 122;

// Caller: The opened object.
// Called when: Something is opened.
const int CUST_EVENT_OPEN = 123;

// Caller: The unlocked object.
// Called when: Something is unlocked in any way other than the DM using force-unlock.
const int CUST_EVENT_UNLOCK = 124;

// Caller: The using creature.
// Called when: Any item or placable is used.
const int CUST_EVENT_USED = 125;

// Caller: The clicked trigger.
// Called when: A trigger is clicked on.
const int CUST_EVENT_CLICK = 126;

// Caller: The creature who failed to open.
// Called when: Any creature fails to open a door.
const int CUST_EVENT_FAIL_TO_OPEN = 127;

// Caller: The object with the heartbeat script.
// Called when: That script is fired.
const int CUST_EVENT_HEART_BEAT = 128;

// Caller: The equiped item.
// Called when: An item is equiped by a PC or a creature's AI script.
const int CUST_EVENT_EQUIPS = 129;

// Caller: The unequiped item.
// Called when: An item is unequiped by a PC or a creature's AI script.
const int CUST_EVENT_UNUEQUIPS = 130;

// Caller: The spell caster.
// Called when: When a spell, spell-like ability, or anything using a spell-script is called.
const int CUST_EVENT_SPELL_CAST_BY = 131;

// Caller: The damager.
// Called when: When an object damages a non-PC object.
const int CUST_EVENT_DAMAGES = 132;

// Caller: The killer.
// Called when: When an object kills or destroys another object/creature/player/etc.
const int CUST_EVENT_KILLS = 133;

// Caller: The object that knocked-out the creature.
// Called when: An object knocks something out with non-lethal damage.
const int CUST_EVENT_KNOCKS_OUT = 134;

// Caller: The object that left the creature dying.
// Called when: An object damages a creature and leaves it bleeding.
const int CUST_EVENT_LEAVES_DYING = 135;        // Called when an object leaves a creature/player bleeding (with lethal damage).

// Caller: The object that attacked.
// Called when: A non-PC object is physically attacked.
const int CUST_EVENT_PHYSICAL_ATTACKS = 136;    // Called when an object physically attacks another non-PC object.

// Caller: The module.
// Called when: A PC enters the server, this event is called on each object in
//              their inventory.
const int CUST_EVENT_ENTERED_WITH = 137;

// Caller: The module.
// Called when: A PC enters, this event is called on each equiped item.
const int CUST_EVENT_ENTERED_WITH_EQUIPED = 138;

// Caller: The module.
// Called when: A PC enters a module for the first time since the server's last
//              reset. This event is called on each object in their inventory.
const int CUST_EVENT_FIRST_ENTERED_WITH = 139;

// Caller: The module.
// Called when: A PC enters a module for the first time since the server's last
//              reset. This event is called on each of their equiped items.
const int CUST_EVENT_FIRST_ENTERED_WITH_EQUIPED = 140;

// Caller: The spell caster.
// Called when: A spell effects an object in any way.
const int CUST_EVENT_SPELL_EFFECTS = 141;

// Caller: Module.
// Called when: A PC enters the server, but has NOT entered before since last
// reset.
const int CUST_EVENT_CLIENT_FIRST_ENTER = 142;

// The prefix for the array of event scripts on an object.
// DO NOT CHANGE THESE without first considering the changes required to every
// item using these scripts.
const string LSA_CUST_EVENT_LIST = "CES_";
const string LI_CUST_EVENT_LIST_LENGTH = "CESL";

// The prefix for the array of links to another object.
const string LOA_CUST_EVENT_LINKS = "CEL_";
const string LI_CUST_EVENT_LINKS_LENGTH = "CELL";

// The local variable name of the event type, passed to each event script when
// an event is triggered.
const string LI_CUST_EVENT_TYPE = "CET_";

////////////////////////////////////////////////////////////////////////////////
// Structures //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Global Variables ////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// The debug id for this system.
int debugId;

////////////////////////////////////////////////////////////////////////////////
// Function Prototypes /////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// This function signals a custom event. After calling this function, the events
// system will run any and all event scripts stored on the caller object with
// the same type as event_type.
// caller:      The owner of the event script.
// event_type:  The integer value coresponding to the type of event fired. See
//              The cor_event_inc.nss script.
void SignalCustomEvent(object caller, int event_type);

// This function links a custom event fron one object to another. It is used
// when an object must apply an event to another object for whatever reason.
// Only event scripts of event_type will be linked. All linked scripts of that
// type are executed when an event of that type is signaled on the target.
// oCaller:     The owner of the event script.
// oTarget:     The target which will receive the event script.
// eventType:   The type of event script(s) to be added.
//
// For example, a ring of spell-reflection has an CUST_EVENT_SPELL_CAST_AT
// script, which would oridinarily only function if a spell was cast on the ring
// itself. However, the ring's CUST_EVENT_ENTERED_WITH_EQUIPED and
// CUST_EVENT_EQUIPS scripts call,
// AddCustomEvent(oRing, oRingOwner, CUST_EVENT_SPELL_CAST_AT);
// This adds the ring's spell reflection script to the owner, making the ring's
// wearer reflect spells.
void LinkCustomEvent(object oCaller, object oTarget, int eventType);

// The opposite of LinkCustomEvent, this function removes any links between the
// events of caller on target of type eventType.
void UnlinkCustomEvent(object oCaller, object oTarget, int eventType);

// Called once to initialize the custom events system.
void CorEventsOnModuleLoad();

// Runs all event on that object, and that object only.
// Not for external use, as it does not pass parameters.
// See SignalCustomEvent().
void RunEventsOnObject(object caller, int eventType);

// Calls the event eventType on all items equiped by creature oTarget,
// even creature items.
void SignalCustomEventOnEquiped(object oTarget, int eventType);
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Re: Custom Events Framework

Post by ç i p h é r »

Ronan wrote:We also need to know if it will be possible to catch NPC events on PCs in NWN2, and if it will be any easier than using the default.nss hack.
Yeah a real good question to ask the devs in the next community chat.

Code: Select all

//const int NW_CREATURE_EVENT_ON_DAMAGED = 31;
//const int NW_CREATURE_EVENT_ON_DEATH = 32;
//const int NW_CREATURE_EVENT_ON_HEART_BEAT = 33;
//  Placable Events:
These look extraneous...?

Code: Select all

// AddCustomEvent(oRing, oRingOwner, CUST_EVENT_SPELL_CAST_AT);
Should be LinkCustomEvent, right?


I think it would be useful for the purposes of obtaining feedback to give an overview of the events system. It'd be easier than trying to piece together an understanding by reviewing the code.

Specific questions:

1. Usage. Since it's fairly common to define custom events for use in game systems, can you explain how a dev would go about defining, setting up, and using a custom event? This is something we'll want to be crystal clear on to avoid getting into trouble down the road. I'm sure we've all got examples from our own work, but signalling custom events with creature UDEs is one thing that comes to mind for me and I know is fairly common.

2. How are you managing the event inheritance such as you described with Spell Reflection? Are you going to use an event struct to store child/parent objects? Just wondering as it's not evident here. Devs won't need to worry about those details but for the sake of offering feedback, I thought I'd ask.

3. Do you want to try and define a fairly exhaustive list of events up front or as we go?
Ronan
Dungeon Master
Posts: 4611
Joined: Sun Feb 20, 2005 9:48 am

Re: Custom Events Framework

Post by Ronan »

ç i p h é r wrote:

Code: Select all

//const int NW_CREATURE_EVENT_ON_DAMAGED = 31;
//const int NW_CREATURE_EVENT_ON_DEATH = 32;
//const int NW_CREATURE_EVENT_ON_HEART_BEAT = 33;
//  Placable Events:
These look extraneous...?
I mostly listed the BW events for my own reference, and don't actually expect them to be used.
ç i p h é r wrote:

Code: Select all

// AddCustomEvent(oRing, oRingOwner, CUST_EVENT_SPELL_CAST_AT);
Should be LinkCustomEvent, right?
Oops! Looks like I forgot to update a few more things in that script.
ç i p h é r wrote:I think it would be useful for the purposes of obtaining feedback to give an overview of the events system. It'd be easier than trying to piece together an understanding by reviewing the code.
Yeah, understanding it may be a problem, as its more abstract than most builders are used to. I think a template file will go a long way to explaining it.
ç i p h é r wrote:Specific questions:

1. Usage. Since it's fairly common to define custom events for use in game systems, can you explain how a dev would go about defining, setting up, and using a custom event? This is something we'll want to be crystal clear on to avoid getting into trouble down the road. I'm sure we've all got examples from our own work, but signalling custom events with creature UDEs is one thing that comes to mind for me and I know is fairly common.
I was actually going to put together a well-commented template file, like AJAI's OnSpawn script. But first I think we need to get the framework how we want it.
ç i p h é r wrote:2. How are you managing the event inheritance such as you described with Spell Reflection? Are you going to use an event struct to store child/parent objects? Just wondering as it's not evident here. Devs won't need to worry about those details but for the sake of offering feedback, I thought I'd ask.
I could paste the code if you'd like, though I suppose I should be submitting that to the repository. For some inane reason I implimented it with arrays, so I need to change to linked lists. Anyways, inheritance was going to be controlled via the event scripts themselves. In other words, the ring would call LinkCustomEvent() from its CUST_EVENT_ON_EQUIP and CUST_EVENT_ENTERED_WITH_EQUIPED, and call UnlinkCustomEvent() from CUST_EVENT_ON_UNEQUIP.
ç i p h é r wrote:3. Do you want to try and define a fairly exhaustive list of events up front or as we go?
I was thinking just to do our best-guess as to what will be supportable in NWN2. It will likely keep changing until release, and probably even after, of course.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

Putting it in the repository would be great. I don't have any more thoughts to share but I probably will once you publish the code and/or the template file.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

I've been thinking about how best to invoke functions from events while maintaining modularity as much as possible.

As I see it, there are a number of ways to achieve the same result in NWScript. One way would be to execute the main script (file) for the subsystem in question. This seems to be the best way to preserve modularity as the coupling is minimal. However, it's not necessarily as efficient since you can't pass information that may already be available or deduced in the event handler. The more efficient way would thus be to actually CALL the entry function into the subsystem from the event handler passing it whatever arguments are necessary. This would require partitioning subsystem functions into files and includes that can then be referenced appropriately in the event handler. Another way may also exist with OnUserDefined hooks.

Have you given any thought to this? I think it's something all devs are going to have to consider early on when figuring out the best implementation so it would be good to clear this up in advance.
Ronan
Dungeon Master
Posts: 4611
Joined: Sun Feb 20, 2005 9:48 am

Post by Ronan »

ç i p h é r wrote:I've been thinking about how best to invoke functions from events while maintaining modularity as much as possible.
Just to clarify, your wondering about calling functions from a NWN event, correct? If so, I've been thinking about that a bit as well.
ç i p h é r wrote:As I see it, there are a number of ways to achieve the same result in NWScript. One way would be to execute the main script (file) for the subsystem in question. This seems to be the best way to preserve modularity as the coupling is minimal. However, it's not necessarily as efficient since you can't pass information that may already be available or deduced in the event handler. The more efficient way would thus be to actually CALL the entry function into the subsystem from the event handler passing it whatever arguments are necessary. This would require partitioning subsystem functions into files and includes that can then be referenced appropriately in the event handler. Another way may also exist with OnUserDefined hooks.
If your saying what I think your saying, I thought the public/private conventions covered this? We can't hide things in an include file, so we push them off with the underscore, only showing the functions which should be used externally. I think this has an advantage of speed, simplicity and organization over a method using ExecuteScript() and many files with void main()s.
ç i p h é r wrote:Have you given any thought to this? I think it's something all devs are going to have to consider early on when figuring out the best implementation so it would be good to clear this up in advance.
In case I read you wrong and you were talking about our custom events, I don't foresee the custom events being needed for core ACR functionality. I do see them being needed for proper implimentations of many spells (such as true-strike) and abilities which we will undoubtably add later. Its supposed to be a modular way to add features, and I don't think it makes a lot of sense to use it on features we know we need now, unless its functionality is required. Do you agree?

(Plus, Obsidian is allowing us to change event scripts on the fly in NWN2, so I'd like to wait a bit and see how that pans out).
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

Ronan wrote:Just to clarify, your wondering about calling functions from a NWN event, correct? If so, I've been thinking about that a bit as well.
Yes, precisely. Specifically, do we want to call the main script of a system (add ExecuteScript(...) to event script) from the relevant NWN event or do we want to call the system's entry function (add #include and SysEntryFunction() to event script) or is there another alternative? Either are plausible, but which is preferred? You can't get any simpler than an ExecuteScript but as I mentioned, you can't pass data to/from the calling function this way, if need be. So I suppose I'm leaning towards the latter.
Ronan wrote:In case I read you wrong and you were talking about our custom events, I don't foresee the custom events being needed for core ACR functionality. I do see them being needed for proper implimentations of many spells (such as true-strike) and abilities which we will undoubtably add later. Its supposed to be a modular way to add features, and I don't think it makes a lot of sense to use it on features we know we need now, unless its functionality is required. Do you agree?
I hadn't really thought about that but yes, it makes sense.
Ronan
Dungeon Master
Posts: 4611
Joined: Sun Feb 20, 2005 9:48 am

Post by Ronan »

ç i p h é r wrote:
Ronan wrote:Just to clarify, your wondering about calling functions from a NWN event, correct? If so, I've been thinking about that a bit as well.
Yes, precisely. Specifically, do we want to call the main script of a system (add ExecuteScript(...) to event script) from the relevant NWN event or do we want to call the system's entry function (add #include and SysEntryFunction() to event script) or is there another alternative? Either are plausible, but which is preferred? You can't get any simpler than an ExecuteScript but as I mentioned, you can't pass data to/from the calling function this way, if need be. So I suppose I'm leaning towards the latter.
One thing I just noticed after messing around with our spellhook code (switched from the default ExecuteScript() to a function call) is that code does not seem to be shared between void main()s. In other words, removing the ExecuteScript() from the spellhook ment that each spell script suddenly had to compile the spellhook itself in its .ncs file. The result was an increase in module size by 5 megabytes! If I was writing compiled code, I might worry about how this would hurt performance by rendering caching schemes useless, but I don't think any spell scripts make it very far up a PC's memory hierarchy anyways.
ç i p h é r wrote:I hadn't really thought about that but yes, it makes sense.
I guess didn't mention it, but that was actually one of the bigger reasons I wanted that "public/private" convention.
Locked