ERF V1.0hhELExportInfoh_aih_aih_classh_constantsh_debugh_eventh_landmark_clih_landmark_comh_library h_list h_private h_time h_util h_utillib_generatorslib_generatorslib_movementlib_movement m /;J$"X6:Gp { h'B':\;/1tHGFF V3.28d6lO'l (08@HPX`hpx         % * / 7 C K S b q {        d Mod_MinGameVerExpansion_PackCommentsTopResRefResTypeDependenciesMissing1.62This ERF contains changes divide NPC_SELF into an NPC_SELF object and an accompanying store. The store object is never deleted, avoiding Bioware bugs with store deletion. Instead, the stores are put onto a free list, and reused.h_ai h_privateh_utillib_generators lib_movementh_aih_aih_class h_constantsh_debugh_eventh_landmark_clih_landmark_com h_libraryh_list h_privateh_timeh_utilh_utillib_generatorslib_generators lib_movement lib_movement h_private3  !"#$%&'()*+,-./01245 NCS V1.0B    MEME_ObjectSelf6 MEME_NPCSelf6*+   #include "h_time" #include "h_landmark_cli" #include "h_event" /* 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. * */ // MeCreateMeme // File: h_ai // 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 // File: h_ai // 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); int MeGetMemeResult(object oMeme = OBJECT_INVALID); // MeSetPriority // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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); // MeDestroyChildMemes // File: h_ai // Destroys the child memes that belong this parent. // If this meme is suspended because of those child memes, you should // call MeResumeMeme(), which calls this function automatically. // This function does not notify the children with _end or _brk callbacks, // it does not call UpdateActions() or ComputeBestMeme(). void MeDestroyChildMemes(object oParent, int iResumeParent = 1); // MeGetActiveMeme // File: h_ai // Returns the currently running meme. object MeGetActiveMeme(); // MeGetPendingMeme // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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, int bUpdateActions = 1); // MeIsMemeSuspended // File: h_ai // Returns TRUE if the meme is currently suspended. int MeIsMemeSuspended(object oMeme); // MeCreateSequence // File: h_ai // 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 // File: h_ai // 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*/); // object MeStartSequence // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // 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 */); // MeExecuteGenerators // File: h_ai // This is an internal function to the MemeticAI Toolkit. void MeExecuteGenerators(string sSuffix); // MeComputeBestMeme // File: h_ai // This is an internal function to the MemeticAI Toolkit. void MeComputeBestMeme(object oMeme = OBJECT_INVALID); // MeUpdateActions // File: h_ai // 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 // File: h_ai // 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 // File: h_ai // // This causes the NPC to stop behaving memetically.*** void MePauseSystem(); // MeHasScheduledMeme // File: h_ai // 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. // An optional object may be passed to tell the test function to skip a meme. // This is used when child memes are created with MEME_INSTANT. The parent meme is being // suspended, but is still active while the child is being created. This parameter // allows the function to overlook the value of the given meme and compare all other // memes. // // iPriority: this is the priority to compare it against, like PRIO_LOW, PRIO_HIGH, etc. // iModifier: this is the modifier from -100 to +100 // oExcludeMeme: this is the meme to overlook -- used to pass up memes that are being suspended. int MeHasScheduledMeme(int iPriority, int iModifier, object oExcludeMeme=OBJECT_INVALID); // -- Implementation ----------------------------------------------------------- void _MemeDone(object oActiveMeme, int iRunInstance); // ------ 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(); return oGenerator; } void MeStartGenerator(object oGenerator) { _Start("MeStartGenerator name = '"+GetLocalString(oGenerator, "Name")+"'", DEBUG_TOOLKIT); SetLocalInt(oGenerator, "MEME_Active", 1); _End(); } 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(); } 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(); return oMeme; } count++; } if (!GetIsObjectValid(oMeme)) break; i++; } _End(); 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(); return oMeme; } count++; if (!GetIsObjectValid(oMeme)) break; i++; } _End(); 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(); 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) { _PrintString("Suspending the active meme."); ClearAllActions(); if (bCallBrk) { MeExecuteScript(GetLocalString(oMeme, "Name"),"_brk", OBJECT_SELF, oMeme); // January, 2004: Niveau0 if (!GetLocalInt(oMeme, "MEME_Suspended")) { int iRunInstance = GetLocalInt(oMeme, "MEME_RunCount"); iRunInstance++; SetLocalInt(oMeme, "MEME_RunCount", iRunInstance); //ActionDoCommand(ActionDoCommand(DelayCommand(0.0, _MemeDone(oMeme, iRunInstance)))); ActionDoCommand(ActionDoCommand(_MemeDone(oMeme, iRunInstance))); _End(); return; } } // If there is only one meme on the NPC and it suspends, there will // not be any other meme to be active when ComputeBestMeme is called. // ActiveMeme should be cleared to prevent this situation. SetLocalObject(NPC_SELF, "MEME_ActiveMeme", OBJECT_INVALID); } // MeComputeBestMeme(oMeme); // January, 2004: Niveau0 MeComputeBestMeme(OBJECT_INVALID); // Changed to invalid, the suspended one should never run again MeUpdateActions(); _End(); } // 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, int bUpdateActions=1) { // 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); // January, 2004: niveau // make update actions optional if more than one meme gets resumed at a time, performance reasons // you have to call it manually then //MeUpdateActions(); if (bUpdateActions) MeUpdateActions(); _End(); } 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, oParent))) { _PrintString("Another meme is higher than you, this meme won't get created.", DEBUG_TOOLKIT); _End(); // 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, notify through _brk callback. MeExecuteScript(GetLocalString(oParent, "Name"),"_brk", OBJECT_SELF, oParent); MeSuspendMeme(oParent, 0); } } 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(); return oMeme; } int MeGetPriority(object oMeme) { return GetLocalInt(oMeme, "MEME_Priority"); } int MeGetModifier(object oMeme) { return GetLocalInt(oMeme, "MEME_Modifier"); } // Private prototype void _MeDestroyMeme(object oMeme, int iCallEndScript = 1, int iComputeBestMeme = 1, int iDestroySiblings = 1, int iRestartParent = 1); // It's important to realize that the parameters this passes to _MeDestroyMeme // cause this function is bypass normal meme-notifcationst that they're ended void MeDestroyChildMemes(object oParent, int iResumeParent = 1) { _Start("MeDestroyChildMemes name = '"+GetLocalString(oParent, "Name")+"'", DEBUG_TOOLKIT); _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, iResumeParent); count--; } _End(); } void _MeDestroyMeme(object oMeme, int iCallEndScript = 1, int iComputeBestMeme = 1, int iDestroySiblings = 1, int iRestartParent = 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) && iRestartParent) { _PrintString("That was the last child, resuming the parent "+GetLocalString(oParent, "Name"), DEBUG_TOOLKIT); // No more children, let them run. MeResumeMeme(oParent, 0); } 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) { // Note: This code is also done in MeDestroyChildMemes(); MeDestroyChildMemes(oParent); MeResumeMeme(oParent, 0); } 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(); } 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(); return oMeme; } nth++; } oMeme = MeGetObjectByIndex(oBag, j); } } } _End(); return OBJECT_INVALID; } void _MemeDone(object oActiveMeme, int iRunInstance) { if (!GetIsObjectValid(oActiveMeme)) return; if ((oActiveMeme != GetLocalObject(NPC_SELF, "MEME_ActiveMeme")) || (iRunInstance != GetLocalInt(oActiveMeme, "MEME_RunCount"))) { _PrintString("We have been overrun!", DEBUG_TOOLKIT); _End(); 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(); 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(); return; } */ // Start January, 2004: niveau0 // Fix for suspended meme if (MeGetMemeFlag(oActiveMeme, MEME_REPEAT)) { if (MeIsMemeSuspended(oActiveMeme) || // Maybe meme got suspended in _end MeGetPriority(oActiveMeme) == PRIO_NONE || // Maybe meme lost its priority in _end 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(_MemeDone(oActiveMeme, iRunInstance))); } _End(); return; } // End niveau0 _PrintString("Meme completed, destroying.", DEBUG_TOOLKIT); _MeDestroyMeme(oActiveMeme, 0); // Internal version doesn't call _end script. MeUpdateActions(); _End(); 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); } _End(); } 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(); } 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); MeDestroyChildMemes(oMeme, 0); if (GetLocalObject(NPC_SELF, "MEME_ActiveMeme") == oMeme) { ClearAllActions(); MeExecuteScript(GetLocalString(oMeme, "Name"),"_brk", OBJECT_SELF, oMeme); int iRunCount = GetLocalInt(oMeme, "MEME_RunCount") + 1; SetLocalInt(oMeme, "MEME_RunCount", iRunCount); _MemeDone(oMeme, iRunCount); } _End(); } 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(); } object MeGetParentGenerator(object oMeme) { _Start("MeGetParentGenerator", DEBUG_UTILITY); _End(); return GetLocalObject(oMeme, "MEME_Generator"); } object MeGetParentMeme(object oMeme) { _Start("MeGetParentMeme", DEBUG_UTILITY); _End(); 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); } int MeGetMemeResult(object oMeme = OBJECT_INVALID) { if (oMeme == OBJECT_INVALID) oMeme = GetLocalObject (OBJECT_SELF, "MEME_ObjectSelf"); // NOTICE -- I set 0 as TRUE, 1 as FALSE // Why? This allows me to have a default value of 0 as a success. return (!GetLocalInt(oMeme, "MEME_Result")); } 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(); } // ----- 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(); 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(); 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(); 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(); return oMeme; } object MeStartSequence(object oSequence) { _Start("MeStartSequence", DEBUG_TOOLKIT); object oSeqRef = GetLocalObject(oSequence, "MEME_SequenceRef"); if (GetIsObjectValid(oSeqRef)) { _PrintString("Sequence already running!"); _End(); 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(); 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(); } 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(); 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(); } object MeGetSequence(string sName = "") { _Start("MeGetSequence", DEBUG_UTILITY); _End(); return GetLocalObject(NPC_SELF,"MEME_Sequence_"+sName); } // ----- Core Toolkit ---------------------------------------------------------- void MeExecuteGenerators(string sSuffix) { if (!GetIsObjectValid(NPC_SELF)) { _PrintString("This NPC has attempted to prematurely execute generators."); 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(); 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(); } 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(); 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(); 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"); _PrintString("Priority of active meme: "+IntToString(iPriority)); _PrintString("Priority of this meme: "+IntToString(mPriority)); 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(); 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(); 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(); } /* void _Tickle(object oActiveMeme) { _Start("JumpStartingNPC", DEBUG_TOOLKIT); 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(); void MeUpdateActions() { if (GetLocalInt(NPC_SELF, "MEME_Paused") || GetLocalInt(NPC_SELF, "MeUpdateScheduled")) return; _Start("MeUpdateActions scheduled='TRUE'", DEBUG_TOOLKIT); SetLocalInt(NPC_SELF, "MeUpdateScheduled", 1); // Toy with these see the performance difference. You might see a stuttered // NPC but have a less laggy system with the first one. DelayCommand(0.1, _MeUpdateActions()); //DelayCommand(0.0, _MeUpdateActions()); _End(); } void _MeUpdateActions() { _Start("MeUpdateActions", DEBUG_TOOLKIT); DeleteLocalInt(NPC_SELF, "MeUpdateScheduled"); if (!GetIsObjectValid(NPC_SELF)) { _PrintString("Error: attempting to update actions on a non-memetic NPC."); _End(); return; } // 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(); return; } object oActiveMeme; oActiveMeme = GetLocalObject(NPC_SELF, "MEME_ActiveMeme"); object oPendingMeme; oPendingMeme = GetLocalObject(NPC_SELF, "MEME_PendingMeme"); if (!GetIsObjectValid(oActiveMeme) && !GetIsObjectValid(oPendingMeme)) ClearAllActions(); if (!GetIsObjectValid(oPendingMeme)) { if (GetLocalInt(oActiveMeme, "MEME_Priority") != PRIO_NONE) { _PrintString("No pending memes, the currently active behavior is fine. ("+_GetName(oActiveMeme)+")", DEBUG_TOOLKIT); /* // Experimental tickle code causes the NPC to be kickstarted if // someone has cleared our action queue -- like the game engine if (GetCurrentAction() == ACTION_INVALID && oActiveMeme != OBJECT_INVALID) { if (!GetLocalInt(OBJECT_SELF, "MEME_ScheduledForTickle")) { SetLocalInt(OBJECT_SELF, "MEME_ScheduledForTickle", 1); _PrintString("NPC is stalled, restarting. Something cleared by Action Queue."); DelayCommand(0.0, _Tickle(oActiveMeme)); } _End(); return; } */ _End(); 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))); 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(); } 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(); } void MePauseSystem() { _Start("MePauseSystem", DEBUG_TOOLKIT); int i = 0; object oGen = OBJECT_INVALID; SetLocalInt(NPC_SELF, "MEME_Paused", 1); ClearAllActions(); _End(); } void MeResumeSystem() { _Start("MeResumeSystem", DEBUG_TOOLKIT); int i = 0; object oGen = OBJECT_INVALID; DeleteLocalInt(NPC_SELF, "MEME_Paused"); MeRestartSystem(); _End(); } int MeHasScheduledMeme(int iPriority, int iModifier, object oExcludeMeme=OBJECT_INVALID) { _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)); switch (MeGetObjectCount(oBag)) { case 0: break; case 1: oBag = GetLocalObject(NPC_SELF, "MEME_PrioBag"+IntToString(iPriority)); if (MeGetObjectByIndex(oBag, 0) == oExcludeMeme) break; default: _End(); 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) && oMeme != oExcludeMeme) { if (GetLocalInt(oMeme, "MEME_Modifier") >= iModifier) { _PrintString("Meme is: "+_GetName(MeGetObjectByIndex(oBag, 0))); _End(); return 1; } } } _End(); 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(); } /* 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(); return; } //DelayCommand(0.0, _GoMeme(oMeme)); MeExecuteScript(GetLocalString(oMeme, "Name"),"_go", OBJECT_SELF, oMeme); _End(); } 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(); } /* 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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 // File: h_class // // 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*/); // MeDeclareLocalMessage // File: h_class // // Explicitly defines a message so that it may be inherited, automatically // destroyed or eventually saved to a database. // // This function 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 cause the object to use its local, overriding value, shadowing // the inherited value. void MeDeclareLocalMessage(string sMessageName, object oTarget = OBJECT_INVALID); // MeInheritFrom // File: h_class // // 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 // File: h_class // // 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. // "vermin, sorcerer,child_of_dark" // "base,pickle" void MeInstanceOf(object oTarget, string sClass); // MeGetParentClass // File: h_class // An object that is an instance of a class has a number of objects that // it is associated to. This function gets the name of each of these classes. // With this name you can use MeGetParentClass to get the actual object. // // This is not a function most people need to know about or use. // // oObject: The object that is an instance of a class. If the object is // not an instance of a class a null string is returned. // iIndex: Since an object can belong to more than one class, this is an // index representing which class name you want. This is a 0-based // index. string MeGetParentClass(object oObject, int iIndex = 0); // MeGetClassObject // File: h_class // Class objects are related to a set of objects that are an "instance" of a // class -- see MeInstanceOf() for more detail on this process. // These "class objects" hold variables that are shared amongst the instances // of the class when they are created. It is possible to adjust values on a // class object and automatically impact all memebers of the class. // // Note: This is not a function most people need to know about or use. // Additionally, the class object is not created until the first instance // of the class is made. // // sClassName: This is the name of the class that you are looking for, like // "generic" or "fighter" or "bartender". // object MeGetClassObject(string sClassName); // ---- INHERITED VARIABLE ACCESS FUNCTIONS------------------------------------- // MeSetLocalInt // File: h_class // This sets an integer variable on an object. If the object inherits data from another // object, or from a class, the value is stored locally, overriding the source of the inheritance. void MeSetLocalInt(object oObject, string sVarName, int nValue); // MeSetLocalFloat // File: h_class // This sets a float variable on an object. If the object inherits data from another // object, or from a class, the value is stored locally, overriding the source of the inheritance. void MeSetLocalFloat(object oObject, string sVarName, float fValue); // MeSetLocalString // File: h_class // This sets a string variable on an object. If the object inherits data from another // object, or from a class, the value is stored locally, overriding the source of the inheritance. void MeSetLocalString(object oObject, string sVarName, string sValue); // MeSetLocalObject // File: h_class // This sets an object variable on an object. If the object inherits data from another // object, or from a class, the value is stored locally, overriding the source of the inheritance. void MeSetLocalObject(object oObject, string sVarName, object oValue); // MeSetLocalLocation // File: h_class // This sets a location variable on an object. If the object inherits data from another // object, or from a class, the value is stored locally, overriding the source of the inheritance. void MeSetLocalLocation(object oObject, string sVarName, location lValue); // MeSetLocalMessage // File: h_class // This sets a memetic message on an object. If the object inherits data from another // object, or from a class, the value is stored locally, overriding the source of the inheritance. void MeSetLocalMessage(object oTarget, string sMessageName, struct message sMessage); // MeGetLocalInt // File: h_class // This gets an integer variable from an object with a given name. If the object inherits // data from another object or is an instance of a class, this function will get // the value from source of the inheritance. Of course, if the value has been // changed on this object, the overidden value is retrieved, instead. int MeGetLocalInt(object oObject, string sVarName); // MeGetLocalFloat // File: h_class // This gets a float variable from an object with a given name. If the object inherits // data from another object or is an instance of a class, this function will get // the value from source of the inheritance. Of course, if the value has been // changed on this object, the overidden value is retrieved, instead. float MeGetLocalFloat(object oObject, string sVarName); // MeGetLocalString // File: h_class // This gets an string variable from an object with a given name. If the object inherits // data from another object or is an instance of a class, this function will get // the value from source of the inheritance. Of course, if the value has been // changed on this object, the overidden value is retrieved, instead. string MeGetLocalString(object oObject, string sVarName); // MeGetLocalObject // File: h_class // This gets an object variable from an object with a given name. If the object inherits // data from another object or is an instance of a class, this function will get // the value from source of the inheritance. Of course, if the value has been // changed on this object, the overidden value is retrieved, instead. object MeGetLocalObject(object oObject, string sVarName); // MeGetLocalLocation // File: h_class // This gets a location variable from an object with a given name. If the object inherits // data from another object or is an instance of a class, this function will get // the value from source of the inheritance. Of course, if the value has been // changed on this object, the overidden value is retrieved, instead. location MeGetLocalLocation(object oObject, string sVarName); // MeGetLocalMessage // File: h_class // This gets a memetic message from an object with a given name. If the object inherits // data from another object or is an instance of a class, this function will get // the value from source of the inheritance. Of course, if the value has been // changed on this object, the overidden value is retrieved, instead. struct message MeGetLocalMessage(object oTarget, string sMessageName); // MeDeleteLocalMessage // File: h_class // This deletes the memetic message datastructure from an object. It doesn't // have anything to do with classes or inheritance. It just removes the // variable like a call to DeleteLocalInt or DeleteLocalString. void MeDeleteLocalMessage(object oTarget, string sMessageName); // ---- 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); } void MeDeclareLocalMessage(string sMessageName, object oTarget = OBJECT_INVALID) { if (oTarget == OBJECT_INVALID) oTarget = MEME_SELF; if (oTarget == OBJECT_INVALID) oTarget = OBJECT_SELF; MeDeclareString ("MEME_Msg_"+sMessageName, oTarget); MeDeclareInt ("MEME_Msg_"+sMessageName, oTarget); MeDeclareFloat ("MEME_Msg_"+sMessageName, oTarget); MeDeclareLocation("MEME_Msg_"+sMessageName, oTarget); MeDeclareObject ("MEME_Msg_"+sMessageName, oTarget); MeDeclareString ("MEME_Msg_"+sMessageName, oTarget); MeDeclareString ("MEME_Msg_"+sMessageName, oTarget); MeDeclareObject ("MEME_MsgSnd_"+sMessageName, oTarget); MeDeclareObject ("MEME_MsgRcv_"+sMessageName, 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); } // Note: inheritance not supported. struct message MeGetLocalMessage(object oTarget, string sMessageName) { struct message sMessage; sMessage.sData = MeGetLocalString (oTarget, "MEME_Msg_"+sMessageName); sMessage.iData = MeGetLocalInt (oTarget, "MEME_Msg_"+sMessageName); sMessage.fData = MeGetLocalFloat (oTarget, "MEME_Msg_"+sMessageName); sMessage.lData = MeGetLocalLocation(oTarget, "MEME_Msg_"+sMessageName); sMessage.oData = MeGetLocalObject (oTarget, "MEME_Msg_"+sMessageName); sMessage.sChannelName = MeGetLocalString (oTarget, "MEME_MsgCh_"+sMessageName); sMessage.sMessageName = MeGetLocalString (oTarget, "MEME_MsgNm_"+sMessageName); sMessage.oSender = MeGetLocalObject (oTarget, "MEME_MsgSnd_"+sMessageName); sMessage.oTarget = MeGetLocalObject (oTarget, "MEME_MsgRcv_"+sMessageName); return sMessage; } 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); } // Note: inheritance not supported. void MeSetLocalMessage(object oTarget, string sMessageName, struct message sMessage) { // First we store the five data fields, MEME_Msg_ is a prefix to avoid colliding // with other user variables or other data. MeSetLocalString (oTarget, "MEME_Msg_"+sMessageName, sMessage.sData); MeSetLocalInt (oTarget, "MEME_Msg_"+sMessageName, sMessage.iData); MeSetLocalFloat (oTarget, "MEME_Msg_"+sMessageName, sMessage.fData); MeSetLocalLocation(oTarget, "MEME_Msg_"+sMessageName, sMessage.lData); MeSetLocalObject (oTarget, "MEME_Msg_"+sMessageName, sMessage.oData); // Next we store the message routing information. Although these fields // are read only, I use this function internally and use these values. MeSetLocalString (oTarget, "MEME_MsgCh_"+sMessageName, sMessage.sChannelName); MeSetLocalString (oTarget, "MEME_MsgNm_"+sMessageName, sMessage.sMessageName); // Finally we store the transient ownership object references MeSetLocalObject (oTarget, "MEME_MsgSnd_"+sMessageName, sMessage.oSender); MeSetLocalObject (oTarget, "MEME_MsgRcv_"+sMessageName, sMessage.oTarget); } // Note: inheritance not supported. void MeDeleteLocalMessage(object oTarget, string sMessageName) { DeleteLocalString (oTarget, "MEME_Msg_"+sMessageName); DeleteLocalInt (oTarget, "MEME_Msg_"+sMessageName); DeleteLocalFloat (oTarget, "MEME_Msg_"+sMessageName); DeleteLocalLocation(oTarget, "MEME_Msg_"+sMessageName); DeleteLocalObject (oTarget, "MEME_Msg_"+sMessageName); DeleteLocalString (oTarget, "MEME_MsgCh_"+sMessageName); DeleteLocalString (oTarget, "MEME_MsgNm_"+sMessageName); DeleteLocalObject (oTarget, "MEME_MsgSnd_"+sMessageName); DeleteLocalObject (oTarget, "MEME_MsgRcv_"+sMessageName); } //------------------------------------------------------------------------------ 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 = GetLocalObject(oTarget, "MEME_Parent"); string sKey = GetLocalString(oParent, "MEME_ClassKey"); string sFullName = GetLocalString(oParent, "Name"); // 6. Merge it with each of the new class keys string sNewKey; for (count = MeGetStringCount(oTarget, "MEME_NewParents"); count > 0; count--) { sNewName = MeGetStringByIndex(oTarget, count-1, "MEME_NewParents"); sFullName += "/"+sNewName; sNewKey = GetLocalString(oModule, "MEME_ClassKey_"+sNewName); sKey = _MeKeyCombine(sNewKey, sKey); } _End("MergeList", DEBUG_UTILITY); // 7. Does this new class key exist? Is this a newly discovered class combination? A new breed of NPC? Could it be?? object oNewClass = GetLocalObject(oModule, "MEME_ClassKey_"+sKey); object oClass, oOwner; if (oNewClass == OBJECT_INVALID) { _Start("NewClass", DEBUG_UTILITY); //_PrintString("I've never seen this combination of classes, let's set you up.", DEBUG_UTILITY); // 8. Create the new merged class object oMemeMagicWP = GetObjectByTag("Magic_Memetic_WP"); oNewClass = CreateObject(OBJECT_TYPE_STORE, "Magic_Memetic_Store", GetLocation(oMemeMagicWP)); if (!GetIsObjectValid(oNewClass)) { _PrintString("Error: Failed to create class object represeting merged class combination.", DEBUG_UTILITY); } SetLocalString(oNewClass, "Name", sFullName); // (There is no _ini to call, this is a combined class.) // (There are no names to be stored, only key referencing.) SetLocalString(oNewClass, "MEME_ClassKey", sNewKey); SetLocalObject(oModule, "MEME_ClassKey_"+sNewKey, oNewClass); // 9. Copy the declaration tables from each class, in order // Now honestly, this is a potential area for TMI, this may need to be // split out into asynchronous copy chunks. I would just separate this // whole block into a function and call DelayCommand(0.0, x(a,b,c)); count = MeGetStringCount(oTarget, "MEME_Parents"); //_PrintString("This object now belongs to "+IntToString(count)+" classes.", DEBUG_UTILITY); for (0; count > 0; count--) { sName = MeGetStringByIndex(oTarget, count-1, "MEME_Parents"); oClass = GetLocalObject(oModule, "MEME_Class_"+sName); //_PrintString("Merging the declaration tables from class "+sName+".", DEBUG_UTILITY); // Each class may have declared variables of nine types, we // must transfer those definitions to the merged class. // This process is done once to be able to efficiently look up // owners of variables with a nearly-direct access lookup - // O(1) vs. O(n) order of complexity when calling MeGet*(). _Start("CopyAllDeclTables", DEBUG_UTILITY); _CopyDeclTable(oClass, oNewClass, "DECL_F"); _CopyDeclTable(oClass, oNewClass, "DECL_FL"); _CopyDeclTable(oClass, oNewClass, "DECL_O"); _CopyDeclTable(oClass, oNewClass, "DECL_OL"); _CopyDeclTable(oClass, oNewClass, "DECL_I"); _CopyDeclTable(oClass, oNewClass, "DECL_IL"); _CopyDeclTable(oClass, oNewClass, "DECL_L"); //_CopyDeclTable(oClass, oNewClass, "DECL_L"); // Lists of locations are not supported yet _CopyDeclTable(oClass, oNewClass, "DECL_S"); _CopyDeclTable(oClass, oNewClass, "DECL_SL"); _End("CopyAllDeclTables", DEBUG_UTILITY); // Conceivably here is where we would also assess the flags to // optionally copy the value instead of inheriting the value. } _End("NewClass", DEBUG_UTILITY); } else { //_PrintString("Oh, I know what class this is." , DEBUG_UTILITY); } // 10. Set the Parent to be the new merged class object _Start("ConnectToNewClass", DEBUG_UTILITY); SetLocalObject(oTarget, "MEME_Parent", oNewClass); if (!GetIsObjectValid(oNewClass)) { //_PrintString("Error: Failed to create class object represeting merged class combination.", DEBUG_UTILITY); } //_PrintString("Now that I know about this class, I'll set your MEME_Parent to it. ("+_GetName(oTarget)+"->"+_GetName(oNewClass)+")", DEBUG_UTILITY); // 11. For each new class, MeExecuteScript() name_go count = MeGetStringCount(oTarget, "MEME_NewParents"); //_PrintString("There are "+IntToString(count)+" classes to instantiate.", DEBUG_UTILITY); for (0; count > 0; count--) { sNewName = MeGetStringByIndex(oTarget, count-1, "MEME_NewParents"); // Direct access for efficiency oNewClass = GetLocalObject(oModule, "MEME_Class_"+sNewName); DelayCommand(0.0, MeExecuteScript("c_"+sNewName,"_go", OBJECT_SELF, oNewClass)); } // 12. Clean up that new parent list. MeDeleteStringRefs(oTarget, "MEME_NewParents"); _End("ConnectToNewClass", DEBUG_UTILITY); _End("MeInstanceOf", DEBUG_UTILITY); } /* I support two forms of variable inheritance. This distinction is made to * reduce memory consumption and increase the flexibility of the code. * * First, an object may inherit variables from a named, shared, "class object". * It will inherit all the variables on the class and the variables it inherits. * * Alternatively, an object may inherit values from a "parent" non-class object. * But this will only inherit the variables the parent has explicitly declared. * If the parent inherits variables from another object, these will not be inherited. * * Any object can have declared variables. * Any object can inherit variables from parent objects. * A class is an invisible object that is globally accessible by name. * A class copies all the variable declarations * An object inherits all the variables a class inherits -- but only a class. */ /* Class Variables --------------- 1) Module Object int MEME_KeyCount object MEME_CLASS_+ 2) Class (Parent) Objects string MEME_ClassName string MEME_ClassKey 3) Inheriting (Child) Objects object MEME_Parent 4) Objects with Declared Variables */ /* Constants for the Memetic Toolkit * William Bull * September, 2002 */ // ----- Datatypes ------------------------------------------------------------- struct message { // Define the name of the message. Anonymous messages are ok, but are // only received by events that have subscribed to all messages. // Events subscribe to all messages by subscribing to the null string, "". string sMessageName; // Message Data string sData; int iData; float fData; location lData; object oData; // These are normally filled in for you. The assumption is that // messages will be resent by various senders to various receivers. // As a result, these are defined when the message is sent on a particular // channel, by a particular object to a particular object. object oSender; object oTarget; string sChannelName; }; // ----- POI Sizes ------------------------------------------------------------- const float POI_LARGE = 10.0; const float POI_SMALL = 5.0; const int AOE_LARGE_POI = AOE_PER_INVIS_SPHERE; const int AOE_SMALL_POI = 37; // ----- Observer Meme --------------------------------------------------------- const int SIGNAL_OBSERVER = 0x02021; // Some arbitrary signal trigger. const int NOTIFY_ARM = 0x000001; const int NOTIFY_DISARM = 0x000002; const int NOTIFY_APPEAR = 0x000004; const int NOTIFY_VANISH = 0x000008; const int NOTIFY_ATTACK = 0x000010; const int NOTIFY_DEFEND = 0x000020; const int NOTIFY_CAST_AT = 0x000040; // Requires changes to all spell scipts. const int NOTIFY_CAST_ON = 0x000080; // Requires changes to all spell scipts. const int NOTIFY_ENEMY = 0x000100; const int NOTIFY_FRIEND = 0x000200; const int NOTIFY_PC = 0x000400; const int NOTIFY_NPC = 0x000800; const int NOTIFY_DM = 0x001000; const int NOTIFY_DEAD = 0x002000; const int NOTIFY_ALIVE = 0x004000; const int NOTIFY_HEALTHY = 0x008000; const int NOTIFY_BRUISED = 0x010000; const int NOTIFY_WOUNDED = 0x020000; const int NOTIFY_HURT = 0x040000; const int NOTIFY_BADLYHURT = 0x080000; const int NOTIFY_NEARDEATH = 0x100000; const int NOTIFY_HEALTH_INC = 0x200000; const int NOTIFY_HEALTH_DEC = 0x400000; // ----- Core Meme Constants --------------------------------------------------- // Event Constants const int MEME_EVENT = 0x001; const int GENERATOR_EVENT = 0x002; const int ALL_TRIGGERS = 9999999; // General Meme Constants const int SEQ_REPEAT = 0x001; const int SEQ_RESUME_FIRST = 0x002; const int SEQ_RESUME_LAST = 0x004; const int MEME_REPEAT = 0x008; const int MEME_RESUME = 0x010; const int MEME_CHECKPOINT = 0x020; const int MEME_INSTANT = 0x040; const int MEME_IMMEDIATE = 0x080; const int EVENT_PERSISTANT = 0x200; const int EVENT_REPEAT = 0x400; const int MEME_CHILDREN = 0x800; // PoI Emitter Constants //const int EMIT_MANY = 0x00; (Not implemented) //const int EMIT_ONCE_TO_ONE = 0x01; (Not implemented) //const int EMIT_ONCE_TO_MANY = 0x02; (Not implemented) //const int EMIT_ONCE = 0x04; (Not implemented) const int EMIT_TO_PC = 0x08; const int EMIT_TO_DM = 0x10; // This may not work; depending on if Bioware lets DM's trigger AoEs. const int EMIT_TO_NPC = 0x20; int EMIT_TO_ALL = EMIT_TO_PC | EMIT_TO_DM | EMIT_TO_NPC; // Meme Priority Constants const int PRIO_DEFAULT = 0; const int PRIO_NONE = 1; const int PRIO_LOW = 2; const int PRIO_MEDIUM = 3; const int PRIO_HIGH = 4; const int PRIO_VERYHIGH = 5; // Generator Constants const int GEN_SINGLEUSE = 1; //const int GEN_PROPOGATE_PRIO = 0x100; (Not implemented) const int TIME_ONE_MINUTE = 60; const int TIME_ONE_HOUR = 3600; const int TIME_ONE_DAY = 86400; // Variable Declaration Constants const int VAR_INHERIT = 0x01; //const int VAR_INHERIT_COPY = 0x02; (Not implemented) //const int VER_INHERIT_FORCE = 0x04; (Not implemented) //const int VAR_PERSISTANT = 0x08; (Not implemented) //const int VAR_EXPIRE = 0x10; (Not implemented) // ----- Private --------------------------------------------------------------- object MEME_SELF = GetLocalObject (OBJECT_SELF, "MEME_ObjectSelf"); object NPC_SELF = GetLocalObject (OBJECT_SELF, "MEME_NPCSelf"); const int TYPE_MEME = 0x00001; const int TYPE_SEQ_REF = 0x00002; const int TYPE_GENERATOR = 0x00004; const int TYPE_EVENT = 0x00008; const int TYPE_SEQUENCE = 0x00010; const int TYPE_MEME_BAG = 0x00020; const int TYPE_GENERATOR_BAG = 0x00040; const int TYPE_EVENT_BAG = 0x00080; const int TYPE_SEQUENCE_BAG = 0x00100; const int TYPE_PRIO_BAG = 0x00200; const int TYPE_PRIO_BAG1 = 0x00400; const int TYPE_PRIO_BAG2 = 0x00800; const int TYPE_PRIO_BAG3 = 0x01000; const int TYPE_PRIO_BAG4 = 0x02000; const int TYPE_PRIO_BAG5 = 0x04000; const int TYPE_PRIO_SUSPEND = 0x08000; const int TYPE_CLASS = 0x10000; const int TYPE_NPC_SELF = 0x20000; /* Constants for the lib_combat * Joel Martin * May, 2003 */ // ----- SIGNALS --------------------------------------------------------------- const int CH_DEAD = 691; const int CH_COMBAT = 699; const int CH_ALL = 700; // ----- EFFECTS --------------------------------------------------------------- const int BLINDNESS = 32; /* * Title: Memetic AI Debugging Functions * Documentation: http://www.memeticai.org/api/toc.html?prototype=true&group=3 * Contributors: William Bull, Lomin */ const int DEBUG_ACTIVE = 1; const int DEBUG_ALL = -1; const int DEBUG_NONE = 0x00; const in