ERF V1.0 gExportInfoh_aih_classh_constantsh_debugh_landmark_clih_landmark_clih_landmark_comh_landmark_comh_landmark_init h_landmark_init h_library h_time h_util lib_landmark2lib_landmark2b)"LZMb* і'2OGmqGFF V3.28(,8PX (08@HPX`hpx   c r               & - 4 BTMod_MinGameVerExpansion_PackCommentsTopResRefResTypeDependenciesMissing1.30WUpdate Trail and Landmark code. Includes new client functions and some small bug fixes.h_landmark_clih_landmark_comh_landmark_init lib_landmark2h_aih_class h_constantsh_debugh_landmark_clih_landmark_clih_landmark_comh_landmark_comh_landmark_inith_landmark_init h_libraryh_timeh_util lib_landmark2 lib_landmark2 +  !"#$%&'()* #include "h_class" #include "h_time" #include "h_util" /* File: h_ai - Memetic Artificial Intelligence Toolkit * Author: William Bull, Daryl Low * Date: Copyright April, 2003 * * Description * * These functions allow for the construction of a modular behavior response * system. This script is part of a complete replacement of Bioware's generic AI. * */ /* TODO: We must go through all the while loops and make sure they are changed * to handle invalid objects as a result of MeGetObjectByIndex()... * Have we done this yet? * Is a TODO list still valid? * Please visit http://www.summermeat.net/memeticai/ for the last info. */ // MeCreateMeme // Create a behavior that is defined in a file or library. // // You can attach variables to this meme object. When the meme runs, it can access // this data through the use of the global variable MEME_SELF. // // sName: The name of the meme, for example "i_wait" // iPriority: The importance of this behavior: // PRIO_HIGH, PRIO_LOW, PRIO_MEDIUM, PRIO_HIGH, PRIO_VERYHIGH // The highest priority meme at a given moment will actually run. // iModifier: The value within a priority band, a number from -100 to 100. // iFlags: These control if the behavior of meme through its lifecycle. Some memes will // automatically turn on or off flags their own flags, once it is created. // MEME_RESUME: if the meme is interrupted, auto resume the behavior, otherwise destroy it. // MEME_REPEAT: this causes the meme to loop, some memes *must* loop to work, like i_walkwp // MEME_INSTANT: do not create this meme, if there are existing higher priority memes // MEME_IMMEDIATE: create this meme but don't interrupt the current meme // MEME_CHILDREN: Allow multiple children to run, regardless of their return result. // Normally, only one successful child to runs. All other children are destroyed // once a children ends without returning FALSE via a call to MeSetMemeResult(); // If the meme ends without calling MeSetMemeResult(FALSE) the toolkit assumes // the child meme succeeds. // You can mask flags together, for example: MEME_RESUME | MEME_REPEAT // oParent: This is either a generator or another meme. object MeCreateMeme(string sName, int iPriority = 2, int iModifier = 0, int iFlags = 0x10 /*MEME_RESUME*/, object oParent = OBJECT_INVALID); // MeSetMemeResult // This defines whether or not the meme completed successfully. // For example, if the meme represents going somewhere, this function should // be called if for some reason it cannot get there. // // NOTE: If this function is not called, the system assumes the meme succeeded. // At this time you really only need to call MeSetMemeResult(FALSE); // // This result will be used to evaluate child memes -- these are memes which // are created with a parent meme parameter. If the meme succeeds, the other // child memes may be destroyed if this is called with a result of TRUE. // // iResult: TRUE or FALSE void MeSetMemeResult(int iResult, object oMeme = OBJECT_INVALID); // MeSetPriority // Change the priority of an existing memetic object, This can include memes, generators, // and events. In fact any object that has an ObjectRef list named ChildMeme. // // oTarget: The meme to be adjusted. // iPriority: The importance of this behavior: // PRIO_HIGH, PRIO_LOW, PRIO_MEDIUM, PRIO_HIGH, PRIO_VERYHIGH // iModifier: An optional value within a priority band, a number from -100 to 100. // bPropogate: A boolean (0 or 1) to signify that the value set on oTarget should // also be set on all the memes refered to in the ChildMeme object ref list. // For example a generator that creates a set of memes and is associated // to those memes can have its default priority changed. This could // cause those previously created memes' priority to change as well. // // Notes: You must call MeUpdateActions(); after calling this function. This will // cause any meme priority changes to take effect. void MeSetPriority(object oTarget, int iPriority, int iModifier = 0, int bPropogate = 0); // MeGetPriority // Gets the internal priority of a memetic object. This works on memes, generators and // event events that have been created with a priority. int MeGetPriority(object oMeme); // MeGetModifier // Gets the internal priority of a memetic object. This works on memes, generators and // event events that have been created with a priority. int MeGetModifier(object oMeme); // MeDestroyMeme // Destroys a meme and selects the next meme to be activated. // You may need to call MeUpdateActions() to cause the scheduled meme to execute. void MeDestroyMeme(object oMeme); // MeGetActiveMeme // Returns the currently running meme. object MeGetActiveMeme(); // MeGetPendingMeme // Returns a meme that is scheduled to preempt the current active meme. This // is the meme that will run as soon as UpdateActions() is called. Its was // choosen by the internal function, ComputeBestMeme(). object MeGetPendingMeme(); // MeGetMeme // Find a meme on the current NPC with a given name and priority. // sName: An optional meme name, like "i_attacK' // iIndex: A zero based index into the list of matches // iPriority: The priority of the meme, PRIO_DEFAULT (0) will match any priority object MeGetMeme(string sName = "", int iIndex = 0, int iPriority = 0); // MeGetParentGenerator // If the meme was created by a generator and the generator is associated to it, // this function returns that generator. object MeGetParentGenerator(object oMeme); // MeGetParentMeme // If the meme was created as a child of another meme, this will return that parent meme // or OBJECT_INVALID if there is no parent. object MeGetParentMeme(object oMeme); // MeRestartMeme // This causes the action queue to be cleared, the meme to be interrupted with // a _brk call, then reinitialized with an _ini call and restarted with a _go call. // // oMeme: The active meme that should be restarted. // bCallInit: A TRUE or FALSE flag which causes _ini to be called. void MeRestartMeme(object oMeme, int bCallInit = 0, float fDelay = 0.0); // MeStopMeme // This causes the action queue to be cleared, the meme to be interrupted with // a _brk call, then ended naturally. If the meme is MEME_REPEAT it will likely // start over, if not it will be destroyed and a the next meme will execute. void MeStopMeme(object oMeme, float fDelay = 0.0); // MeCreateGenerator // This creates a specific generator to respond to NWN callbacks to generate // memetic objects and signals. The generator is not immediately started. // You may need to configure the generator's behavior by setting variables on // the object which this function returns. You will need to start the generator // by calling MeStartGenerator(). // // sName: The string name of the generator. This must match with a name of a // generator script (i.e. g_attack) or a generator name in a library. // iPriority: The priority to be passed to memes this generator creates. // iModifier: The modifier to be passed to memes this generator creates. // iFlags: There are no officially supported generator flags, at this time. object MeCreateGenerator(string sName, int iPriority = 0, int iModifier = 0, int iFlags = 0); // MeStartGenerator // This will start the generator and start processing NWN callbacks. // Each generator may be attached to many NWN callbacks. Once started, a // generator may respond to these callbacks by creating a variety of memetic // bjects or communicate via memetic signals. void MeStartGenerator(object oGenerator); // MeStopGenerator // This will stop the generator optionally remove the memetic objects the generator has created. // // Each generator may be attached to many NWN callbacks. Once started, a // generator may respond to these callbacks by creating a variety of memetic // objects or communicate via memetic signals. // // You can attach variables to this generator object. When the generator runs, it can access // this data through the use of the global variable MEME_SELF. // // oGenerator: an active generator that will be stopped. // iRemoveChildren: set of flags to signify which child objects to destroy: // TYPE_MEME: remove all memes - including sequences which have been started // TYPE_SEQUENCE: remove all definitions of sequences registered to this generator void MeStopGenerator(object oGenerator, int iRemoveChildren = 0x01 /* TYPE_MEME */); // MeDestroyGenerator // This will destroy a generator. // Each generator may be attached to many NWN callbacks. Once started, a // generator may respond to these callbacks by creating a variety of memetic // objects or communicate via memetic signals. This function ca automatically // destroy the sequences and memes created by the generater. // // oGenerator: an active generator that will be stopped. // iRemoveChildren: set of flags to signify which child objects to destroy: // TYPE_MEME: if the meme is interrupted, auto resume the behavior, otherwise destroy it. // TYPE_SEQUENCE: this causes the meme to loop, some memes *must* loop to work, like i_walkwp void MeDestroyGenerator(object oGenerator, int iRemoveChildren = 0x11 /* TYPE_MEME | TYPE_SEQUENCE */); // MeGetGenerator // This gets a generator with the given name or by count. // You can provide as many or as few of these parameters as you like to query the internal memetic store. // // sName: The name of the generator, like "g_comabt". // iIndex: The 0 based index of the generator - if there are more than one with the same name. object MeGetGenerator(string sName = "", int iIndex = 0); // MeGetChildMeme // This gets the iIndex meme that was created by the given generator or // meme. Returns OBJECT_INVALID if none exist. // // oTarget: The generator or meme that may have an associated child meme. // iIndex: The 0 based index of memes it has created. object MeGetChildMeme(object oTarget, int iIndex = 0); // MeSuspendMeme // Suspends an active meme, optionally calling the _brk callback. // This causes the toolkit to start the next highest priority meme. // // oMeme: The meme to be suspended. // bCallBrk: pass TRUE or FALSE to signal that the _brk script should be called. void MeSuspendMeme(object oMeme, int bCallBrk = 1); // MeResumeMeme // Resumes a suspended meme; causes it to be prioritized and potentially activated. // A meme with children cannot be resumed. // // oMeme: A meme that has been suspended and does not have children. void MeResumeMeme(object oMeme); // MeIsMemeSuspended // Returns TRUE if the meme is currently suspended. int MeIsMemeSuspended(object oMeme); // MeCreateSequence // This creates a named sequence which can be reused by calling MeStartSequence(). // Creating a sequence is only the first step - see: MeCreateSequenceMeme(). // A sequence encapsulates a collection of memes. It operates on each meme and possibly changes // its priority based on the active meme. For an overview on sequences refer to the User's Guide. // // sName: The name of the sequence -- you make this up, arbitrarily. It doesn't correspond to // any script name, it's so you can get access to your sequences, later. // iPriority: The importance of this behavior -- // PRIO_DEFAULT, PRIO_HIGH, PRIO_LOW, PRIO_MEDIUM, PRIO_HIGH, PRIO_VERYHIGH // Using PRIO_DEFAULT will cause the sequence to change its priority to match the meme at each step. // iModifier: The value within a priority band, a number from -100 to 100. // iFlags: These control if the behavior of meme through its lifecycle. Some memes will // automatically turn on or off flags their own flags, once it is created. // Note: MEME_RESUME and MEME_REPEAT are not used when you create a sequence. // SEQ_REPEAT: This the sequences version of MEME_REPEAT, you should never clear the MEME_REPEAT // flag on a sequence -- clear the SEQ_REPEAT flag if you want to stop the sequence from repeating. // SEQ_RESUME_FIRST: This allows the sequence to resume after being preempted by // another meme. The sequence will restart at the first meme. // If a meme in the sequence has the MEME_CHECKPOINT flag and it is executed, // the sequence will resume at this meme. // SEQ_RESUME_LAST: This allows the sequence to resume after being // preempted by another meme. The sequence will restart // at the last completed meme. If a meme in the sequence has // the MEME_CHECKPOINT flag and it is executed, the sequence will resume at this meme. // MEME_INSTANT: Do not create this meme, if there are existing higher priority memes // MEME_IMMEDIATE: Create this meme but don't interrupt the current meme // MEME_CHILDREN: Allow multiple children to run, regardless of their return result. // Normally, only one successful child to runs. All other children are destroyed // once a children ends without returning FALSE via a call to MeSetMemeResult(); // If the meme ends without calling MeSetMemeResult(FALSE) the toolkit assumes // the child meme succeeds. // You can mask flags together, for example: MEME_RESUME | MEME_REPEAT object MeCreateSequence(string sName, int iPriority = 0, int iModifier = 0, int iFlags = 5 /*SEQ_REPEAT | SEQ_RESUME_LAST*/, object oGenerator = OBJECT_INVALID); // MeCreateSequenceMeme // Create a behavior that is defined in a file or library that will be used in a sequence. // The primary difference between this and MeCreateMeme is the new flag, MEME_CHECKPOINT, // described below: // // oSequence: The sequence this meme will belong to. // sName: The name of the meme, for example "i_wait"(This must corrsepond to a meme script or meme name, in a library.) // iPriority: The importance of this behavior: // PRIO_HIGH, PRIO_LOW, PRIO_MEDIUM, PRIO_HIGH, PRIO_VERYHIGH // The highest priority meme at a given moment will actually run. // iModifier: The value within a priority band, a number from -100 to 100. // iFlags: These control if the behavior of meme through its lifecycle. Some memes will // automatically turn on or off flags their own flags, once it is created. // MEME_CHECKPOINT: If this meme is part of a sequence, when the sequence resumes, // restart at this point. If this meme is not part of a sequence, this flag does nothing. // MEME_RESUME: if the meme is interrupted, auto resume the behavior, otherwise destroy it. // MEME_REPEAT: this causes the meme to loop, some memes *must* loop to work, like i_walkwp // MEME_INSTANT: do not create this meme, if there are existing higher priority memes // MEME_IMMEDIATE: create this meme but don't interrupt the current meme // MEME_CHILDREN: Allow multiple children to run, regardless of their return result. // Normally, only one successful child to runs. All other children are destroyed // once a children ends without returning FALSE via a call to MeSetMemeResult(); // If the meme ends without calling MeSetMemeResult(FALSE) the toolkit assumes // the child meme succeeds. // You can mask flags together, for example: MEME_RESUME | MEME_REPEAT // // Returns an object representing a sequence. Treat this like a template that can // be started and stopped over and over again. object MeCreateSequenceMeme(object oSequence, string sName, int iPriority = 2, int iModifier = 0, int iFlags = 0x10 /*MEME_RESUME*/); // ojbect MeStartSequence // Causes a sequence to run. Only one instance of the sequence may be running // at a given time -- don't call this function twice. // // Returns a meme that represents the sequence. The priority of the meme was defined // by the parameters used in the call to MeCreateSequence(). This object is a real // meme that can be reprioritized, destroyed, etc. object MeStartSequence(object oSequence); // MeStopSequence // Causes the sequence to stop executing, destroying the meme. The memes inside the // sequence are not destroyed. You can call // // oSequenceMeme: the meme returned to you by MeStartSequence() void MeStopSequence(object oSequenceMeme); // MeDestroySequence // Destroys the sequence. // // oSequence: the object returned from MeCreateSequence(). If this sequence is started, // it will be stopped before it's destroyed. void MeDestroySequence(object oSequence); // MeGetSequence // Returns a sequence with a given name. This is a sequence created by the NPC, the // name must match the string passed to MeCreateSequence(). This name doesn't correspond // to any script - it's just a name you make up. object MeGetSequence(string sName = ""); // MeCreateEvent // This creates an object that recieves and handles events. These are usually named // e_. It will not execute its code until you attach a trigger to the event // with MeAddTrigger*() functions. After the triggers are added the event should be // started with a call to MeStartEvent(). // // You can attach variables to this event object. When the event runs, it can access // this data through the use of the global variable MEME_SELF. // // sName: This is a name that matches a script like e_observe, or is properly // registered in a script library. // iFlags: These flags will determine when the event will be destroyed or // scheduled. Eventually this will be EVENT_REPEAT, for now only // EVENT_PERSISTANT is supported. object MeCreateEvent(string sName, int iFlag = 0x400 /* EVENT_REPEAT */); // MeStartEvent // This causes the triggers of the event to become active. It will now receive // signals or activate after the time trigger expires. void MeStartEvent(object oEvent); // MeStopEvent // This causes the event to stop processing signals, and invalidates the // scheduled time trigger. Any events created as a parent of a meme will cause // the childrent to deleted. // // oEvent: The event to be stopped. // iRemoveChildren: set of flags to signify which child objects to destroy: // TYPE_MEME: if the meme is interrupted, auto resume the behavior, otherwise destroy it. // TYPE_SEQUENCE: this causes the meme to loop, some memes *must* loop to work, like i_walkwp void MeStopEvent(object oEvent, int iRemoveMemes = 0x01 /* TYPE_MEME */); // MeDestroyEvent // This destroys an event and deallocates its data. It will unsubscribe from a given // channel. void MeDestroyEvent(object oEvent, int iRemoveMemes = 0x11 /* TYPE_MEME | TYPE_SEQUENCE */); // MeGetEvent // This gets an event with the given name or by count. // You can provide as many or as few of these parameters as you like to query the internal memetic store. // // sName: The name of the event, like "e_observe". // iIndex: The 0 based index of the event - if there are more than one with the same name. // If no name is provided, the iIndex matches all of them. object MeGetEvent(string sName = "", int Nth = 0); // MeAddTriggerTime // This causes the event to fire after a peroid of time. There can only be one // scheduled firing time at any given moment. The previous time is invalidated. // // eEvent: The event object, returned from MeCreateEvent(). // iDelay: The amount of time before the event fires. // isAbsoulteDelay: A boolean. If false, iDelay represents the game time when the event // should fire. If true, iDelay is the actual seconds it will wait. void MeAddTriggerTime(object oEvent, float iDelay, int isAbsoluteDelay = 1); // MeRemoveTriggerTime // Causes the event to not activate when the last trigger time was scheduled. void MeRemoveTriggerTime(object oEvent); // MeScheduleEvent // This is an internal function to the MemeticAI Toolkit. // // This causes the time trigger to be scheduled. It is automatically called // as a result of setting the trigger time. void MeScheduleEvent(object oEvent); // MeAddTriggerSignal // This registers a signal (a number) that will cause the the event to fire. // A signal can be limited to a private channel, defined by the channel string. // (Anything can send your NPC a signal with MeSendSignal(). If your NPC has an // event that is sensitive to the sigal sent, it will activate.) // // oEvent: The event object created by MeCreateEvent(); // iSignal: The signal number the event is sensitive to. // iDelay: The amount of time it will wait before it activates after getting the signal. // sChannel: The string name of the channel that the signal will come in on. // Anonymous channels are supported. void MeAddTriggerSignal(object oEvent, int iSignal = 9999999, float iDelay = 0.0, string sChannel = ""); // MeRemoveTriggerSignal // This causes the event to no longer listen for the signal, on the given channel. // // oEvent: The event object created by MeCreateEvent(); // iSignal: The signal number the event is sensitive to. // sChannel: The string name of the channel that the signal will come in on. void MeRemoveTriggerSignal(object oEvent, int iSignal, string sChannel = ""); // MeRemoveAllTriggers // This causes the event to no longer listen for any signals and will no longer // be activated by the time trigger. void MeRemoveAllTriggers(object oEvent); // MeIsEventScheduled // Checks to see if the event is sensitive to any signals or has a time trigger scheduled. int MeIsEventScheduled(object oEvent); // MeSendSignal // This sends a signal with optional data (i.e. a message) to yourself, or any // number of subscribers who are listening on a channel and are sensitive to your // signal integer. Every event that has a signal trigger that matches will execute. // If no channel is specified, the signal will be sent internally. // Remember: signal handling is independent of your action queue. Events should // never call Action*() functions; they should create memes or adjust // generators, which will create memes or reprioritize exisiting memes. void MeSendSignal(int iSignal, string sMsg = "", object oData = OBJECT_INVALID, int iData = 0, string sChannel = ""); // MeGetEventChannel // If this event was triggered by a signal on a channel, this function returns // the name of that channel. If not, it returns NULL. // // Note: This function should only be called within an event script, like e_observer_go. string MeGetEventChannel(); // MeGetEventData // If this event was triggered by a signal, it may receive data in the form of an // object reference. This function returns that object data. // // Note: This function should only be called within an event script, like e_observer_go. object MeGetEventData(); // MeGetEventInt // If this event was triggered by a signal, it may receive data in the form of an // integer. This function returns that int data. // // Note: This function should only be called within an event script, like e_observer_go. int MeGetEventInt(); // MeGetEventMsg // If this event was triggered by a signal, it may receive data in the form of an // string message. This function returns that string data. // // Note: This function should only be called within an event script, like e_observer_go. string MeGetEventMsg(); // MeGetEventSender // If this event was triggered by a signal, it was sent from a source object, // possible OBJECT_SELF. This function returns who sent the signal. // // Note: This function should only be called within an event script, like e_observer_go. object MeGetEventSender(); // MeGetEventSignal // If this event was triggered by a signal, this function returns the signal // number that triggered the event. If was triggered by a timer event, this value // is zero. // // Note: This function should only be called within an event script, like e_observer_go. int MeGetEventSignal(); // MeExecuteGenerators // This is an internal function to the MemeticAI Toolkit. void MeExecuteGenerators(string sSuffix); // MeComputeBestMeme // This is an internal function to the MemeticAI Toolkit. void MeComputeBestMeme(object oMeme = OBJECT_INVALID); // MeUpdateActions // This causes the highest priority meme to become active. // // This is the primary function for starting the memetic behavior of an NPC. // Normally this is used in an onSpawn script for the creature after a series of // memes or generators have been created. Multiple calls to this function will // not break the flow of the normal behavior. // // Generator scripts (g_) do not normally need to call this function. // It is called immediately after all generators are executed. // // This function should be called after a meme is reprioritized. void MeUpdateActions(); // MeResetSystem // // This is called to jump start a stall memetic NPC. This usually happens if // a script calls ClearAllActions. It will also cancel any current conversation // because it calls ClearAllActions and restarts the active meme. void MeRestartSystem(); // MePauseSystem // // This causes the NPC to stop behaving memetically.*** void MePauseSystem(); // MeExecuteEvent // This is an internal function to the MemeticAI Toolkit. // // This causes an event to be processed, executing an e_ event script. void MeExecuteEvent(object oEvent, int iTimeID, int iSignal = 0, string sMessage = "", object oData = OBJECT_INVALID, int iData = 0, string sChannel = "", object oSource = OBJECT_INVALID); // MeModuleBroadcast // This is an internal function to the MemeticAI Toolkit. // // This attaches information to a module and sends does an AssignAction on each // of the NPCs subscribed to the channel. void MeModuleBroadcast(int iSignal, string sMessage, object oData, int iData, string sChannel, object oObject); // MeProcessSignals // This is an internal function to the MemeticAI Toolkit. // // This is the compainion to MeModuleBroadcast, where the NPC internally handles // message with the accompanying data. void MeProcessSignals(int iEventNum, string sMessage, object oData, int iData, string sSourceChannel, object oSourceObject); // MeHasScheduledMeme // This is an internal function to the MemeticAI Toolkit. // // This function checks to see if there is a meme with *at least* the given priority // and modifier. It is used to see if memes with the MEME_INSTANT flag are allowed to run. int MeHasScheduledMeme(int iPriority, int iModifier); // -- Implementation ----------------------------------------------------------- // ------ Generator Functions--------------------------------------------------- object MeCreateGenerator(string sName, int iPriority = 0, int iModifier = 0, int iFlags = 0) { _Start("MeCreateGenerator name='"+sName+"' priority='"+IntToString(iPriority)+"' modifier = '"+IntToString(iModifier)+"'", DEBUG_TOOLKIT); object oGenerator = OBJECT_INVALID; object oBag = OBJECT_INVALID; if (!GetIsObjectValid(NPC_SELF)) MeInit(); oBag = GetLocalObject(NPC_SELF, "MEME_GeneratorBag"); oGenerator = _MeMakeObject(oBag, sName, TYPE_GENERATOR); SetLocalInt(oGenerator, "MEME_Priority", iPriority); SetLocalInt(oGenerator, "MEME_Modifier", iModifier); SetLocalInt(oGenerator, "MEME_Flags", iFlags); MeExecuteScript(sName,"_ini", OBJECT_SELF, oGenerator); _End("MeCreateGenerator", DEBUG_TOOLKIT); return oGenerator; } void MeStartGenerator(object oGenerator) { _Start("MeStartGenerator name = '"+GetLocalString(oGenerator, "Name")+"'", DEBUG_TOOLKIT); SetLocalInt(oGenerator, "MEME_Active", 1); _End("MeStartGenerator", DEBUG_TOOLKIT); } void MeStopGenerator(object oGenerator, int iRemoveMemes = 0x01) { _Start("MeStopGenerator name = '"+GetLocalString(oGenerator, "Name")+"'", DEBUG_TOOLKIT); SetLocalInt(oGenerator, "MEME_Active", 0); int i = 0; object oMeme = OBJECT_INVALID; int count; if (iRemoveMemes & TYPE_MEME) { count = MeGetObjectCount(oGenerator, "ChildMeme"); _PrintString("Destroying the meme's created by this generator."); for (i = count - 1; i >= 0; i--) { oMeme = MeGetObjectByIndex(oGenerator, i, "ChildMeme"); MeDestroyMeme(oMeme); } } if (iRemoveMemes & TYPE_SEQUENCE) { count = MeGetObjectCount(oGenerator, "ChildSequence"); _PrintString("Destroying the sequence templates's created by this generator."); for (i = count - 1; i >= 0; i--) { oMeme = MeGetObjectByIndex(oGenerator, i, "ChildSequence"); MeDestroySequence(oMeme); } } _End("MeStopGenerator", DEBUG_TOOLKIT); } void MeDestroyGenerator(object oGenerator, int iRemoveMemes = 0x11) { _Start("MeDestroyGenerator name = '"+GetLocalString(oGenerator, "Name")+"'", DEBUG_TOOLKIT); object oBag = OBJECT_INVALID; object oMeme = OBJECT_INVALID; int count; int i; if (!GetIsObjectValid(NPC_SELF)) MeInit(); oBag = GetLocalObject(NPC_SELF, "MEME_GeneratorBag"); if (iRemoveMemes) { if (iRemoveMemes & TYPE_MEME) { _PrintString("Destroying the meme's created by this generator."); count = MeGetObjectCount(oGenerator, "ChildMeme"); for (i=0; i < count; i++) { oMeme = MeGetObjectByIndex(oGenerator, i, "ChildMeme"); MeDestroyMeme(oMeme); } } if (iRemoveMemes & TYPE_SEQUENCE) { count = MeGetObjectCount(oGenerator, "SequenceMeme"); _PrintString("Destroying the sequence templates's created by this generator."); for (i=0; i < count; i++) { oMeme = MeGetObjectByIndex(oGenerator, i, "SequenceMeme"); MeDestroyMeme(oMeme); } } } else { _PrintString("Diassociating this generator from the memes it created."); for (i=0; i < count; i++) { oMeme = MeGetObjectByIndex(oGenerator, i, "ChildMeme"); DeleteLocalObject(oMeme, "MEME_Generator"); } } _MeRemoveObject(oBag, oGenerator); _Start("MeDestroyGenerator", DEBUG_TOOLKIT); } object MeGetGenerator(string sName = "", int Nth = 0) { _Start("MeGetGenerator name = '"+sName+"' Nth = '"+IntToString(Nth)+"'", DEBUG_TOOLKIT); object oGenerator = OBJECT_INVALID; object oBag = OBJECT_INVALID; object oMeme = OBJECT_INVALID; int i = 0; int count = 0; if (!GetIsObjectValid(NPC_SELF)) MeInit(); oBag = GetLocalObject(NPC_SELF, "MEME_GeneratorBag"); while(1) { oMeme = MeGetObjectByIndex(oBag, i, "Meme"); if (sName == "" || GetLocalString(oMeme, "Name") == sName) { if (count == Nth) { _End("MeGetGenerator", DEBUG_TOOLKIT); return oMeme; } count++; } if (!GetIsObjectValid(oMeme)) break; i++; } _End("MeGetGenerator", DEBUG_TOOLKIT); return OBJECT_INVALID; } object MeGetChildMeme(object oTarget, int Nth = 0) { _Start("MeGetChildMeme name = '"+GetLocalString(oTarget, "Name")+"' Nth = '"+IntToString(Nth)+"'", DEBUG_TOOLKIT); object oMeme = OBJECT_INVALID; int i = 0; int count = 0; while(1) { oMeme = MeGetObjectByIndex(oTarget, i, "ChildMeme"); if (count == Nth) { _End("MeGetChildMeme"); return oMeme; } count++; if (!GetIsObjectValid(oMeme)) break; i++; } _End("MeGetChildMeme", DEBUG_TOOLKIT); return OBJECT_INVALID; } // ----- Memes Functions ------------------------------------------------------- void MeSuspendMeme(object oMeme, int bCallBrk = 1) { _Start("MeSuspendMeme meme='"+_GetName(oMeme)+"'", DEBUG_TOOLKIT); // Only suspend an active meme. if (GetLocalInt(oMeme, "MEME_Suspended")) { _End("MeSuspendMeme", DEBUG_TOOLKIT); return; } SetLocalInt(oMeme, "MEME_Suspended", 1); int iPriority = GetLocalInt(oMeme, "MEME_Priority"); object oPrioBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); object oSuspendBag = GetLocalObject(NPC_SELF, "MEME_SuspendBag"); _MeMoveObject(oPrioBag, oMeme, oSuspendBag); if (GetLocalObject(NPC_SELF, "MEME_ActiveMeme") == oMeme) { if (bCallBrk) { ClearAllActions(); MeExecuteScript(GetLocalString(oMeme, "Name"),"_brk", OBJECT_SELF, oMeme); } } MeComputeBestMeme(oMeme); _End("MeSuspendMeme", DEBUG_TOOLKIT); } // This function is fairly equivalent to repriotizing a meme; it places // the meme into the appropriate bag and computes the best meme void MeResumeMeme(object oMeme) { // Only resume a suspended meme. if (GetLocalInt(oMeme, "MEME_Suspended") == 0) return; // Don't forget, there is a MEME_Parent _Start("MeResumeMeme", DEBUG_TOOLKIT); SetLocalInt(oMeme, "MEME_Suspended", 0); int iPriority = GetLocalInt(oMeme, "MEME_Priority"); object oPrioBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); object oSuspendBag = GetLocalObject(NPC_SELF, "MEME_SuspendBag"); _MeMoveObject(oSuspendBag, oMeme, oPrioBag); MeComputeBestMeme(oMeme); MeUpdateActions(); _End("MeResumeMeme", DEBUG_TOOLKIT); } int MeIsMemeSuspended(object oMeme) { return GetLocalInt(oMeme, "MEME_Suspended"); } object MeCreateMeme(string sName, int iPriority = 2, int iModifier = 0, int iFlags = 0x10 /*MEME_RESUME*/, object oParent = OBJECT_INVALID) { _Start("MeCreateMeme name = '"+sName+"' priority = '"+IntToString(iPriority)+"' modifier = '"+IntToString(iModifier)+"'", DEBUG_TOOLKIT); object oPrioBag, oMeme; int iParentType = GetLocalInt(oParent, "MEME_Type"); if (!GetIsObjectValid(NPC_SELF)) MeInit(); if (!GetIsObjectValid(NPC_SELF)) _PrintString("Assert: This is not possible", DEBUG_TOOLKIT); // If you pass PRIO_DEFAULT you will copy the priority of the parent if (GetIsObjectValid(oParent) && iPriority == PRIO_DEFAULT) { iPriority = GetLocalInt(oParent, "MEME_Priority"); iModifier = GetLocalInt(oParent, "MEME_Modifier"); } if (iModifier < -100) iModifier = -100; else if (iModifier > 100) iModifier = 100; if (iPriority == PRIO_DEFAULT) iPriority = PRIO_MEDIUM; if ((iFlags & MEME_INSTANT) && (MeHasScheduledMeme(iPriority, iModifier))) { _PrintString("Another meme is higher than you, this meme won't get created.", DEBUG_TOOLKIT); _End("MeCreateMeme", DEBUG_TOOLKIT); // Eventually a reason can be spoken, if necessary. return OBJECT_INVALID; } // Get the bag which represents the priority slot oPrioBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); if (!GetIsObjectValid(oPrioBag)) _PrintString("Assert: cannot find priobag.", DEBUG_TOOLKIT); // Create a meme at the given priority slot if (sName == "i_sequence") oMeme = _MeMakeObject(oPrioBag, sName, TYPE_SEQ_REF); else oMeme = _MeMakeObject(oPrioBag, sName, TYPE_MEME); if (!GetIsObjectValid(oMeme)) _PrintString("Assert: did not make meme.", DEBUG_TOOLKIT); if (oParent != OBJECT_INVALID) { // If this is the first child meme, clear the stale result value. if (MeGetObjectCount(oParent, "ChildMeme") == 0) SetLocalInt(oParent,"MEME_Result", 0); if (iParentType == TYPE_GENERATOR || iParentType == TYPE_EVENT) { MeAddObjectRef(oParent, oMeme, "ChildMeme"); SetLocalObject(oMeme, "MEME_Generator", oParent); } // If the parent is a meme -- or a sequence meme // Child memes always suspend their parent, until all children are destroyed. else if ((iParentType == TYPE_MEME) || (iParentType == TYPE_SEQ_REF)) { MeAddObjectRef(oParent, oMeme, "ChildMeme"); SetLocalObject(oMeme, "MEME_Parent", oParent); // Immediately stop the parent, do not call its _brk callback. // There is no real reason why I don't call _brk -- it's left here as an // option to toggle back, if the it turns out that it makes more sense. MeSuspendMeme(oParent, FALSE); } } SetLocalInt(oMeme, "MEME_Priority", iPriority); SetLocalInt(oMeme, "MEME_Modifier", iModifier); SetLocalInt(oMeme, "MEME_Flags", iFlags); MeExecuteScript(sName,"_ini", OBJECT_SELF, oMeme); _PrintString("Executing "+sName+"_ini", DEBUG_TOOLKIT); MeComputeBestMeme(oMeme); _End("MeCreateMeme", DEBUG_TOOLKIT); return oMeme; } int MeGetPriority(object oMeme) { return GetLocalInt(oMeme, "MEME_Priority"); } int MeGetModifier(object oMeme) { return GetLocalInt(oMeme, "MEME_Modifier"); } void _MeDestroyMeme(object oMeme, int iCallEndScript = 1, int iComputeBestMeme = 1, int iDestroySiblings = 1); void _MeDestroyMeme(object oMeme, int iCallEndScript = 1, int iComputeBestMeme = 1, int iDestroySiblings = 1) { _Start("MeDestroyMeme name = '"+GetLocalString(oMeme, "Name")+"'", DEBUG_TOOLKIT); object oPrioBag; int iPriority; iPriority = GetLocalInt(oMeme, "MEME_Priority"); oPrioBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); if (iCallEndScript) { _PrintString("Running destructor script for meme, "+GetLocalString(oMeme,"Name")+".", DEBUG_TOOLKIT); MeExecuteScript(GetLocalString(oMeme, "Name"),"_end", OBJECT_SELF, oMeme); } // Object is not actually destroyed until after this script runs. // Note this is an oddity of NWScript -- more than likely to allow for // visual animations and sloppy code -- whatever gets a product out the // door and on the shelves, right? _MeRemoveObject(oPrioBag, oMeme); // If there is a parent, possibly resume the suspended parent object oParent = GetLocalObject(oMeme, "MEME_Parent"); if (GetIsObjectValid(oParent)) { _PrintString("This meme is a child!", DEBUG_TOOLKIT); // Evaluate and use MEME_Result MeRemoveObjectRef(oParent, oMeme, "ChildMeme"); // If there are no more children, this meme can be resume. if (MeGetObjectCount(oParent, "ChildMeme") == 0) { _PrintString("That was the last child, resuming the parent "+GetLocalString(oParent, "Name"), DEBUG_TOOLKIT); // No more children, let them run. MeResumeMeme(oParent); } else { _PrintString("This meme has siblings...", DEBUG_TOOLKIT); // If MEME_CHILDREN is set, then we don't care about the return // result. Otherwise, destroy the children when one succeeds. if (MeGetMemeFlag(oMeme, MEME_CHILDREN) == 0) { // Ok, in this case, 0 is pass, 1 is fail. // This logic is reversed because we want memes to succeed by // default. So 0 is the default value of a NWScript variable. // You must explicitly call MeSetResult(FALSE) to fail and let // a meme's siblings execute. if ((GetLocalInt(oParent, "MEME_Result") == 0) && iDestroySiblings) { _PrintString("This meme succeeded, we don't need its siblings.", DEBUG_TOOLKIT); int count = MeGetObjectCount(oParent, "ChildMeme"); object oSibling; while (count) { oSibling = MeGetObjectByIndex(oParent, 0, "ChildMeme"); MeRemoveObjectByIndex(oParent, 0, "ChildMeme"); _MeDestroyMeme(oSibling, 0, 0, 0); count--; } _PrintString("Resuming parent "+GetLocalString(oParent, "Name"), DEBUG_TOOLKIT); MeResumeMeme(oParent); } else _PrintString("This meme didn't succeed, one of the other siblings will get its chance.", DEBUG_TOOLKIT); } else _PrintString("I'm supposed to play nice and let my siblings go.", DEBUG_TOOLKIT); } } // If there is a generator, possibly detach from the generator's list object oGenerator = GetLocalObject(oMeme, "MEME_Generator"); if (GetIsObjectValid(oGenerator)) { MeRemoveObjectRef(oGenerator, oMeme, "ChildMeme"); } SetLocalObject(NPC_SELF, "MEME_ActiveMeme", OBJECT_INVALID); SetLocalObject(NPC_SELF, "MEME_PendingMeme", OBJECT_INVALID); if (iComputeBestMeme) MeComputeBestMeme(); _End("MeDestroyMeme", DEBUG_TOOLKIT); } void MeDestroyMeme(object oMeme) { _MeDestroyMeme(oMeme); } object MeGetActiveMeme() { return GetLocalObject(NPC_SELF, "MEME_ActiveMeme"); } object MeGetPendingMeme() { return GetLocalObject(NPC_SELF, "MEME_PendingMeme"); } object MeGetMeme(string sName = "", int Nth = 0, int iPriority = 0) { _Start("MeGetMeme name = '"+sName+"' Nth = '"+IntToString(Nth)+"'", DEBUG_UTILITY); object oBag = OBJECT_INVALID; object oMeme = OBJECT_INVALID; int i, j, nth; int count = 0; nth = 0; for (i = PRIO_VERYHIGH; i >= PRIO_NONE; i--) { if (iPriority == 0 || iPriority == i) { oBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(i)); count = MeGetObjectCount(oBag); oMeme = MeGetObjectByIndex(oBag, 0); for (j = 0; j < count; j++) { _PrintString("Looking at '"+GetLocalString(oMeme, "Name")+"' meme, number "+IntToString(j)+" in PrioBag"+IntToString(i)+".",DEBUG_UTILITY); if (sName == "" || GetLocalString(oMeme, "Name") == sName) { if (nth == Nth) { _End("MeGetMeme", DEBUG_UTILITY); return oMeme; } nth++; } oMeme = MeGetObjectByIndex(oBag, j); } } } _End("MeGetMeme", DEBUG_UTILITY); return OBJECT_INVALID; } // ************* Lucullo fix ************** // this is an internal function that replaces cb_done void _MemeDone(object oActiveMeme, int iRunInstance) { if (!GetIsObjectValid(oActiveMeme)) return; string sName = GetLocalString(oActiveMeme, "Name"); _Start("_MemeDone meme = '"+sName+"' run='"+IntToString(iRunInstance)+"'", DEBUG_TOOLKIT); // since we are not a main script, reload register for safety NPC_SELF = GetLocalObject (OBJECT_SELF, "MEME_NPCSelf"); if (GetLocalInt(NPC_SELF, "MEME_Paused")) { _PrintString("NPC is paused", DEBUG_TOOLKIT); _End("_MemeDone", DEBUG_TOOLKIT); return; } if (oActiveMeme != GetLocalObject(NPC_SELF, "MEME_ActiveMeme") || iRunInstance != GetLocalInt(oActiveMeme, "MEME_RunCount")) { _PrintString("We have been overrun!", DEBUG_TOOLKIT); _End("_MemeDone", DEBUG_TOOLKIT); return; } _PrintString("Notifying meme it has completed.", DEBUG_TOOLKIT); MeExecuteScript(sName,"_end", OBJECT_SELF, oActiveMeme); if (MeGetMemeFlag(oActiveMeme, MEME_REPEAT)) { if (GetIsObjectValid(GetLocalObject(NPC_SELF, "MEME_PendingMeme"))) { MeUpdateActions(); } else { MeExecuteScript(sName,"_go", OBJECT_SELF, oActiveMeme); iRunInstance ++; SetLocalInt(oActiveMeme, "MEME_RunCount", iRunInstance); _PrintString("Meme = "+sName+" next run ="+IntToString(iRunInstance), DEBUG_TOOLKIT); ActionDoCommand(ActionDoCommand(DelayCommand(0.0, _MemeDone(oActiveMeme, iRunInstance)))); } _End("_MemeDone", DEBUG_TOOLKIT); return; } _PrintString("Meme completed, destroying.", DEBUG_TOOLKIT); _MeDestroyMeme(oActiveMeme, 0); // Internal version doesn't call _end script. MeUpdateActions(); _End("_MemeDone", DEBUG_TOOLKIT); return; } void _MeRestartMeme(object oMeme, int bCallInit, int iTimeStamp) { if (iTimeStamp) { if (GetLocalInt(oMeme, "MEME_TimeStamp") != iTimeStamp) return; } _Start("MeRestartMeme name='"+_GetName(oMeme)+"'", DEBUG_UTILITY); int iTimeStamp = GetLocalInt(oMeme, "MEME_TimeStamp") + 1; SetLocalInt(oMeme, "MEME_TimeStamp", iTimeStamp); if (GetLocalObject(NPC_SELF, "MEME_ActiveMeme") == oMeme) { ClearAllActions(); MeExecuteScript(GetLocalString(oMeme, "Name"),"_brk", OBJECT_SELF, oMeme); if (bCallInit) MeExecuteScript(GetLocalString(oMeme, "Name"),"_ini", OBJECT_SELF, oMeme); MeExecuteScript(GetLocalString(oMeme, "Name"),"_go", OBJECT_SELF, oMeme); // ************* Lucullo fix ************** int iRunCount = GetLocalInt(oMeme, "MEME_RunCount") + 1; SetLocalInt(oMeme, "MEME_RunCount", iRunCount); _PrintString("Meme = "+GetLocalString(oMeme, "Name")+" next run ="+IntToString(iRunCount), FALSE, DEBUG_TOOLKIT); ActionDoCommand(ActionDoCommand(DelayCommand(0.0, _MemeDone(oMeme, iRunCount)))); } _End("MeRestartMeme", DEBUG_UTILITY); } void MeRestartMeme(object oMeme, int bCallInit = 0, float fDelay = 0.0) { _Start("MeRestartMeme name='"+_GetName(oMeme)+"'", DEBUG_UTILITY); if (fDelay > 0.0) { int iTimeStamp = GetLocalInt(oMeme, "MEME_TimeStamp") + 1; SetLocalInt(oMeme, "MEME_TimeStamp", iTimeStamp); DelayCommand(fDelay, _MeRestartMeme(oMeme, bCallInit, iTimeStamp)); } else { _MeRestartMeme(oMeme, bCallInit, 0); } _End("MeRestartMeme", DEBUG_UTILITY); } void _MeStopMeme(object oMeme, int iTimeStamp) { if (iTimeStamp) { if (GetLocalInt(oMeme, "MEME_TimeStamp") != iTimeStamp) return; } _Start("MeStopMeme name='"+_GetName(oMeme)+"'", DEBUG_UTILITY); int iTimeStamp = GetLocalInt(oMeme, "MEME_TimeStamp") + 1; SetLocalInt(oMeme, "MEME_TimeStamp", iTimeStamp); if (GetLocalObject(NPC_SELF, "MEME_ActiveMeme") == oMeme) { ClearAllActions(); MeExecuteScript(GetLocalString(oMeme, "Name"),"_brk", OBJECT_SELF, oMeme); // ************* Lucullo fix ************** // ExecuteScript("cb_done", OBJECT_SELF); int iRunCount = GetLocalInt(oMeme, "MEME_RunCount") + 1; SetLocalInt(oMeme, "MEME_RunCount", iRunCount); _MemeDone(oMeme, iRunCount); } _End("MeStopMeme", DEBUG_UTILITY); } void MeStopMeme(object oMeme, float fDelay = 0.0) { _Start("MeStopMeme name='"+_GetName(oMeme)+"'", DEBUG_UTILITY); if (fDelay > 0.0) { int iTimeStamp = GetLocalInt(oMeme, "MEME_TimeStamp") + 1; SetLocalInt(oMeme, "MEME_TimeStamp", iTimeStamp); DelayCommand(fDelay, _MeStopMeme(oMeme, iTimeStamp)); } else { _MeStopMeme(oMeme, 0); } _End("MeStopMeme", DEBUG_UTILITY); } object MeGetParentGenerator(object oMeme) { _Start("MeGetParentGenerator", DEBUG_UTILITY); _End("MeGetParentGenerator", DEBUG_UTILITY); return GetLocalObject(oMeme, "MEME_Generator"); } object MeGetParentMeme(object oMeme) { _Start("MeGetParentMeme", DEBUG_UTILITY); _End("MeGetParentMeme", DEBUG_UTILITY); return GetLocalObject(oMeme, "MEME_Parent"); } void MeSetMemeResult(int iResult, object oMeme = OBJECT_INVALID) { if (oMeme == OBJECT_INVALID) oMeme = GetLocalObject (OBJECT_SELF, "MEME_ObjectSelf"); object oParent = MeGetParentMeme(oMeme); // NOTICE -- I set 0 as TRUE, 1 as FALSE // Why? This allows me to have a default value of 0 as a success. SetLocalInt(oParent, "MEME_Result", !iResult); } void MeSetPriority(object oTarget, int iPriority, int iModifier = 0, int iPropogate = 0) { _Start("MeSetPriority name = '"+GetLocalString(oTarget, "Name")+"' priority = '"+IntToString(iPriority)+"' modifier = '"+IntToString(iModifier)+"'", DEBUG_TOOLKIT); object oActive, oPending; object oBag, oNewBag; int iOldPriority; int i, count; object oMeme; // Changing the priority of a suspended meme is easy. if (GetLocalInt(oTarget, "MEME_Suspended") == 1) { SetLocalInt(oTarget, "MEME_Priority", iPriority); SetLocalInt(oTarget, "MEME_Modifier", iModifier); } else { iOldPriority = GetLocalInt(oTarget, "MEME_Priority"); if (iOldPriority != iPriority) { oBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iOldPriority)); oNewBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); SetLocalInt(oTarget, "MEME_Priority", iPriority); if (!GetIsObjectValid(NPC_SELF)) _PrintString("ASSERT: NPC_SELF is invalid!", DEBUG_TOOLKIT); if (!GetIsObjectValid(oBag)) _PrintString("ASSERT: Bag ("+IntToString(iOldPriority)+") is invalid!", DEBUG_TOOLKIT); if (!GetIsObjectValid(oNewBag)) _PrintString("ASSERT: New bag ("+IntToString(iPriority)+") is invalid!", DEBUG_TOOLKIT); if (!GetIsObjectValid(oTarget)) _PrintString("ASSERT: meme is invalid!", DEBUG_TOOLKIT); _MeMoveObject(oBag, oTarget, oNewBag); if (oActive == oTarget) SetLocalObject(NPC_SELF, "MEME_ActiveMeme", OBJECT_INVALID); if (oPending == oTarget) SetLocalObject(NPC_SELF, "MEME_PendingMeme", OBJECT_INVALID); } SetLocalInt(oTarget, "MEME_Modifier", iModifier); } if (iPropogate) { oActive = GetLocalObject(NPC_SELF, "MEME_ActiveMeme"); oPending = GetLocalObject(NPC_SELF, "MEME_PendingMeme"); count = MeGetObjectCount(oTarget, "ChildMeme"); for (i = 0; i < count; i++) { oMeme = MeGetObjectByIndex(oTarget, i, "ChildMeme"); _PrintString("Propogating priority change to meme "+IntToString(i)+" ("+GetLocalString(oMeme,"Name")+").",DEBUG_TOOLKIT); MeSetPriority(oMeme, iPriority, iModifier, iPropogate); } } MeComputeBestMeme(oMeme); _End("MeSetPriority", DEBUG_TOOLKIT); } // ----- Sequences ------------------------------------------------------------- object MeCreateSequence(string sName, int iPriority = 0, int iModifier = 0, int iFlags = 5, object oGenerator = OBJECT_INVALID) { _Start("MeCreateSequence name='"+sName+"' priority='"+IntToString(iPriority)+"' modifier = '"+IntToString(iModifier)+"'", DEBUG_TOOLKIT); object oSequence = OBJECT_INVALID; object oBag = OBJECT_INVALID; if (!GetIsObjectValid(NPC_SELF)) MeInit(); oBag = GetLocalObject(NPC_SELF, "MEME_Sequence_"+sName); if (GetIsObjectValid(oBag)) { _PrintString("Sequence already exists!", DEBUG_TOOLKIT); _End("MeCreateSequence", DEBUG_TOOLKIT); return OBJECT_INVALID; } oSequence = _MeMakeObject(NPC_SELF, sName, TYPE_SEQUENCE); SetLocalObject(NPC_SELF, "MEME_Sequence_"+sName, oSequence); // adopt the priority of the generator if it is passed. if (GetIsObjectValid(oGenerator)) { MeAddObjectRef(oGenerator, oSequence, "ChildSequence"); SetLocalInt(oSequence, "MEME_Priority", GetLocalInt(oGenerator, "MEME_Priority")); SetLocalInt(oSequence, "MEME_Modifier", GetLocalInt(oGenerator, "MEME_Modifier")); SetLocalObject(oSequence, "MEME_Generator", oGenerator); } else { SetLocalInt(oSequence, "MEME_Priority", iPriority); SetLocalInt(oSequence, "MEME_Modifier", iModifier); } if (iFlags & MEME_REPEAT) iFlags |= SEQ_REPEAT; iFlags |= MEME_REPEAT; if (iFlags & (SEQ_RESUME_FIRST | SEQ_RESUME_LAST)) iFlags |= MEME_RESUME; SetLocalInt(oSequence, "MEME_Flags", iFlags); _End("MeCreateSequence", DEBUG_TOOLKIT); return oSequence; } object MeCreateSequenceMeme(object oSequence, string sName, int iPriority = 2, int iModifier = 0, int iFlags = 0x10) { _Start("MeCreateSequenceMeme name = '"+sName+"' priority = '"+IntToString(iPriority)+"' modifier = '"+IntToString(iModifier)+"'", DEBUG_TOOLKIT); object oMeme; if (!GetIsObjectValid(oSequence)) { _PrintString("No valid sequence given!", DEBUG_TOOLKIT); _End("MeCreateSequenceMeme", DEBUG_TOOLKIT); return OBJECT_INVALID; } if (!GetIsObjectValid(NPC_SELF)) MeInit(); if (iPriority == PRIO_DEFAULT) { iPriority = GetLocalInt(oSequence, "MEME_Priority"); if (iPriority) iModifier = GetLocalInt(oSequence, "MEME_Modifier"); else { iPriority = PRIO_MEDIUM; iModifier = 0; } } if (iModifier < -100) iModifier = -100; else if (iModifier > 100) iModifier = 100; oMeme = _MeMakeObject(oSequence, sName, TYPE_MEME); SetLocalInt(oMeme, "MEME_Priority", iPriority); SetLocalInt(oMeme, "MEME_Modifier", iModifier); SetLocalInt(oMeme, "MEME_Flags", iFlags); SetLocalObject(oMeme, "MEME_Sequence", oSequence); _End("MeCreateSequenceMeme", DEBUG_TOOLKIT); return oMeme; } object MeStartSequence(object oSequence) { _Start("MeStartSequence", DEBUG_TOOLKIT); object oSeqRef = GetLocalObject(oSequence, "MEME_SequenceRef"); if (GetIsObjectValid(oSeqRef)) { _PrintString("Sequence already running!", DEBUG_TOOLKIT); _End("MeStartSequence", DEBUG_TOOLKIT); return oSeqRef; } oSeqRef = MeCreateMeme("i_sequence", GetLocalInt(oSequence, "MEME_Priority"), GetLocalInt(oSequence, "MEME_Modifier"), GetLocalInt(oSequence, "MEME_Flags"), GetLocalObject(oSequence, "MEME_Generator")); SetLocalObject(oSeqRef,"MEME_SequenceName", GetLocalObject(oSequence, "Name")); SetLocalObject(oSeqRef,"MEME_Sequence", oSequence); SetLocalObject(oSequence,"MEME_SequenceRef", oSeqRef); _End("MeStartSequence", DEBUG_TOOLKIT); return oSeqRef; } void MeStopSequence(object oTarget) { _Start("MeStopSequence", DEBUG_UTILITY); object oObject; int iType = GetLocalInt(oTarget, "MEME_Type"); if (iType == TYPE_SEQUENCE) { oObject = GetLocalObject(oTarget, "MEME_SequenceRef"); if (GetIsObjectValid(oObject)) { DeleteLocalObject(oTarget, "MEME_SequenceRef"); MeDestroyMeme(oObject); } } else if (iType == TYPE_SEQ_REF) { oObject = GetLocalObject(oTarget, "MEME_Sequence"); DeleteLocalObject(oObject, "MEME_SequenceRef"); MeDestroyMeme(oTarget); } _End("MeStopSequence", DEBUG_UTILITY); } void MeDestroySequence(object oTarget) { _Start("MeDestroySequence", DEBUG_UTILITY); object oSequence, oSequenceRef, oObject; int i = 0; int iType = GetLocalInt(oTarget, "MEME_Type"); if (iType == TYPE_SEQUENCE) { oSequence = oTarget; oSequenceRef = GetLocalObject(oTarget, "MEME_SequenceRef"); } else if (iType == TYPE_SEQ_REF) { oSequenceRef = oTarget; oSequence = GetLocalObject(oTarget, "MEME_Sequence"); } else { _End("MeDestroySequence", DEBUG_UTILITY); return; } MeDestroyMeme(oSequenceRef); object oGenerator = GetLocalObject(oSequence, "MEME_Generator"); if (GetIsObjectValid(oGenerator)) { MeRemoveObjectRef(oGenerator, oSequence, "ChildSequence"); } for (i = MeGetObjectCount(oSequence) - 1; i >= 0; i--) { oObject = MeGetObjectByIndex(oSequence, i); DestroyObject(oObject); } DestroyObject(oSequence); _End("MeDestroySequence", DEBUG_UTILITY); } object MeGetSequence(string sName = "") { _Start("MeGetSequence", DEBUG_UTILITY); _End("MeGetSequence", DEBUG_UTILITY); return GetLocalObject(NPC_SELF,"MEME_Sequence_"+sName); } // ----- Events ---------------------------------------------------------------- object MeCreateEvent(string sName, int iFlags=0x400) { _Start("MeCreateEvent", DEBUG_TOOLKIT); object oResult, oBag; if (!GetIsObjectValid(NPC_SELF)) MeInit(); oBag = GetLocalObject(NPC_SELF, "MEME_EventBag"); if (GetIsObjectValid(oBag)) _PrintString("Got the event bag.", DEBUG_UTILITY); oResult = _MeMakeObject(oBag, sName, TYPE_EVENT); MeSetMemeFlag(oResult, iFlags); _End("MeCreateEvent", DEBUG_TOOLKIT); return oResult; } void func2(object oTarget, object oEvent, int iTimeID) { MeExecuteEvent(oEvent, iTimeID); } void func1(object oTarget, object oEvent, int iTimeID, float iDelay) { DelayCommand(iDelay, AssignCommand(oTarget, func2(oTarget, oEvent, iTimeID))); } void MeScheduleEvent(object oEvent) { _Start("MeScheduleEvent", DEBUG_TOOLKIT); _PrintString("Has a time schedule, getting it to go.", DEBUG_TOOLKIT); int iTimeID; float iDelay; if (!GetLocalInt(oEvent, "MEME_Active")) { _End("MeScheduleEvent", DEBUG_TOOLKIT); return; } iTimeID = GetLocalInt(oEvent, "MEME_TimeIndex"); iTimeID++; SetLocalInt(oEvent, "MEME_TimeIndex", iTimeID); iDelay = GetLocalFloat(oEvent, "MEME_TimeDelay"); _PrintString("Raw time is: "+FloatToString(iDelay), DEBUG_TOOLKIT); if (!GetLocalInt(oEvent, "MEME_TimeDelayType")) { iDelay -= MeGetCurrentGameTime(); if (iDelay <= 0.0f) iDelay += MeGameHours(24); // bump to next day } object oOBJECT_SELF = OBJECT_SELF; AssignCommand(GetModule(), func1(oOBJECT_SELF, oEvent, iTimeID, iDelay)); _End("MeScheduleEvent", DEBUG_TOOLKIT); } void MeStartEvent(object oEvent) { _Start("MeStartEvent", DEBUG_TOOLKIT); int iTimeID; float iDelay; string sName; if (!GetLocalInt(oEvent, "MEME_Active")) //&& GetLocalInt(oEvent, "MEME_HasTimeTrigger")) { _PrintString("Starting the event.", DEBUG_TOOLKIT); SetLocalInt(oEvent, "MEME_Active", 1); sName = GetLocalString(oEvent, "Name"); MeExecuteScript(sName,"_ini", OBJECT_SELF, oEvent); if (GetLocalInt(oEvent, "MEME_HasTimeTrigger")) { MeScheduleEvent(oEvent); } } else { _PrintString("Event is already active, cancelling the start request.", DEBUG_TOOLKIT); } _End("MeStartEvent", DEBUG_TOOLKIT); } void MeStopEvent(object oEvent, int iRemoveMemes = 0x01) { _Start("MeStopEvent", DEBUG_TOOLKIT); // Destroy Children int i = 0; object oMeme = OBJECT_INVALID; int count; if (iRemoveMemes & TYPE_MEME) { count = MeGetObjectCount(oEvent, "ChildMeme"); _PrintString("Destroying the meme's created by this generator."); for (i = count - 1; i >= 0; i--) { oMeme = MeGetObjectByIndex(oEvent, i, "ChildMeme"); MeDestroyMeme(oMeme); } } if (iRemoveMemes & TYPE_SEQUENCE) { count = MeGetObjectCount(oEvent, "ChildSequence"); _PrintString("Destroying the sequence templates's created by this generator."); for (i = count - 1; i >= 0; i--) { oMeme = MeGetObjectByIndex(oEvent, i, "ChildSequence"); MeDestroySequence(oMeme); } } // Deschedule the Event int iTimeID; if (GetLocalInt(oEvent, "MEME_Active")) { iTimeID = GetLocalInt(oEvent, "MEME_TimeIndex"); SetLocalInt(oEvent, "MEME_TimeIndex", iTimeID+1); SetLocalInt(oEvent, "MEME_Active", 0); } if (!MeIsEventScheduled(oEvent)) { _PrintString("This event is not scheduled; destroying event.", DEBUG_TOOLKIT); MeDestroyEvent(oEvent); } _End("MeStopEvent", DEBUG_TOOLKIT); } void MeDestroyEvent(object oEvent, int iRemoveMemes = 0x11) { _Start("MeDestroyEvent", DEBUG_TOOLKIT); object oNextEvent; int i = 0; string sName = GetLocalString(oEvent, "Name"); int count; string sChannel; object oMeme; // Disassociate or destroy child memes if (iRemoveMemes) { if (iRemoveMemes & TYPE_MEME) { _PrintString("Destroying the meme's created by this generator."); count = MeGetObjectCount(oEvent, "ChildMeme"); for (i=0; i < count; i++) { oMeme = MeGetObjectByIndex(oEvent, i, "ChildMeme"); MeDestroyMeme(oMeme); } } if (iRemoveMemes & TYPE_SEQUENCE) { count = MeGetObjectCount(oEvent, "SequenceMeme"); _PrintString("Destroying the sequence templates's created by this generator."); for (i=0; i < count; i++) { oMeme = MeGetObjectByIndex(oEvent, i, "SequenceMeme"); MeDestroyMeme(oMeme); } } } else { _PrintString("Diassociating this generator from the memes it created."); for (i=0; i < count; i++) { oMeme = MeGetObjectByIndex(oEvent, i, "ChildMeme"); DeleteLocalObject(oMeme, "MEME_Generator"); } } // Remove event chain oNextEvent = MeGetObjectByIndex(oEvent, 0, "Event"); while (GetIsObjectValid(oNextEvent)) { MeRemoveObjectRef(oNextEvent, oEvent, "Event"); MeRemoveObjectRef(oEvent, oNextEvent, "Event"); if (!MeIsEventScheduled(oNextEvent)) MeDestroyEvent(oNextEvent); oNextEvent = MeGetObjectByIndex(oEvent, 0, "Event"); } if (GetLocalInt(oEvent, "MEME_HasGlobalSignalTrigger")) { count = MeGetStringCount(oEvent, "Channel"); _PrintString("The event has "+IntToString(count)+" global signals to be destroyed.", DEBUG_TOOLKIT); for (i = 0; i < count; i++) { sChannel = MeGetStringByIndex(oEvent, i, "Channel"); MeRemoveObjectRef(GetModule(), OBJECT_SELF, sChannel+"Listener"); } } else _PrintString("The event has no global signals to be destroyed.", DEBUG_TOOLKIT); MeExecuteScript(sName,"_end", OBJECT_SELF, oEvent); DestroyObject(oEvent); _End("MeDestroyEvent", DEBUG_TOOLKIT); } object MeGetEvent(string sName = "", int Nth = 0) { _Start("MeGetEvent", DEBUG_UTILITY); object oBag = GetLocalObject(OBJECT_SELF, "MEME_Events"); object oResult = MeGetObjectByIndex(oBag, Nth); _End("MeGetEvent", DEBUG_UTILITY); return oResult; } void MeAddTriggerTime(object oEvent, float iDelay, int isDelay = 1) { _Start("MeAddTriggerTime delay = '"+FloatToString(iDelay)+"'", DEBUG_TOOLKIT); int iTimeID = GetLocalInt(oEvent, "MEME_TimeIndex"); SetLocalInt(oEvent, "MEME_HasTimeTrigger", 1); SetLocalFloat(oEvent, "MEME_TimeDelay", iDelay); SetLocalInt(oEvent, "MEME_TimeDelayType", isDelay); MeScheduleEvent(oEvent); // This will only schedule it if the event has been started. // 1. schedule a time based event // delay action on module to execute a script on me // unique id flag which says this is active i.e. timestamp/counter // has a flag about a pending time event. // 2. signal // turn on signal active flag // global: add event ref to module to listen to signal // 3. event trigger // ? do I need to know the trigger event in the chain // activation reason - with a subtype for event trigger or signal trigger // event chain response active flag // // in the execute code, when it fires, if it has no triggers you may need // to delete it -- don't if there is the event_persistant flag. _End("MeAddTriggerTime", DEBUG_TOOLKIT); } void MeAddTriggerSignal(object oEvent, int iSignal, float iDelay = 0.0, string sChannel = "") { _Start("MeAddTriggerSignal", DEBUG_TOOLKIT); SetLocalInt(oEvent, "MEME_HasSignalTrigger", 1); MeAddIntRef(oEvent, iSignal, "Signal"+sChannel); MeAddFloatRef(oEvent, iDelay, "SignalDelay"+sChannel); if (sChannel != "") { _PrintString("Subscribing to channel "+sChannel+"."); SetLocalInt(oEvent, "MEME_HasGlobalSignalTrigger", 1); if (MeHasObjectRef(GetModule(), OBJECT_SELF, sChannel+"Listener") == -1) { _PrintString("I've never subscribed to this channel, I will subscribe now."); MeAddObjectRef(GetModule(), OBJECT_SELF, sChannel+"Listener"); MeAddStringRef(oEvent, sChannel, "Channel"); } } _End("MeAddTriggerSignal", DEBUG_TOOLKIT); } void MeRemoveTriggerTime(object oEvent) { _Start("MeRemoveTriggerTime", DEBUG_TOOLKIT); int iTimeIndex = GetLocalInt(oEvent, "MEME_TimeIndex"); SetLocalInt(oEvent, "MEME_TimeIndex", iTimeIndex+1); SetLocalInt(oEvent, "MEME_HasTimeTrigger", 0); _End("MeRemoveTriggerTime", DEBUG_TOOLKIT); } void MeRemoveTriggerSignal(object oEvent, int iSignal, string sChannel = "") { _Start("MeRemoveTriggerSignal", DEBUG_TOOLKIT); int i, count; int iSig; string sSigChan = "Signal"+sChannel; string sSigDelChan = "SignalDelay"+sChannel; count = MeGetIntCount(oEvent, sSigChan); for (i = count - 1; i >= 0; i--) { iSig = MeGetIntByIndex(oEvent, i, sSigChan); if (iSig == iSignal) { MeRemoveIntByIndex(oEvent, i, sSigChan); MeRemoveFloatByIndex(oEvent, i, sSigDelChan); } } if (sChannel != "" && !MeGetIntCount(oEvent, sSigChan)) { MeRemoveStringRef(oEvent, sChannel, "Channel"); MeRemoveObjectRef(GetModule(), OBJECT_SELF, sChannel+"Listener"); } if (MeGetStringCount(oEvent, "Channel") == 0) { SetLocalInt(oEvent, "MEME_HasGlobalSignalTrigger", 0); if (MeGetIntCount(oEvent, "Signal") == 0) { SetLocalInt(oEvent, "MEME_HasSignalTrigger", 0); } } _End("MeRemoveTriggerSignal", DEBUG_TOOLKIT); } int MeIsEventScheduled(object oEvent) { return GetLocalInt(oEvent, "MEME_Active") || (GetLocalInt(oEvent, "MEME_Flags") & EVENT_PERSISTANT) || GetLocalInt(oEvent, "MEME_HasEventTrigger") || GetLocalInt(oEvent, "MEME_HasGlobalSignalTrigger") || GetLocalInt(oEvent, "MEME_HasSignalTrigger") || GetLocalInt(oEvent, "MEME_HasTimeTrigger"); } void MeSendSignal(int iSignal, string sMsg = "", object oData = OBJECT_INVALID, int iData = 0, string sChannel = "") { if (sChannel == "") { MeProcessSignals(iSignal, sMsg, oData, iData, sChannel, OBJECT_SELF); } else { object oObject = OBJECT_SELF; AssignCommand(GetModule(), MeModuleBroadcast(iSignal, sMsg, oData, iData, sChannel, oObject)); } } // ----- Core Toolkit ---------------------------------------------------------- void MeExecuteGenerators(string sSuffix) { if (!GetIsObjectValid(NPC_SELF)) return; _Start("MeExecuteGenerators suffix = '"+sSuffix+"'", DEBUG_TOOLKIT); object oBag = OBJECT_INVALID; object oGenerator = OBJECT_INVALID; int i = 0; int count = 0; if (GetLocalInt(NPC_SELF, "MEME_Paused")) { _PrintString("Will not execute generators, the system is paused.", DEBUG_TOOLKIT); _End("MeExecuteGenerators", DEBUG_TOOLKIT); return; } oBag = GetLocalObject(NPC_SELF, "MEME_GeneratorBag"); count = MeGetObjectCount(oBag); for (i = 0; i < count; i++) { oGenerator = MeGetObjectByIndex(oBag, i); if (GetLocalInt(oGenerator, "MEME_Active")) { _PrintString("Starting generator "+_GetName(oGenerator)+".", DEBUG_TOOLKIT); MeExecuteScript(GetLocalString(oGenerator, "Name"),sSuffix, OBJECT_SELF, oGenerator); } if (GetLocalInt(oGenerator, "MEME_Flags") & GEN_SINGLEUSE) { MeDestroyGenerator(oGenerator, 0); } } _End("MeExecuteGenerators", DEBUG_TOOLKIT); } void MeComputeBestMeme(object oMeme) { _Start("MeComputeBestMeme name = '"+GetLocalString(oMeme, "Name")+"'", DEBUG_TOOLKIT); object oBag = OBJECT_INVALID; object oPending = GetLocalObject(NPC_SELF, "MEME_PendingMeme"); object oActive = GetLocalObject(NPC_SELF, "MEME_ActiveMeme"); int iPriority = 0; int mPriority = 0; int pPriority = 0; int iModifier = 0; int pModifier = 0; int i = 0; int j = 0; string sName; // Only used for debugging. // -1. Dead simplest case first. // Side Effect: If !GetIsObjectValid(oMeme), then GetLocalInt(oMeme,"MEME_Priority") == 0 != PRIO_NONE if (GetLocalInt(oMeme, "MEME_Priority") == PRIO_NONE) { if ((oMeme != oActive) && (oMeme != oPending)) { _PrintString("No priority meme is ignored.", DEBUG_TOOLKIT); // Not Active nor Pending, just ignore it _End("MeComputeBestMeme", DEBUG_TOOLKIT); return; } // Is Active or Pending, completely recompute pending meme } else { // 0. Simplest case first. if (!GetIsObjectValid(oPending) && !GetIsObjectValid(oActive) && GetIsObjectValid(oMeme)) { _PrintString("Pending meme is now"+GetLocalString(oMeme,"Name")+".", DEBUG_TOOLKIT); SetLocalObject(NPC_SELF, "MEME_PendingMeme", oMeme); _End("MeComputeBestMeme", DEBUG_TOOLKIT); return; } // 0.1 if (GetIsObjectValid(oMeme)) { if (!GetIsObjectValid(oPending)) { // 1. Check for case where a recently modified meme beats active meme if (oMeme != oActive) { _PrintString("Checking to see if meme is better than active meme (" + _GetName(oActive) + ").", DEBUG_TOOLKIT); iPriority = GetLocalInt(oActive, "MEME_Priority"); mPriority = GetLocalInt(oMeme, "MEME_Priority"); if (iPriority < mPriority) { _PrintString("1. Pending meme is now "+GetLocalString(oMeme,"Name")+".", DEBUG_TOOLKIT); SetLocalObject(NPC_SELF, "MEME_PendingMeme", oMeme); } else if (iPriority == mPriority) { if (GetLocalInt(oActive, "MEME_Modifier") < GetLocalInt(oMeme, "MEME_Modifier")) { _PrintString("2. Pending meme is now "+GetLocalString(oMeme,"Name")+".", DEBUG_TOOLKIT); SetLocalObject(NPC_SELF, "MEME_PendingMeme", oMeme); } } _End("MeComputeBestMeme", DEBUG_TOOLKIT); return; } } else { // 2. Check optimized case where a recently modified meme beats highest pending meme. if (oMeme != oPending) { _PrintString("Checking to see if meme is better than pending meme (" + _GetName(oPending) + ").", DEBUG_TOOLKIT); iPriority = GetLocalInt(oPending, "MEME_Priority"); mPriority = GetLocalInt(oMeme, "MEME_Priority"); if (iPriority < mPriority) { _PrintString("3. Pending meme is now "+GetLocalString(oMeme,"Name")+".", DEBUG_TOOLKIT); SetLocalObject(NPC_SELF, "MEME_PendingMeme", oMeme); } else { iModifier = GetLocalInt(oPending, "MEME_Modifier"); if ((iPriority == mPriority) && (iModifier < GetLocalInt(oMeme, "MEME_Modifier"))) { _PrintString("4. Pending meme is now "+GetLocalString(oMeme,"Name")+".", DEBUG_TOOLKIT); SetLocalObject(NPC_SELF, "MEME_PendingMeme", oMeme); } } _End("MeComputeBestMeme", DEBUG_TOOLKIT); return; } } } } // 3. Find the highest priority meme. (ignoring bag PRIO_NONE.) oPending = OBJECT_INVALID; for (i = PRIO_VERYHIGH; i > PRIO_NONE; i--) { j = 0; _PrintString("Looking in band "+IntToString(i)+" for next best meme.", DEBUG_TOOLKIT); oBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(i)); while(1) { oMeme = MeGetObjectByIndex(oBag, j); if (!GetIsObjectValid(oMeme)) break; // Debug sName = GetLocalString(oMeme, "Name"); if (sName == "i_sequence") sName = GetLocalString(oMeme, "MEME_SequenceName"); _PrintString("Evaluating meme '"+sName+"'.", DEBUG_TOOLKIT); // End Debug iPriority = GetLocalInt(oMeme, "MEME_Priority"); iModifier = GetLocalInt(oMeme, "MEME_Modifier"); if (!GetIsObjectValid(oPending) || (iPriority > pPriority) || (iPriority == pPriority && iModifier > pModifier)) { pPriority = iPriority; pModifier = iModifier; oPending = oMeme; } j++; } if (GetIsObjectValid(oPending)) break; } if (GetIsObjectValid(oPending)) { _PrintString("5. Pending meme is now "+GetLocalString(oPending,"Name")+".", DEBUG_TOOLKIT); } if (oPending != oActive) SetLocalObject(NPC_SELF, "MEME_PendingMeme", oPending); else SetLocalObject(NPC_SELF, "MEME_PendingMeme", OBJECT_INVALID); _End("MeComputeBestMeme", DEBUG_TOOLKIT); } void _Tickle(object oActiveMeme) { _Start("JumpStartingNPC", DEBUG_TOOLKIT); MeExecuteScript(GetLocalString(oActiveMeme, "Name"), "_go", OBJECT_SELF, oActiveMeme); // ************* Lucullo fix ************** // ActionDoCommand(ActionDoCommand(ExecuteScript("cb_done", OBJECT_SELF))); int iRunCount = GetLocalInt(oActiveMeme, "MEME_RunCount") + 1; SetLocalInt(oActiveMeme, "MEME_RunCount", iRunCount); _PrintString("Meme = "+GetLocalString(oActiveMeme, "Name")+" next run ="+IntToString(iRunCount), DEBUG_TOOLKIT); ActionDoCommand(ActionDoCommand(DelayCommand(0.0, _MemeDone(oActiveMeme, iRunCount)))); DeleteLocalInt(OBJECT_SELF, "MEME_ScheduledForTickle"); _End(); } void MeUpdateActions() { _Start("MeUpdateActions", DEBUG_TOOLKIT); // I'm paused or the DM is using me. if (GetLocalInt(NPC_SELF, "MEME_Paused")) { _PrintString("Will not update actions, the system is paused."); _End("MeUpdateActions", DEBUG_TOOLKIT); return; } // ************* Lucullo fix ************** // logic for _Tickle was here object oActiveMeme; oActiveMeme = GetLocalObject(NPC_SELF, "MEME_ActiveMeme"); object oPendingMeme; oPendingMeme = GetLocalObject(NPC_SELF, "MEME_PendingMeme"); if (!GetIsObjectValid(oPendingMeme)) { if (GetLocalInt(oActiveMeme, "MEME_Priority") != PRIO_NONE) { _PrintString("No pending memes, the currently active behavior is fine.", DEBUG_TOOLKIT); // ************* Lucullo fix ************** // moved the logic, then suppressed it /* // Someone has cleared our action queue if (GetCurrentAction() == ACTION_INVALID) { if (!GetLocalInt(OBJECT_SELF, "MEME_ScheduledForTickle")) { SetLocalInt(OBJECT_SELF, "MEME_ScheduledForTickle", 1); _PrintString("NPC is stalled, restarting. Something cleared the Action Queue?"); DelayCommand(0.0, _Tickle(oActiveMeme)); } _End("MeUpdateActions", DEBUG_TOOLKIT); return; } */ _End("MeUpdateActions", DEBUG_TOOLKIT); return; } } ClearAllActions(); if (GetIsObjectValid(oActiveMeme)) { _PrintString("Stopping active meme, "+GetLocalString(oActiveMeme,"Name")+".", DEBUG_TOOLKIT); // MEME_INSTANT means that this meme shouldn't cause the interruption of the current meme // but it will get a chance to run (seemlessly) and if a MEME_IMMEDIATE meme is created, // it can be scheduled even though a MEME_INSTANT meme is active. MEME_INSTANT is a short // duration interruption meme. if (!MeGetMemeFlag(oPendingMeme, MEME_IMMEDIATE)) { MeExecuteScript(GetLocalString(oActiveMeme, "Name"),"_brk", OBJECT_SELF, oActiveMeme); if (!MeGetMemeFlag(oActiveMeme, MEME_RESUME)) { _PrintString("Destroying active meme,"+GetLocalString(oActiveMeme,"Name")+".", DEBUG_TOOLKIT); MeDestroyMeme(oActiveMeme); } else { _PrintString("The active meme,"+GetLocalString(oActiveMeme,"Name")+" is resumeable, it isn't being destroyed.", DEBUG_TOOLKIT); } } } SetLocalObject(NPC_SELF, "MEME_ActiveMeme", oPendingMeme); SetLocalObject(NPC_SELF, "MEME_PendingMeme", OBJECT_INVALID); _PrintString("Starting new active meme,"+GetLocalString(oPendingMeme,"Name")+".", DEBUG_TOOLKIT); MeExecuteScript(GetLocalString(oPendingMeme, "Name"),"_go", OBJECT_SELF,oPendingMeme); //if (GetLocalString(oActiveMeme, "Name") != "i_sequence") ActionDoCommand(ActionDoCommand(ExecuteScript("cb_done", OBJECT_SELF))); // ************* Lucullo fix ************** // ActionDoCommand(ActionDoCommand(ExecuteScript("cb_done", OBJECT_SELF))); int iRunCount = GetLocalInt(oPendingMeme, "MEME_RunCount") + 1; SetLocalInt(oPendingMeme, "MEME_RunCount", iRunCount); _PrintString("Meme = "+GetLocalString(oPendingMeme, "Name")+" next run ="+IntToString(iRunCount), DEBUG_TOOLKIT); ActionDoCommand(ActionDoCommand(DelayCommand(0.0, _MemeDone(oPendingMeme, iRunCount)))); _End("MeUpdateActions", DEBUG_TOOLKIT); } void MeRestartSystem() { _Start("MeRestartSystem", DEBUG_TOOLKIT); object oPendingMeme = GetLocalObject(NPC_SELF, "MEME_PendingMeme"); object oActiveMeme = GetLocalObject(NPC_SELF, "MEME_ActiveMeme"); ClearAllActions(); if (oPendingMeme == OBJECT_INVALID) { SetLocalObject(NPC_SELF, "MEME_PendingMeme", oActiveMeme); SetLocalObject(NPC_SELF, "MEME_ActiveMeme", OBJECT_INVALID); } MeUpdateActions(); _End("MeRestartSystem", DEBUG_TOOLKIT); } void MePauseSystem() { _Start("MePauseSystem", DEBUG_TOOLKIT); int i = 0; object oGen = OBJECT_INVALID; SetLocalInt(NPC_SELF, "MEME_Paused", 1); ClearAllActions(); _End("MePauseSystem", DEBUG_TOOLKIT); } void MeResumeSystem() { _Start("MeResumeSystem", DEBUG_TOOLKIT); int i = 0; object oGen = OBJECT_INVALID; DeleteLocalInt(NPC_SELF, "MEME_Paused"); MeRestartSystem(); _End("MeResumeSystem", DEBUG_TOOLKIT); } string MeGetEventChannel() { return GetLocalString(MEME_SELF, "MEME_Channel"); } object MeGetEventData() { return GetLocalObject(MEME_SELF, "MEME_Data"); } int MeGetEventInt() { return GetLocalInt(MEME_SELF, "MEME_Integer"); } string MeGetEventMsg() { return GetLocalString(MEME_SELF, "MEME_Message"); } object MeGetEventSender() { return GetLocalObject(MEME_SELF, "MEME_Sender"); } int MeGetEventSignal() { return GetLocalInt(MEME_SELF, "MEME_Signal"); } void MeExecuteEvent(object oEvent, int iTimeID, int iSignal = 0, string sMessage = "", object oData = OBJECT_INVALID, int iData = 0, string sChannel = "", object oSource = OBJECT_INVALID) { _Start("MeExecuteEvent name='"+GetLocalString(oEvent, "Name")+"' msg = '"+sMessage+"' signal='"+IntToString(iSignal)+"'", DEBUG_TOOLKIT); if (GetLocalInt(NPC_SELF, "MEME_Paused")) { _PrintString("Will not execute this event, the system is paused.", DEBUG_TOOLKIT); _PrintString("Note, this event activation was just lost. When paused, signals are not queue.", DEBUG_TOOLKIT); _PrintString("Channel messages are only received when active.", DEBUG_TOOLKIT); _End("MeExecuteEvent", DEBUG_TOOLKIT); return; } string sName = GetLocalString(oEvent, "Name"); float iDelay; SetLocalObject(oEvent, "MEME_Data", oData); SetLocalInt (oEvent, "MEME_Integer", iData); SetLocalString(oEvent, "MEME_Channel", sChannel); SetLocalObject(oEvent, "MEME_Sender", oSource); SetLocalString(oEvent, "MEME_Message", sMessage); SetLocalInt (oEvent, "MEME_Signal", iSignal); if (iTimeID != 0 && iTimeID != GetLocalInt(oEvent, "MEME_TimeIndex")) { _End("MeExecuteEvent", DEBUG_TOOLKIT); return; } if (iTimeID != 0 && !(GetLocalInt(oEvent, "MEME_Flags") & EVENT_REPEAT)) { MeRemoveTriggerTime(oEvent); iTimeID = GetLocalInt(oEvent, "MEME_TimeIndex"); } MeExecuteScript(sName,"_go", OBJECT_SELF, oEvent); DeleteLocalObject(oEvent, "MEME_Data"); DeleteLocalInt (oEvent, "MEME_Integer"); DeleteLocalString(oEvent, "MEME_Channel"); DeleteLocalObject(oEvent, "MEME_Sender"); DeleteLocalString(oEvent, "MEME_Message"); DeleteLocalInt (oEvent, "MEME_Signal"); // Trigger subevents here // Obviously event chains are not finished. if (MeIsEventScheduled(oEvent)) { // Set Up Next Timed Event _PrintString("Event is still scheduled -- somehow.", DEBUG_TOOLKIT); if (GetLocalInt(oEvent, "MEME_HasTimeTrigger") && (GetLocalInt(oEvent, "MEME_Flags") & EVENT_REPEAT) && iTimeID == GetLocalInt(oEvent, "MEME_TimeIndex")) { MeScheduleEvent(oEvent); } } else { _PrintString("This event has nothing else scheduled, it'll be destroyed.", DEBUG_TOOLKIT); MeDestroyEvent(oEvent); } MeUpdateActions(); _End("MeExecuteEvent", DEBUG_TOOLKIT); } void MeModuleBroadcast(int iSignal, string sMessage, object oData, int iData, string sChannel, object oObject) { string sName = _GetName(oObject); if (sName == "") sName = "Module"; _Start("MeModuleBroadcast msg='"+sMessage+"' event='"+IntToString(iSignal)+"' source='"+sName+"' channel='"+sChannel+"'", DEBUG_TOOLKIT); object oModule = GetModule(); object oNPC; int iListenerCount; int i; iListenerCount = MeGetObjectCount(GetModule(), sChannel+"Listener"); _PrintString("Channel "+sChannel+" has "+IntToString(iListenerCount)+" subscribers.", DEBUG_TOOLKIT); for (i = 0; i < iListenerCount; i++) { // This does not use NPC_SELF as it is probably the module. oNPC = MeGetObjectByIndex(OBJECT_SELF, i, sChannel+"Listener"); if (GetIsObjectValid(oNPC)) { _PrintString("Broadcasting event to "+_GetName(oNPC)+".", DEBUG_TOOLKIT); NPC_SELF = GetLocalObject (oNPC, "MEME_NPCSelf"); AssignCommand(oNPC, MeProcessSignals(iSignal, sMessage, oData, iData, sChannel, oObject)); } } _End("MeModuleBroadcast", DEBUG_TOOLKIT); } void MeProcessSignals(int iEventNum, string sMessage, object oData, int iData, string sSourceChannel, object oSourceObject) { NPC_SELF = GetLocalObject (OBJECT_SELF, "MEME_NPCSelf"); string sName = _GetName(oSourceObject); if (sName == "") sName = "Module"; _Start("MeProcessSignals msg='"+sMessage+"' event='"+IntToString(iEventNum)+"' source='"+sName+"' channel='"+sSourceChannel+"'", DEBUG_TOOLKIT); object oEvent; int iEventCount; int iSignalCount; int iSignal; int i, j; string sSigChan = "Signal"+sSourceChannel; string sSigDelChan = "SignalDelay"+sSourceChannel; // Events are stored in EventBag object oBag = GetLocalObject(NPC_SELF, "MEME_EventBag"); if (!GetIsObjectValid(oBag)) _PrintString("ASSERT: Bag does not exist!", DEBUG_TOOLKIT); if (GetIsObjectValid(NPC_SELF)) { _PrintString("Got the NPC_SELF.", DEBUG_TOOLKIT); } else _PrintString("ASSERT: NPC_SELF does not exist.", DEBUG_TOOLKIT); _PrintString("Getting the object count of "+_GetName(oBag), DEBUG_TOOLKIT); iEventCount = MeGetObjectCount(oBag); _PrintString("I have "+IntToString(iEventCount)+" internal events.", DEBUG_TOOLKIT); for (i = 0; i < iEventCount; i++) { oEvent = MeGetObjectByIndex(oBag, i); _PrintString("Looking at event "+GetLocalString(oEvent, "Name")+".", DEBUG_TOOLKIT); _PrintString(GetLocalString(oEvent, "Name")+" Active="+IntToString(GetLocalInt(oEvent, "MEME_Active")), DEBUG_TOOLKIT); _PrintString(GetLocalString(oEvent, "Name")+" HasSignalTrigger="+IntToString(GetLocalInt(oEvent, "MEME_HasSignalTrigger")), DEBUG_TOOLKIT); if (GetIsObjectValid(oEvent)) { if (GetLocalInt(oEvent, "MEME_Active") && GetLocalInt(oEvent, "MEME_HasSignalTrigger")) { iSignalCount = MeGetIntCount(oEvent, sSigChan); _PrintString("Event ("+GetLocalString(oEvent, "Name")+") responds to "+IntToString(iSignalCount)+" signals from Channel "+sSourceChannel+".", DEBUG_TOOLKIT); for (j = 0; j < iSignalCount; j++) { int iLocalSig = MeGetIntByIndex(oEvent, j, sSigChan); if (iLocalSig == ALL_TRIGGERS || iEventNum == iLocalSig) { _PrintString("Calling MeExecuteEvent via DelayCommand.", DEBUG_TOOLKIT); DelayCommand(MeGetFloatByIndex(oEvent, j, sSigDelChan), MeExecuteEvent(oEvent, 0, iEventNum, sMessage, oData, iData, sSourceChannel, oSourceObject)); } } } } else { MeRemoveObjectByIndex(oBag, i); i--; iEventCount--; } } _End("MeProcessSignals", DEBUG_TOOLKIT); } int MeHasScheduledMeme(int iPriority, int iModifier) { _Start("MeHasScheduledMeme", DEBUG_TOOLKIT); object oBag, oMeme; int i; int count; for (i = iPriority+1; i <= PRIO_VERYHIGH; i++) { oBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(i)); if (MeGetObjectCount(oBag)) { _End("MeHasScheduledMeme", DEBUG_TOOLKIT); return 1; } } oBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); count = MeGetObjectCount(oBag); for (i = 0; i < count; i++) { oMeme = MeGetObjectByIndex(oBag, i); if (GetIsObjectValid(oMeme)) { if (GetLocalInt(oMeme, "MEME_Modifier") >= iModifier) { _End("MeHasScheduledMeme", DEBUG_TOOLKIT); return 1; } } } _End("MeHasScheduledMeme", DEBUG_TOOLKIT); return 0; } /*------------------------------------------------------------------------------ * Meme: i_sequence * Author: William Bull * Date: July, 2002 * Notes: This is an internal meme that should never be used directly. * Please refer to the sequence API in the online documentation. *------------------------------------------------------------------------------*/ void i_sequence_ini() { _Start("Sequence event = 'Init'", DEBUG_TOOLKIT); SetLocalInt(MEME_SELF, "MEME_CurrentIndex", 0); // If this is set here, the sequence will always re-init the memes in // the sequence. This may not be the best thing if the sequence is a // singleton. MEME_SELF is the sequence_ref, not the sequence. The ref // may go away after the sequence runs its course -- since the memes are // not re-inited on i_sequence_end, the sequence may have stale data // in the memes. SetLocalInt(MEME_SELF, "MEME_InitSequence", 1); _End("Sequence", DEBUG_TOOLKIT); } /* Another step towards improving script execution with DelayCommand -- unsuccessful. void _GoMeme(object oActiveMeme) { if (!GetIsObjectValid(oActiveMeme)) return; object oNPCSelf = MeGetNPCSelf(); string sName = GetLocalString(oActiveMeme, "Name"); if (GetIsObjectValid(GetLocalObject(oNPCSelf, "MEME_PendingMeme"))) { MeUpdateActions(); } else if (GetLocalObject(oNPCSelf, "MEME_ActiveMeme") == oActiveMeme) { MeExecuteScript(sName,"_go", OBJECT_SELF, oActiveMeme); ActionDoCommand(ActionDoCommand(ExecuteScript("cb_done", OBJECT_SELF))); } } */ void i_sequence_go() { _Start("Sequence event = 'Go'", DEBUG_TOOLKIT); object oSequence = GetLocalObject(MEME_SELF, "MEME_Sequence"); int iIndex = GetLocalInt(MEME_SELF, "MEME_CurrentIndex"); object oMeme = MeGetObjectByIndex(oSequence, iIndex); int iFlags = GetLocalInt(MEME_SELF, "MEME_Flags"); int count, i; object oTemp; //ActionSpeakString("Starting "+GetLocalString(oMeme,"Name")+"..."); if ((iIndex == 0) && (GetLocalInt(MEME_SELF, "MEME_InitSequence") > 0)) { // This is the first time this has run... count = MeGetObjectCount(oSequence); for (i = 0; i < count; i++) { oTemp = MeGetObjectByIndex(oSequence, i); // Here is where I should check to see if the meme has a special // flag for seq_no_init. I'm not sure if I want this flag yet, but // this is where it should go. Meme _init is also called during // construction of the memes. // Note: if this is a repeating meme, MEME_InitSequence will be 2, // if this is the first time it's being initied, it will be 1. This // might be useful if I want a flag which doesn't reset a meme on // sequence repeats, but does on the sequence init. MeExecuteScript(GetLocalString(oTemp, "Name"),"_ini", OBJECT_SELF, oTemp); } // Rememeber this is the first time this has run... SetLocalInt(MEME_SELF, "MEME_InitSequence", 0); } if (!GetIsObjectValid(oMeme)) { SetLocalInt(MEME_SELF, "MEME_CurrentIndex", 0); SetLocalInt(MEME_SELF, "MEME_RestartIndex", 0); if (!(iFlags & SEQ_REPEAT)) { SetLocalInt(MEME_SELF, "MEME_Flags", iFlags & (~MEME_REPEAT)); } _End("Sequence", DEBUG_TOOLKIT); return; } //DelayCommand(0.0, _GoMeme(oMeme)); MeExecuteScript(GetLocalString(oMeme, "Name"),"_go", OBJECT_SELF, oMeme); _End("Sequence", DEBUG_TOOLKIT); } void i_sequence_brk() { _Start("Sequence event = 'Interrupted'", DEBUG_TOOLKIT); object oSequence = GetLocalObject(MEME_SELF, "MEME_Sequence"); int iIndex = GetLocalInt(MEME_SELF, "MEME_CurrentIndex"); object oMeme = MeGetObjectByIndex(oSequence, iIndex); MeExecuteScript(GetLocalString(oMeme, "Name"),"_brk", OBJECT_SELF, oMeme); SetLocalInt(MEME_SELF, "MEME_CurrentIndex", GetLocalInt(MEME_SELF, "MEME_RestartIndex")); _End("Sequence", DEBUG_TOOLKIT); } /* Private Meme */ // This is accessed by the lib_ai script -- the library of memetic objects. // It is hidden here to avoid being mixed up with common memes. It's really // only interesting for people who tracking a bug, or are masochistic workaholics. void i_sequence_end() { _Start("Sequence event = 'Step Taken'", DEBUG_TOOLKIT); object oSequence = GetLocalObject(MEME_SELF, "MEME_Sequence"); int iIndex = GetLocalInt(MEME_SELF, "MEME_CurrentIndex"); object oMeme = MeGetObjectByIndex(oSequence, iIndex); int iFlags = GetLocalInt(MEME_SELF, "MEME_Flags"); int mFlags = GetLocalInt(oMeme, "MEME_Flags"); object nextMeme = MeGetObjectByIndex(oSequence, iIndex+1); MeExecuteScript(GetLocalString(oMeme, "Name"),"_end", OBJECT_SELF, oMeme); // Deal with repeating memes in the sequence if (MeGetMemeFlag(oMeme, MEME_REPEAT)) { ; // Do nothing } // are we done? should we reset to repeat, or stop else if (!GetIsObjectValid(nextMeme)) { SetLocalInt(MEME_SELF, "MEME_CurrentIndex", 0); SetLocalInt(MEME_SELF, "MEME_RestartIndex", 0); // This could have a sequence flag which says whether or not the sequence // should reset the memes -- probably the flag should be added to the // memes and the check should be in the i_sequence_go area. SetLocalInt(MEME_SELF, "MEME_InitSequence", 2); if (!(iFlags & SEQ_REPEAT)) { _PrintString("Sequence complete.", DEBUG_TOOLKIT); SetLocalInt(MEME_SELF, "MEME_Flags", iFlags & (~MEME_REPEAT)); } // otherwise we are repeating should we take the prio of the first item? else if (GetLocalInt(oSequence, "MEME_Priority") == PRIO_DEFAULT) { nextMeme = MeGetObjectByIndex(oSequence, 0); _PrintString("1. First Meme in sequence has prio of "+IntToString(GetLocalInt(nextMeme, "MEME_Priority"))+"and modifier of "+IntToString(GetLocalInt(nextMeme, "MEME_Modifier"))+".", DEBUG_TOOLKIT); MeSetPriority(MEME_SELF, GetLocalInt(oMeme, "MEME_Priority"), GetLocalInt(nextMeme, "MEME_Modifier")); } } // otherwise we're advancing and care about next meme. else { // is the meme a checkpoint, if so notate it -- or if SEQ_RESUME_LAST if ((mFlags & MEME_CHECKPOINT) || (iFlags & SEQ_RESUME_LAST)) { _PrintString("Setting RestartIndex to "+IntToString(iIndex+1)+".", DEBUG_TOOLKIT); SetLocalInt(MEME_SELF, "MEME_RestartIndex", iIndex+1); } // advance the current index SetLocalInt(MEME_SELF, "MEME_CurrentIndex", iIndex+1); // should I adopt the priority of the next meme, if so, reprioritize. if (GetLocalInt(oSequence, "MEME_Priority") == PRIO_DEFAULT) { _PrintString("2. Next Meme in sequence has prio of "+IntToString(GetLocalInt(nextMeme, "MEME_Priority"))+"and modifier of "+IntToString(GetLocalInt(nextMeme, "MEME_Modifier"))+".", DEBUG_TOOLKIT); MeSetPriority(MEME_SELF, GetLocalInt(nextMeme, "MEME_Priority"), GetLocalInt(nextMeme, "MEME_Modifier")); } } _End("Sequence", DEBUG_TOOLKIT); } //effect e = EffectAreaOfEffect(AOE_MOB_INVISIBILITY_PURGE, "cb_area_in", "cb_area_hb", "cb_area_out"); //ApplyEffectToObject(DURATION_TYPE_PERMANENT, e, oTarget); /* Variables: OBJECT_SELF Specific MEME_NPCSelf -- The memetic store for hold all the data MEME_ObjectSelf -- The currently active memetic object NPC_SELF Specific MEME_GeneratorBag MEME_PrioBag1...5 MEME_EventBag MEME_Sequence_ MEME_ActiveMeme MEME_PendingMeme Meme Specific: Name MEME_Type MEME_Priority MEME_Modifier MEME_Flags MEME_Event MEME_Generator MEME_Sequence MEME_Parent MEME_Generator Meme_Count_Meme MEME_ChildMeme1, MEME_ChildMeme2, ... Generator Secific: Name MEME_Type MEME_Priority MEME_Modifier MEME_ChildMeme (Memes which it has created) MEME_Flags MEME_Event MEME_Count_Meme MEME_Meme1, MEME_Meme2, ... Meme_Count_Meme MEME_ChildMeme1, MEME_ChildMeme2, ... Sequence Specific: Name MEME_Type MEME_Priority MEME_Modifier MEME_Flags MEME_Event MEME_Generator MEME_SequenceRef Sequence Ref Specific: Name = i_sequence MEME_SequenceName MEME_Sequence MEME_CurrentIndex MEME_RestartIndex Event Specific Name MEME_Type MEME_Active MEME_Flags MEME_Count_Meme MEME_Meme1, MEME_Meme2, ... MEME_HasEventTrigger MEME_Count_Event MEME_Event1, MEME_Event2, ... MEME_EventDelay1, MEME_EventDelay2, ... MEME_HasGlobalSignalTrigger MEME_Count_GlobalSignal MEME_GlobalSignal1, MEME_GlobalSignal2, ... MEME_GlobalSignalDelay1, MEME_GlobalSignalDelay2, ... MEME_HasSignalTrigger MEME_IntCount_Signal MEME_Signal1, MEME_Signal2, ... MEME_SignalDelay1, MEME_SignalDelay2, ... MEME_HasTimeTrigger MEME_TimeIndex MEME_TimeDelay MEME_TimeDelayType Module Specific int MEME_HasBroadcastListeners <-- Not used? int MEME_Count_Listener object MEME_Listener1, MEME_Listener2, ... Meme Script: _go You are started or restarted _end You are have run to completion _brk You have been interrupted */ #include "h_util" /* File: h_class - Class * Author: William Bull, Daryl Low, Lucllo * Date: Copyright April, 2003 * * Description * * These are the class functions which will be added to h_util. * This provides the ability to define a class and an instance. * When an object is declared an instance of a class, the class * constructor is run. * */ // MeDeclareResponseTable // // This explicitly defines response table variables so that it may be inherited // automatically, destroyed or eventually saved to a database. // // This is generally used inside of a class constructor (_ini) // Once a variable is declared on a class, all instances will share these variables. // If this is used on an object that is an instance of a class, then this // function will use its local, overriding value, shadowing the inherited value. void MeDeclareResponseTable(string sTable, object oTarget = OBJECT_INVALID); // MeDeclareInt // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // These functions are generally used inside of a class constructor (_ini) // Once a variable is declared on a class, all instances will share these variables. // // If this is used on an object that is an instance of a class, then this // function will use its local, overriding value, shadowing the inherited value. // // Undeclaring is not supported at this time. // // sName: The name of the variable. // oTarget: The object which owns the variable. If the object is invalid, the // function will try and use the global MEME_SELF. This global is // defined inside of the class constructor _ini. It is generally // assumed that the object is either a class or an instance that // wants to override an inherited value. // flags: These control how the variable is used. At this time, these flags // are exclusive, or'ing ( A | B ) will not make sense. // // VAR_INHERIT This variable should be inherited by its children. // VAR_INHERIT_COPY This variable should be copied to the instances, // but not shared. The variable will be automatically // declared on the instance. // void MeDeclareInt(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareIntRef // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareIntRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareFloat // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareFloat(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareFloatRef // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareFloatRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareString // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareString(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareStringRef // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareStringRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareObject // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareObject(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareObjectRef // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareObjectRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeDeclareLocation // Explicitly defines a variable so that it may be inherited, automatically // destroyed or eventually saved to a database. // // For the full documentation, refer to MeDeclareInt. void MeDeclareLocation(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/); // MeInheritFrom // This causes the target object to inherit the variables declared by a parent. // If the parent inherits, or is an instace of a class, // the target does NOT inherit these variables -- only the ones explicitly // declared on the parent with the MeDeclare*(). // // oTarget: the object that wants to relegate control of some of it undeclared // variables. (i.e. the child.) // oParent: the object that has declared variables to be inherited // (i.e. the parent.) void MeInheritFrom(object oTarget, object oParent); // MeInstanceOf // This causes the target object to inherit the variables of the class // that was created with a call to MeRegisterClass(). A class instanciation script // +_go will be executed. In this script, the global, MEME_SELF // will be oTarget. // // This is used to allow a general class of objects to be constructed. // The class's _go script is used to setup the instance's declared variables, // default local values. If the target is a creature (or NPC_SELF) the class // would use the _go script as an opportunity to add generators and event // handlers on the NPC. // // The toolkit supports multiple inheritance. But to conserve memory, it is // highly recommended that you call this function on an object, once, with // a single comma-delimited list of class names. The system will merge the // inheritance declaration tables into one efficient table for the combination // of classes. For example, I you call f(a), f(b), f(c), f(d) you will have // created the tables: a, b, c, d, ab, abc, abcd. The tables ab and abc would // be unnecessary. // // oTarget: the object that wants to be an instance of the given class(es). // sClass: the name or list of names of classes. // "c_vermin, c_sorcerer, c_child_of_dark" // "c_base,c_pickle" void MeInstanceOf(object oTarget, string sClass); string MeGetParentClass(object oObject, int iIndex = 0); object MeGetClassObject(string sClassName); // ---- INHERITED VARIABLE ACCESS FUNCTIONS------------------------------------- void MeSetLocalInt(object oObject, string sVarName, int nValue); void MeSetLocalFloat(object oObject, string sVarName, float fValue); void MeSetLocalString(object oObject, string sVarName, string sValue); void MeSetLocalObject(object oObject, string sVarName, object oValue); void MeSetLocalLocation(object oObject, string sVarName, location lValue); int MeGetLocalInt(object oObject, string sVarName); float MeGetLocalFloat(object oObject, string sVarName); string MeGetLocalString(object oObject, string sVarName); object MeGetLocalObject(object oObject, string sVarName); location MeGetLocalLocation(object oObject, string sVarName); // ---- DECLARATION IMPLEMENTATION --------------------------------------------- // Variable declarations tables look like: DECL_: where type is: // I: Int, IF: Int Flags, IL: Int List, ILF: Int List Flags // O: Object, OF: Object Flags, OL: Object List, OLF: Object List Flags // S: String, SF: String Flags, SL: String List, SLF: String List Flags // F: Float, FF: Float Flags, FL: Float List, FLF: Float List Flags // L: Location, LF: Location Flags, LL: Location List, LLF: Location List Flags void MeDeclareInt(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; // This is the index to the declaration table, it's used to support // multiple inheritance. This table will be merged with other tables. // Since Bioware doesn't support intraspection, this also allows us to track // the variables we have on the object. if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_I*:"); // This is the declaration table, used to find the owner SetLocalObject(oTarget, "DECL_I:"+sName, oTarget); SetLocalInt(oTarget, "DECL_IF:"+sName, flags); } void MeDeclareIntRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; // This is the index to the declaration table, it's used to support // multiple inheritance. This table will be merged with other tables. // Since Bioware doesn't support intraspection, this also allows us to track // the variables we have on the object. if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_IL*:"); // This is the declaration table, used to find the owner SetLocalObject(oTarget, "DECL_IL:"+sName, oTarget); SetLocalInt(oTarget, "DECL_ILF:"+sName, flags); } void MeDeclareFloat(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_F*:"); SetLocalObject(oTarget, "DECL_F:"+sName, oTarget); SetLocalInt(oTarget, "DECL_FF:"+sName, flags); } void MeDeclareFloatRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_FL*:"); SetLocalObject(oTarget, "DECL_FL:"+sName, oTarget); SetLocalInt(oTarget, "DECL_FLF:"+sName, flags); } void MeDeclareString(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_S*:"); SetLocalObject(oTarget, "DECL_S:"+sName, oTarget); SetLocalInt(oTarget, "DECL_SF:"+sName, flags); } void MeDeclareStringRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_SL*:"); SetLocalObject(oTarget, "DECL_SL:"+sName, oTarget); SetLocalInt(oTarget, "DECL_SLF:"+sName, flags); } void MeDeclareObject(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_O*:"); SetLocalObject(oTarget, "DECL_O:"+sName, oTarget); SetLocalInt(oTarget, "DECL_OF:"+sName, flags); } void MeDeclareObjectRef(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_OL*"); SetLocalObject(oTarget, "DECL_OL:"+sName, oTarget); SetLocalInt(oTarget, "DECL_OLF:"+sName, flags); } void MeDeclareLocation(string sName, object oTarget = OBJECT_INVALID, int flags = 0x01 /*VAR_INHERIT*/) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; if (flags & VAR_INHERIT) MeAddStringRef(oTarget, sName, "DECL_L*"); SetLocalObject(oTarget, "DECL_L:"+sName, oTarget); SetLocalInt(oTarget, "DECL_LF:"+sName, flags); } void MeDeclareResponseTable(string sTable, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; sTable= "MEME_RT_"+sTable; MeDeclareStringRef(sTable, oTarget); MeDeclareIntRef(sTable,oTarget); } // ---- MAPPING IMPLEMENTATION ------------------------------------------------- void MeMapInt(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_I:"+sLocalVar, sInheritedVar); } void MeMapIntRef(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_IL:"+sLocalVar, sInheritedVar); } void MeMapFloat(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_F:"+sLocalVar, sInheritedVar); } void MeMapFloatRef(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_FL:"+sLocalVar, sInheritedVar); } void MeMapString(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_S:"+sLocalVar, sInheritedVar); } void MeMapStringRef(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_SL:"+sLocalVar, sInheritedVar); } void MeMapObject(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_O:"+sLocalVar, sInheritedVar); } void MeMapObjectRef(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_OL:"+sLocalVar, sInheritedVar); } void MeMapLocation(string sLocalVar, string sInheritedVar, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; SetLocalString(oTarget, "VMAP_L:"+sLocalVar, sInheritedVar); } // ---- GET INHERITED IMPLEMENTATION ------------------------------------------- // Variable declarations tables look like: DECL_: where type is: // F: Float, FF: Float Flags, FL: Float List, FLF: Float List Flags F*: Float Declation Table FL*: Float List Declaration Table // O: Object, OF: Object Flags, OL: Object List, OLF: Object List Flags O*: Object Declation Table OL*: Object List Declaration Table // I: Int, IF: Int Flags, IL: Int List, ILF: Int List Flags I*: Int Declation Table IL*: Int List Declaration Table // L: Location, LF: Location Flags, LL: Location List, LLF: Location List Flags L*: Location Declation Table LL*: Location List Declaration Table // S: String, SF: String Flags, SL: String List, SLF: String List Flags S*: String Declation Table SL*: String List Declaration Table int MeGetLocalInt(object oObject, string sVarName) { // 1. There will be a declaration entry if: // * oObject is a class or merged class - merge class entries point to the owner class - order is dependent to solve collisions from multiple inheritance. // * oObject is an instance with an overriding variable object oDeclarationTarget = GetLocalObject(oObject, "DECL_I:"+sVarName); string sMapName; if (oDeclarationTarget != OBJECT_INVALID) { return GetLocalInt(oDeclarationTarget, sVarName); } // 2. There will be a parent if: // * This is a class that inherits a value from another class // * This is an object which directly inherits from another object object oParent = GetLocalObject(oObject, "MEME_Parent"); if (oParent != OBJECT_INVALID) { // 2a. Is the variable name remapped to a new name on the parent object? sMapName = GetLocalString(oObject, "VMAP_I:"+sVarName); if (sMapName != "") sVarName = sMapName; // Now this is expensive - but rarely occurs. I tried to avoid // walking a inheritance tree, wherever possible. return MeGetLocalInt(oParent, sVarName); } // 3. Otherwise just return the usual value. return GetLocalInt(oObject, sVarName); } float MeGetLocalFloat(object oObject, string sVarName) { string sMapName; object oDeclarationTarget = GetLocalObject(oObject, "DECL_F:"+sVarName); if (oDeclarationTarget != OBJECT_INVALID) { return GetLocalFloat(oDeclarationTarget, sVarName); } object oParent = GetLocalObject(oObject, "MEME_Parent"); if (oParent != OBJECT_INVALID) { sMapName = GetLocalString(oObject, "VMAP_F:"+sVarName); if (sMapName != "") sVarName = sMapName; return MeGetLocalFloat(oParent, sVarName); } return GetLocalFloat(oObject, sVarName); } string MeGetLocalString(object oObject, string sVarName) { string sMapName; object oDeclarationTarget = GetLocalObject(oObject, "DECL_S:"+sVarName); if (oDeclarationTarget != OBJECT_INVALID) { return GetLocalString(oDeclarationTarget, sVarName); } object oParent = GetLocalObject(oObject, "MEME_Parent"); if (oParent != OBJECT_INVALID) { sMapName = GetLocalString(oObject, "VMAP_S:"+sVarName); if (sMapName != "") sVarName = sMapName; return MeGetLocalString(oParent, sVarName); } return GetLocalString(oObject, sVarName); } location MeGetLocalLocation(object oObject, string sVarName) { string sMapName; object oDeclarationTarget = GetLocalObject(oObject, "DECL_L:"+sVarName); if (oDeclarationTarget != OBJECT_INVALID) { return GetLocalLocation(oDeclarationTarget, sVarName); } object oParent = GetLocalObject(oObject, "MEME_Parent"); if (oParent != OBJECT_INVALID) { sMapName = GetLocalString(oObject, "VMAP_L:"+sVarName); if (sMapName != "") sVarName = sMapName; return MeGetLocalLocation(oParent, sVarName); } return GetLocalLocation(oObject, sVarName); } object MeGetLocalObject(object oObject, string sVarName) { string sMapName; object oDeclarationTarget = GetLocalObject(oObject, "DECL_O:"+sVarName); if (oDeclarationTarget != OBJECT_INVALID) { return GetLocalObject(oDeclarationTarget, sVarName); } object oParent = GetLocalObject(oObject, "MEME_Parent"); if (oParent != OBJECT_INVALID) { sMapName = GetLocalString(oObject, "VMAP_O:"+sVarName); if (sMapName != "") sVarName = sMapName; return MeGetLocalObject(oParent, sVarName); } return GetLocalObject(oObject, sVarName); } void MeSetLocalInt(object oTarget, string sVarName, int nValue) { // Only bother with a delcaration entry if there is inheritance. if (GetLocalObject(oTarget, "MEME_Parent") != OBJECT_INVALID) { SetLocalObject(oTarget, "DECL_I:"+sVarName, oTarget); } SetLocalInt(oTarget, sVarName, nValue); } void MeSetLocalFloat(object oTarget, string sVarName, float fValue) { // Only bother with a delcaration entry if there is inheritance. if (GetLocalObject(oTarget, "MEME_Parent") != OBJECT_INVALID) { SetLocalObject(oTarget, "DECL_F:"+sVarName, oTarget); } SetLocalFloat(oTarget, sVarName, fValue); } void MeSetLocalString(object oTarget, string sVarName, string sValue) { // Only bother with a delcaration entry if there is inheritance. if (GetLocalObject(oTarget, "MEME_Parent") != OBJECT_INVALID) { SetLocalObject(oTarget, "DECL_S:"+sVarName, oTarget); } SetLocalString(oTarget, sVarName, sValue); } void MeSetLocalObject(object oTarget, string sVarName, object oValue) { // Only bother with a delcaration entry if there is inheritance. if (GetLocalObject(oTarget, "MEME_Parent") != OBJECT_INVALID) { SetLocalObject(oTarget, "DECL_O:"+sVarName, oTarget); } SetLocalObject(oTarget, sVarName, oValue); } void MeSetLocalLocation(object oTarget, string sVarName, location lValue) { // Only bother with a delcaration entry if there is inheritance. if (GetLocalObject(oTarget, "MEME_Parent") != OBJECT_INVALID) { SetLocalObject(oTarget, "DECL_L:"+sVarName, oTarget); } SetLocalLocation(oTarget, sVarName, lValue); } //------------------------------------------------------------------------------ string MeGetParentClass(object oObject, int iIndex = 0) { return MeGetStringByIndex(oObject, iIndex, "MEME_Parents"); } object MeGetClassObject(string sClassName) { return GetLocalObject(GetModule(), "MEME_Class_"+sClassName); } //------------------------------------------------------------------------------ // Create the relationship between child and parent void MeInheritFrom(object oTarget, object oParentClass) { _Start("MeInheritFrom", DEBUG_UTILITY); // Detach ourselves from any old classes - we do not track parents // when MeInheritFrom is used. It's an anonymous inheritance. MeDeleteStringRefs(oTarget, "MEME_Parents"); // Overwrite any old inheritance SetLocalObject(oTarget, "MEME_Parent", oParentClass); _End("MeInheritFrom", DEBUG_UTILITY); } //------------------------------------------------------------------------------ // This is an internal function used by MeInstanceOf. Its job is to // go to each variable in the given declaration table type and // extract the variable name and have the new class mirror the class // table. void _CopyDeclTable(object oClass, object oNewClass, string sType) { _Start("_CopyDeclTable", DEBUG_UTILITY); int count; string sDecl = sType+"*:"; // I think this is right. sType = sType+":"; string sVar; object oOwner; count = MeGetStringCount(oClass, sDecl); //_PrintString("count = MeGetStringCount(object:"+_GetName(oClass)+", string:"+sDecl+") == "+IntToString(count)); for (0; count > 0; count--) { sVar = MeGetStringByIndex(oClass, count-1, sDecl); //_PrintString("MeGetStringByIndex(object:"+_GetName(oClass)+", int:"+IntToString(count-1)+", string:"+sDecl+") == "+sVar); oOwner = GetLocalObject(oClass, sType+sVar); //_PrintString("oOwner = GetLocalObject(object:"+_GetName(oClass)+", "+sType+sVar+" == object:"+_GetName(oOwner)); SetLocalObject(oNewClass, sType+sVar, oOwner); //_PrintString("SetLocalObject(object:"+_GetName(oNewClass)+", string:"+sType+sVar+", object:"+_GetName(oOwner)+");"); } _End("_CopyDeclTable", DEBUG_UTILITY); } // Create an instance of a given class, me must support: // // MeInstanceOf(oTarget, "class_base"); // MeInstanceOf(oTarget, "pickle, earplug"); and then... // MeInstanceOf(oTarget, "xyzzy,schnatchzy-poo,BLEEM!!,vorgon poetry,thing-your-aunt-gave-you, "); // // oTarget may be a class or a non-class. void MeInstanceOf(object oTarget, string sClass) { _Start("MeInstanceOf", DEBUG_UTILITY); // 1. First we want to build a list of the requested classes MeExplodeList(oTarget, sClass, "MEME_NewParents"); // 2. Next we remove the classes we already belong to int count = MeGetStringCount(oTarget, "MEME_Parents"); int count2 = MeGetStringCount(oTarget, "MEME_NewParents"); int count3; string sName, sNewName; object oModule = GetModule(); _Start("PruneList", DEBUG_UTILITY); // Iterate through the short, new list. for (; count2 > 0; count2--) { // Get the name of the new class sNewName = MeGetStringByIndex(oTarget, count2-1, "MEME_NewParents"); // Iterate through the potentailly longer old list. for (count3 = count; count3 > 0; count3--) { // Get the name of one of the current classes. sName = MeGetStringByIndex(oTarget, count3-1, "MEME_Parents"); // Do we already have it? if so, remove it from the new list if (sName == sNewName) { //_PrintString("Removing duplicate class entry, "+sNewName+".", DEBUG_UTILITY); MeRemoveStringByIndex(oTarget, count2-1, "MEME_NewParents"); } } } // Now, we have removed all the classes that we already belong to, NewParents may be substantially shorter. // 3. Then we remove the classes that don't exist // (Incidently, the reason why I always count downwards is it allows me to // delete the entry. If I was going up I would have to shrink the count, // which is a hassle.) for (count = MeGetStringCount(oTarget, "MEME_NewParents"); count > 0; count--) { sNewName = MeGetStringByIndex(oTarget, count-1, "MEME_NewParents"); if (GetLocalObject(oModule, "MEME_Class_"+sNewName) == OBJECT_INVALID) { //_PrintString("I have never head of class, "+sNewName+".", DEBUG_UTILITY); //_PrintString("Did you forget to initialize a class library?", DEBUG_UTILITY); MeRemoveStringByIndex(oTarget, count-1, "MEME_NewParents"); } } // Now we have a shorter list of new classes to be added _End("PruneList", DEBUG_UTILITY); count = MeGetStringCount(oTarget, "MEME_NewParents"); if (count == 0) { MeDeleteStringRefs(oTarget, "MEME_NewParents"); //_PrintString("Warning: This declaration has failed, I cannot find any classes you are requesting."); _End("MeInstanceOf", DEBUG_UTILITY); return; } // 4. Add these items to the Parents list _Start("MergeList", DEBUG_UTILITY); count = MeGetStringCount(oTarget, "MEME_NewParents"); //_PrintString("There are "+IntToString(count)+" new classes being added.", DEBUG_UTILITY); for (0; count > 0; count--) { sNewName = MeGetStringByIndex(oTarget, count-1, "MEME_NewParents"); //_PrintString("Adding "+sNewName+".", DEBUG_UTILITY); MeAddStringRef(oTarget, sNewName, "MEME_Parents"); } count = MeGetStringCount(oTarget, "MEME_Parents"); //_PrintString("This object now belongs to "+IntToString(count)+" classes.", DEBUG_UTILITY); // 5. Get the class key of the current Parent object oParent =