/* =========================================================================== Copyright (C) 2015 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenMoHAA source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // listener.cpp : Listener. #include "../script/scriptvariable.h" #include "../script/scriptexception.h" #include "Linklist.h" #ifdef WITH_SCRIPT_ENGINE # include "../fgame/archive.h" # include "../fgame/scriptmaster.h" # include "../fgame/scriptthread.h" # include "../script/scriptclass.h" #endif #if defined(GAME_DLL) # include "../fgame/player.h" # include "../fgame/consoleevent.h" # include "../fgame/animationevent.h" # define LISTENER_Cvar_Get gi.Cvar_Get #elif defined(CGAME_DLL) # define LISTENER_Cvar_Get cgi.Cvar_Get #elif defined(UI_LIB) #else # define LISTENER_Cvar_Get Cvar_Get # include "../client/client.h" #endif // std::move #include DataNode *Event::DataNodeList = NULL; con_map Event::eventDefList; con_arrayset Event::commandList; #ifdef WITH_SCRIPT_ENGINE con_map Event::normalCommandList; con_map Event::returnCommandList; con_map Event::getterCommandList; con_map Event::setterCommandList; #endif EventQueueNode Event::EventQueue; int DisableListenerNotify = 0; bool Listener::EventSystemStarted = 0; int Event::totalevents = 1; Event EV_Listener_CancelFor ( "cancelFor", EV_DEFAULT, "s", "name", "Cancel for event of type name.", EV_NORMAL ); Event EV_Listener_CommandDelay ( "commanddelay", EV_DEFAULT, "fsSSSSSS", "delay command arg1 arg2 arg3 arg4 arg5 arg6", "executes a command after the given delay.", EV_NORMAL ); Event EV_Listener_Classname ( "classname", EV_DEFAULT, NULL, NULL, "classname variable", EV_GETTER ); Event EV_Listener_SetClassname ( "classname", EV_DEFAULT, "s", "classname", "classname variable", EV_NORMAL ); Event EV_Listener_CreateReturnThread ( "thread", EV_DEFAULT, "s", "label", "Creates a thread starting at label.", EV_RETURN ); Event EV_Listener_CreateThread ( "thread", EV_DEFAULT, "s", "label", "Creates a thread starting at label.", EV_NORMAL ); Event EV_Listener_ExecuteReturnScript ( "exec", EV_DEFAULT, "s", "script", "Executes the specified script.", EV_RETURN ); Event EV_Listener_ExecuteScript ( "exec", EV_DEFAULT, "s", "script", "Executes the specified script.", EV_NORMAL ); Event EV_Delete ( "delete", EV_DEFAULT, NULL, NULL, "Removes this listener immediately.", EV_NORMAL ); Event EV_Remove ( "immediateremove", EV_DEFAULT, NULL, NULL, "Removes this listener immediately.", EV_NORMAL ); Event EV_ScriptRemove ( "remove", EV_DEFAULT, NULL, NULL, "Removes this listener the next time events are processed.", EV_NORMAL ); Event EV_Listener_EndOn ( "endon", EV_DEFAULT, "s", "name", "Ends the function when the specified event is triggered.", EV_NORMAL ); Event EV_Listener_InheritsFrom ( "inheritsfrom", EV_DEFAULT, "s", "class", "Returns 1 if the class inherits from the specified class. 0 otherwise.", EV_RETURN ); Event EV_Listener_IsInheritedBy ( "isinheritedby", EV_DEFAULT, "s", "class", "Returns 1 if the class is inherited by the specified class. 0 otherwise.", EV_RETURN ); Event EV_Listener_Notify ( "notify", EV_DEFAULT, "s", "name", "Triggers an event. An undefined event will be automatically created by calling waittill or endon.", EV_NORMAL ); Event EV_Listener_GetOwner ( "owner", EV_DEFAULT, NULL, NULL, "Returns the owner.", EV_GETTER ); Event EV_DelayThrow ( "delaythrow", EV_DEFAULT, "s", "label", "Internal usage.", EV_NORMAL ); Event EV_Throw ( "throw", EV_DEFAULT, "s", "label", "Throws to the specified label.", EV_NORMAL ); Event EV_Listener_Unregister ( "unregister", EV_DEFAULT, "s", "label", "Unregisters the label from the event of the same name.", EV_NORMAL ); Event EV_Listener_WaitCreateReturnThread ( "waitthread", EV_DEFAULT, "s", "label", "Creates a thread starting at label and waits until the called thread is finished.", EV_RETURN ); Event EV_Listener_WaitCreateThread ( "waitthread", EV_DEFAULT, "s", "label", "Creates a thread starting at label and waits until the called thread is finished.", EV_NORMAL ); Event EV_Listener_WaitExecuteReturnScript ( "waitexec", EV_DEFAULT, "s", "script", "Executes the specified script and waits until the called thread group is finished.", EV_RETURN ); Event EV_Listener_WaitExecuteScript ( "waitexec", EV_DEFAULT, "s", "script", "Executes the specified script and waits until the called thread group is finished.", EV_NORMAL ); Event EV_Listener_WaitTill ( "waitTill", EV_DEFAULT, "s", "name", "Wait until event of type name", EV_NORMAL ); Event EV_Listener_WaitTillTimeout ( "waittill_timeout", EV_DEFAULT, "fs", "timeout_time name", "Wait until event of type name with a timeout time", EV_NORMAL ); Event EV_Listener_WaitTillAny ( "waittill_any", EV_DEFAULT, "sS", "name1 ...", "Wait until any event of type name", EV_NORMAL ); Event EV_Listener_WaitTillAnyTimeout ( "waittill_any_timeout", EV_DEFAULT, "fsS", "timeout_time name1 ...", "Wait until any event of type name with a timeout time", EV_NORMAL ); CLASS_DECLARATION(Class, Listener, NULL) { {&EV_Listener_CommandDelay, &Listener::CommandDelay }, {&EV_Remove, &Listener::Remove }, {&EV_Delete, &Listener::ScriptRemove }, {&EV_ScriptRemove, &Listener::ScriptRemove }, {&EV_Listener_Classname, &Listener::GetClassname }, {&EV_Listener_InheritsFrom, &Listener::EventInheritsFrom }, {&EV_Listener_IsInheritedBy, &Listener::EventIsInheritedBy }, #ifdef WITH_SCRIPT_ENGINE {&EV_Listener_CancelFor, &Listener::CancelFor }, {&EV_Listener_CreateReturnThread, &Listener::CreateReturnThread }, {&EV_Listener_CreateThread, &Listener::CreateThread }, {&EV_Listener_ExecuteReturnScript, &Listener::ExecuteReturnScript }, {&EV_Listener_ExecuteScript, &Listener::ExecuteScript }, {&EV_Listener_EndOn, &Listener::EventEndOn }, {&EV_Listener_GetOwner, &Listener::EventGetOwner }, {&EV_Listener_Notify, &Listener::EventNotify }, {&EV_DelayThrow, &Listener::EventDelayThrow }, {&EV_Throw, &Listener::EventThrow }, {&EV_Listener_Unregister, &Listener::EventUnregister }, {&EV_Listener_WaitCreateReturnThread, &Listener::WaitCreateReturnThread }, {&EV_Listener_WaitCreateThread, &Listener::WaitCreateThread }, {&EV_Listener_WaitExecuteReturnScript, &Listener::WaitExecuteReturnScript}, {&EV_Listener_WaitExecuteScript, &Listener::WaitExecuteScript }, {&EV_Listener_WaitTill, &Listener::WaitTill }, {&EV_Listener_WaitTillTimeout, &Listener::WaitTillTimeout }, {&EV_Listener_WaitTillAny, &Listener::WaitTillAny }, {&EV_Listener_WaitTillAnyTimeout, &Listener::WaitTillAnyTimeout }, #endif {NULL, NULL } }; cvar_t *g_showevents; cvar_t *g_eventlimit; cvar_t *g_timeevents; cvar_t *g_watch; cvar_t *g_eventstats; template<> int HashCode(Event *const& key) { // can't use key->eventnum because eventnum will be assigned from con_set return (int)(size_t)key; } template<> int HashCode(const command_t& key) { const char* p; int hash = 0; for (p = key.command; *p; p++) { hash = tolower(*p) + 31 * hash; } return hash; } #if defined(ARCHIVE_SUPPORTED) void ArchiveListenerPtr(Archiver& arc, SafePtr *obj) { arc.ArchiveSafePointer(obj); } template<> void ConList::Archive(Archiver& arc) { value.Archive(arc, ArchiveListenerPtr); } template<> void con_set::Entry::Archive(Archiver& arc) { Director.ArchiveString(arc, key); value.Archive(arc); } /* ======================= Archive ======================= */ void Event::Archive(Archiver& arc) { if (arc.Loading()) { fromScript = false; } Class::Archive(arc); arc.ArchiveUnsignedShort(&eventnum); arc.ArchiveUnsignedShort(&dataSize); if (arc.Loading()) { data = new ScriptVariable[dataSize + 1]; } for (int i = dataSize; i > 0; i--) { data[i].ArchiveInternal(arc); } } void L_ArchiveEvents(Archiver& arc) { EventQueueNode *event; int num; num = 0; for (event = Event::EventQueue.next; event != &Event::EventQueue; event = event->next) { Listener *obj; assert(event); obj = event->GetSourceObject(); assert(obj); # if defined(GAME_DLL) if (obj->isSubclassOf(Entity) && (((Entity *)obj)->flags & FL_DONTSAVE)) { continue; } # endif num++; } arc.ArchiveInteger(&num); for (event = Event::EventQueue.next; event != &Event::EventQueue; event = event->next) { Listener *obj; assert(event); obj = event->GetSourceObject(); assert(obj); # if defined(GAME_DLL) if (obj->isSubclassOf(Entity) && (((Entity *)obj)->flags & FL_DONTSAVE)) { continue; } # endif event->event->Archive(arc); arc.ArchiveInteger(&event->inttime); arc.ArchiveInteger(&event->flags); arc.ArchiveSafePointer(&event->m_sourceobject); } } void L_UnarchiveEvents(Archiver& arc) { EventQueueNode *node; int i, numEvents; // the FreeEvents list would already be allocated at this point // clear out any events that may exist L_ClearEventList(); arc.ArchiveInteger(&numEvents); for (i = 0; i < numEvents; i++) { node = new EventQueueNode(); node->event = new Event(); node->event->Archive(arc); arc.ArchiveInteger(&node->inttime); arc.ArchiveInteger(&node->flags); arc.ArchiveSafePointer(&node->m_sourceobject); LL_Add(&Event::EventQueue, node, next, prev); } } #endif void L_ClearEventList() { EventQueueNode *node = Event::EventQueue.next, *tmp; while (node != &Event::EventQueue) { tmp = node->next; delete node->event; delete node; node = tmp; } LL_Reset(&Event::EventQueue, next, prev); Event_allocator.FreeAll(); #if defined(GAME_DLL) AnimationEvent_allocator.FreeAll(); ConsoleEvent_allocator.FreeAll(); #endif } bool L_EventSystemStarted(void) { return Listener::EventSystemStarted; } void L_InitEvents(void) { g_showevents = LISTENER_Cvar_Get("g_showevents", "0", 0); g_eventlimit = LISTENER_Cvar_Get("g_eventlimit", "5000", 0); g_timeevents = LISTENER_Cvar_Get("g_timeevents", "0", 0); g_watch = LISTENER_Cvar_Get("g_watch", "0", 0); g_eventstats = LISTENER_Cvar_Get("g_eventstats", "0", 0); Event::LoadEvents(); ClassDef::BuildEventResponses(); LL_Reset(&Event::EventQueue, next, prev); L_ClearEventList(); Listener::EventSystemStarted = true; } void L_ProcessPendingEvents() { EventQueueNode *node; int t = EVENT_msec; while (!LL_Empty(&Event::EventQueue, next, prev)) { Listener *obj; node = Event::EventQueue.next; assert(node); obj = node->GetSourceObject(); assert(obj); if (node->inttime > t) { break; } // the event is removed from its list LL_Remove(node, next, prev); //gi.DPrintf2("Event: %s\n", node->event->getName().c_str()); // ProcessEvent will dispose of this event when it is done obj->ProcessEvent(node->event); delete node; } } void L_ShutdownEvents(void) { if (!Listener::EventSystemStarted) { return; } L_ClearEventList(); Event::commandList.clear(); Event::eventDefList.clear(); Listener::EventSystemStarted = false; } //=========================================================================== // EventArgDef //=========================================================================== void EventArgDef::Setup(const char *eventName, const char *argName, const char *argType, const char *argRange) { char scratch[256]; const char *ptr; char *tokptr; const char *endptr; int index; // set name name = argName; // set optionality if (isupper(argType[0])) { optional = true; } else { optional = false; } // grab the ranges index = 0; memset(minRangeDefault, true, sizeof(minRangeDefault)); memset(minRange, 0, sizeof(minRange)); memset(maxRangeDefault, true, sizeof(maxRangeDefault)); memset(maxRange, 0, sizeof(maxRange)); if (argRange && argRange[0]) { ptr = argRange; while (1) { // find opening '[' tokptr = (char *)strchr(ptr, '['); if (!tokptr) { break; } // find closing ']' endptr = strchr(tokptr, ']'); if (!endptr) { assert(0); EVENT_Printf( "Argument defintion %s, no matching ']' found for range spec in event %s.\n", name.c_str(), eventName ); break; } // point to the next range ptr = endptr; // skip the '[' tokptr++; // copy off the range spec // skip the ']' strncpy(scratch, tokptr, endptr - tokptr); // terminate the range scratch[endptr - tokptr] = 0; // see if there is one or two parameters here tokptr = strchr(scratch, ','); if (!tokptr) { // just one parameter minRange[index >> 1] = (float)atof(scratch); minRangeDefault[index >> 1] = false; index++; // skip the second parameter index++; } else if (tokptr == scratch) { // just second parameter // skip the first paremeter index++; tokptr++; maxRange[index >> 1] = (float)atof(scratch); maxRangeDefault[index >> 1] = false; index++; } else { qboolean second; // one or two parameters // see if there is anything behind the ',' if (strlen(scratch) > (tokptr - scratch + 1)) { second = true; } else { second = false; } // zero out the ',' *tokptr = 0; minRange[index >> 1] = (float)atof(scratch); minRangeDefault[index >> 1] = false; index++; // skip over the nul character tokptr++; if (second) { maxRange[index >> 1] = (float)atof(tokptr); maxRangeDefault[index >> 1] = false; } index++; } } } // figure out the type of variable it is switch (tolower(argType[0])) { case 'e': type = IS_ENTITY; break; case 'v': type = IS_VECTOR; break; case 'i': type = IS_INTEGER; break; case 'f': type = IS_FLOAT; break; case 's': type = IS_STRING; break; case 'b': type = IS_BOOLEAN; break; case 'l': type = IS_LISTENER; break; } } void EV_Print(FILE *stream, const char *format, ...) { char buffer[1000]; va_list va; va_start(va, format); vsprintf(buffer, format, va); if (stream) { fprintf(stream, "%s", buffer); } else { EVENT_DPrintf("%s", buffer); } va_end(va); } void EventArgDef::PrintRange(FILE *event_file) { qboolean integer; qboolean single; int numRanges; int i; single = false; integer = true; numRanges = 1; switch (type) { case IS_VECTOR: integer = false; numRanges = 3; break; case IS_FLOAT: integer = false; break; case IS_STRING: single = true; break; } for (i = 0; i < numRanges; i++) { if (single) { if (!minRangeDefault[i]) { if (integer) { EV_Print(event_file, "<%d>", (int)minRange[i]); } else { EV_Print(event_file, "<%.2f>", minRange[i]); } } } else { // both non-default if (!minRangeDefault[i] && !maxRangeDefault[i]) { if (integer) { EV_Print(event_file, "<%d...%d>", (int)minRange[i], (int)maxRange[i]); } else { EV_Print(event_file, "<%.2f...%.2f>", minRange[i], maxRange[i]); } } // max default else if (!minRangeDefault[i] && maxRangeDefault[i]) { if (integer) { EV_Print(event_file, "<%d...max_integer>", (int)minRange[i]); } else { EV_Print(event_file, "<%.2f...max_float>", minRange[i]); } } // min default else if (minRangeDefault[i] && !maxRangeDefault[i]) { if (integer) { EV_Print(event_file, "", (int)maxRange[i]); } else { EV_Print(event_file, "", maxRange[i]); } } } } } void EventArgDef::PrintArgument(FILE *event_file) { if (optional) { EV_Print(event_file, "[ "); } switch (type) { case IS_ENTITY: EV_Print(event_file, "Entity "); break; case IS_VECTOR: EV_Print(event_file, "Vector "); break; case IS_INTEGER: EV_Print(event_file, "Integer "); break; case IS_FLOAT: EV_Print(event_file, "Float "); break; case IS_STRING: EV_Print(event_file, "String "); break; case IS_BOOLEAN: EV_Print(event_file, "Boolean "); break; case IS_LISTENER: EV_Print(event_file, "Listener "); break; } EV_Print(event_file, "%s", name.c_str()); PrintRange(event_file); if (optional) { EV_Print(event_file, " ]"); } } void EventDef::Error(const char *format, ...) { char buffer[1000]; va_list va; va_start(va, format); vsprintf(buffer, format, va); va_end(va); EVENT_Printf("^~^~^ Game: '%s' : %s\n", command.c_str(), buffer); } void EventDef::PrintDocumentation(FILE *event_file, bool html) { int i; int p; str text; const char *name = command.c_str(); if (!html) { text = " "; p = 0; if (flags & EV_CONSOLE) { text[p++] = '*'; } if (flags & EV_CHEAT) { text[p++] = 'C'; } if (flags & EV_CACHE) { text[p++] = '%'; } } if (html) { EV_Print(event_file, "\n

%s", name); } else { if (text[0] != ' ') { EV_Print(event_file, "%s %s", text.c_str(), name); } else { EV_Print(event_file, "%s %s", text.c_str(), name); } } SetupDocumentation(); if (definition) { if (html) { EV_Print(event_file, "( "); } else { EV_Print(event_file, "( "); } for (i = 1; i <= definition->NumObjects(); i++) { definition->ObjectAt(i).PrintArgument(event_file); if (i < definition->NumObjects()) { EV_Print(event_file, ", "); } } if (html) { EV_Print(event_file, " )
\n"); } else { EV_Print(event_file, " )\n"); } DeleteDocumentation(); } else { if (html) { EV_Print(event_file, "
\n"); } else { EV_Print(event_file, "\n"); } } if (documentation) { char new_doc[1024]; int old_index; int new_index = 0; for (old_index = 0; old_index < strlen(documentation); old_index++) { if (documentation[old_index] == '\n') { if (html) { new_doc[new_index] = '<'; new_doc[new_index + 1] = 'B'; new_doc[new_index + 2] = 'R'; new_doc[new_index + 3] = '>'; new_doc[new_index + 4] = '\n'; new_index += 5; } else { new_doc[new_index] = '\n'; new_doc[new_index + 1] = '\t'; new_doc[new_index + 2] = '\t'; new_index += 3; } } else { new_doc[new_index] = documentation[old_index]; new_index++; } } new_doc[new_index] = 0; if (html) { EV_Print(event_file, "

    %s
\n", new_doc); } else { EV_Print(event_file, "\t\t- %s\n", new_doc); } } } void EventDef::PrintEventDocumentation(FILE *event_file, bool html) { if (flags & EV_CODEONLY) { return; } // purposely suppressed if (command[0] == '_') { return; } PrintDocumentation(event_file, html); } void EventDef::DeleteDocumentation(void) { if (formatspec) { if (argument_names) { definition->FreeObjectList(); delete definition; definition = NULL; } } } void EventDef::SetupDocumentation(void) { const char *name = command.c_str(); // setup documentation if (formatspec) { if (argument_names) { char argumentNames[256]; str argSpec; str rangeSpec; str argName; EventArgDef argDef; const char *namePtr; const char *specPtr; size_t specLength; int index; Container argNames; specLength = strlen(formatspec); specPtr = formatspec; // // store off all the names // strcpy(argumentNames, argument_names); namePtr = strtok(argumentNames, " "); while (namePtr != NULL) { argNames.AddObject(str(namePtr)); namePtr = strtok(NULL, " "); } index = 0; // // create the definition container // definition = new Container; definition->Resize(argNames.NumObjects()); // go throught he formatspec while (specLength) { // clear the rangeSpec rangeSpec = ""; // get the argSpec argSpec = ""; argSpec += *specPtr; specPtr++; specLength--; // see if there is a range specified while (*specPtr == '[') { // add in all the characters until NULL or ']' while (specLength && (*specPtr != ']')) { rangeSpec += *specPtr; specPtr++; specLength--; } if (specLength && (*specPtr == ']')) { rangeSpec += *specPtr; specPtr++; specLength--; } } if (index < argNames.NumObjects()) { argName = argNames.ObjectAt(index + 1); argDef.Setup(name, argName, argSpec, rangeSpec); definition->AddObject(argDef); } else { assert(0); Error("More format specifiers than argument names for event %s\n", name); } index++; } if (index < argNames.NumObjects()) { assert(0); Error("More argument names than format specifiers for event %s\n", name); } } } } //==================================== // Event //==================================== MEM_BlockAlloc Event_allocator; CLASS_DECLARATION(Class, Event, NULL) { {NULL, NULL} }; #ifndef _DEBUG_MEM /* ======================= new Event ======================= */ void *Event::operator new(size_t size) { return Event_allocator.Alloc(); } /* ======================= delete ptr ======================= */ void Event::operator delete(void *ptr) { Event_allocator.Free(ptr); } #endif /* ======================= FindEventNum ======================= */ unsigned int Event::FindEventNum(const char *s) { command_t cmd(s, EV_NORMAL); return commandList.findKeyIndex(cmd); } /* ======================= NumEventCommands Number of total events ======================= */ int Event::NumEventCommands() { return totalevents; } /* ======================= ListCommands List event commands ======================= */ void Event::ListCommands(const char *mask) { command_t *command; int eventnum; int num; int i; int n; size_t l; int p; int hidden; str text; Container *sortedList; if (!commandList.size()) { EVENT_DPrintf("No events.\n"); return; } sortedList = &ClassDef::sortedList; SortEventList(sortedList); l = 0; if (mask) { l = strlen(mask); } hidden = 0; num = 0; n = sortedList->NumObjects(); for (i = 1; i <= n; i++) { eventnum = sortedList->ObjectAt(i); command = &commandList[eventnum]; if (command->flags & EV_CODEONLY) { hidden++; continue; } if (mask && Q_stricmpn(command->command, mask, l)) { continue; } num++; text = " "; p = 0; if (command->flags & EV_CONSOLE) { text[p++] = '*'; } if (command->flags & EV_CHEAT) { text[p++] = 'C'; } if (command->flags & EV_CACHE) { text[p++] = '%'; } EVENT_Printf("%4d : %s%s\n", eventnum, text.c_str(), command->command); } EVENT_Printf( "\n* = console command.\nC = cheat command.\n%% = cache command.\n\n" "Printed %d of %d total commands.\n", num, n - hidden ); if (developer->integer && hidden) { EVENT_Printf("Suppressed %d commands.\n", hidden); } } /* ======================= ListDocumentation List event documentation ======================= */ void Event::ListDocumentation(const char *mask, qboolean print_to_disk) { int num; int n; size_t l; int flags; int hidden; str name; str text; FILE *event_file = NULL; str event_filename; con_map_enum en = eventDefList; EventDef *def; if (print_to_disk) { if (!mask || !mask[0]) { event_filename = EVENT_FILENAME; } else { event_filename = str(mask) + ".txt"; } event_file = fopen(event_filename.c_str(), "w"); if (event_file == NULL) { return; } } l = 0; if (mask) { l = strlen(mask); } EV_Print(event_file, "\nCommand Documentation\n"); EV_Print(event_file, "=====================\n"); hidden = 0; num = 0; n = 0; for (def = en.NextValue(); def != NULL; def = en.NextValue()) { flags = def->flags; name = def->command; n++; if (flags & EV_CODEONLY) { hidden++; continue; } if (mask && Q_stricmpn(name, mask, l)) { continue; } num++; def->PrintDocumentation(event_file, qfalse); } EV_Print( event_file, "\n* = console command.\nC = cheat command.\n% = cache command.\n\n" "Printed %d of %d total commands.\n", num, n - hidden ); if (developer->integer && hidden) { EV_Print(event_file, "Suppressed %d commands.\n", hidden); } if (event_file != NULL) { EVENT_Printf("Printed event info to file %s\n", event_filename.c_str()); fclose(event_file); } } /* ======================= PendingEvents List pending events ======================= */ void Event::PendingEvents(const char *mask) { EventQueueNode *event; size_t l; int num; l = 0; if (mask) { l = strlen(mask); } num = 0; event = EventQueue.next; while (event != &EventQueue) { assert(event); assert(event->m_sourceobject); if (!mask || !Q_stricmpn(event->event->getName(), mask, l)) { num++; //Event::PrintEvent( event ); } event = event->next; } EVENT_Printf("%d pending events as of %.2f\n", num, EVENT_time); } #ifdef WITH_SCRIPT_ENGINE /* ======================= FindNormalEventNum ======================= */ unsigned int Event::FindNormalEventNum(const_str s) { unsigned int *eventnum = normalCommandList.find(s); if (eventnum) { return *eventnum; } else { return 0; } } /* ======================= FindReturnEventNum Finds an event that return a value ======================= */ unsigned int Event::FindReturnEventNum(const_str s) { unsigned int *eventnum = returnCommandList.find(s); if (eventnum) { return *eventnum; } else { return 0; } } /* ======================= FindSetterEventNum Finds an event that act as a write-variable ======================= */ unsigned int Event::FindSetterEventNum(const_str s) { unsigned int *eventnum = setterCommandList.find(s); if (eventnum) { return *eventnum; } else { return 0; } } /* ======================= FindGetterEventNum Finds an event that act as a read-variable ======================= */ unsigned int Event::FindGetterEventNum(const_str s) { unsigned int *eventnum = getterCommandList.find(s); if (eventnum) { return *eventnum; } else { return 0; } } /* ======================= FindNormalEventNum ======================= */ unsigned int Event::FindNormalEventNum(str s) { s.tolower(); return FindNormalEventNum(Director.AddString(s)); } /* ======================= FindReturnEventNum ======================= */ unsigned int Event::FindReturnEventNum(str s) { s.tolower(); return FindReturnEventNum(Director.AddString(s)); } /* ======================= FindSetterEventNum ======================= */ unsigned int Event::FindSetterEventNum(str s) { s.tolower(); return FindSetterEventNum(Director.AddString(s)); } /* ======================= FindGetterEventNum ======================= */ unsigned int Event::FindGetterEventNum(str s) { s.tolower(); return FindGetterEventNum(Director.AddString(s)); } /* ======================= GetEventWithFlags Returns an event that match the specified flags and the specified type ======================= */ int Event::GetEventWithFlags(str name, int flags, uchar type) { unsigned int *index; con_map *cmdList; if (type == EV_NORMAL) { cmdList = &normalCommandList; } else if (type == EV_RETURN) { cmdList = &returnCommandList; } else if (type == EV_GETTER) { cmdList = &getterCommandList; } else if (type == EV_SETTER) { cmdList = &setterCommandList; } else { return 0; } name.tolower(); index = cmdList->find(Director.GetString(name)); if (!index || !(GetEventFlags(*index) & flags)) { return 0; } else { return *index; } } /* ======================= GetEvent ======================= */ int Event::GetEvent(str name, uchar type) { return GetEventWithFlags(name, EV_DEFAULT, type); } #else int Event::GetEvent(str name, uchar type) { return FindEventNum(name); } #endif static str str_null; /* ======================= GetEventInfo ======================= */ command_t *Event::GetEventInfo(int eventnum) { return &commandList[eventnum]; } /* ======================= GetEventFlags Returns the specified event flags ======================= */ int Event::GetEventFlags(int eventnum) { command_t *cmd = &commandList[eventnum]; if (cmd) { return cmd->flags; } else { return 0; } return 0; } /* ======================= GetEventName Returns the specified event name ======================= */ const char *Event::GetEventName(int eventnum) { command_t *cmd; if (eventnum <= 0) { return str_null; } cmd = &commandList[eventnum]; if (cmd) { return cmd->command; } else { return str_null; } return str_null; } /* ======================= compareEvents ======================= */ int Event::compareEvents(const void *arg1, const void *arg2) { int num1 = *(int *)arg1; int num2 = *(int *)arg2; command_t *cmd1 = &commandList[num1]; command_t *cmd2 = &commandList[num2]; return Q_stricmp(cmd1->command, cmd2->command); } /* ======================= SortEventList Sort events in alphabetical order ======================= */ void Event::SortEventList(Container *sortedList) { unsigned int i; command_t *cmd; sortedList->Resize(commandList.size()); for (i = 1; i <= commandList.size(); i++) { cmd = &commandList[i]; if (cmd != NULL) { sortedList->AddObject(i); } } qsort((void *)sortedList->AddressOfObjectAt(1), (size_t)sortedList->NumObjects(), sizeof(int), compareEvents); } /* ======================= LoadEvents Event loader routine ======================= */ void Event::LoadEvents() { command_t c; DataNode *next; for (; DataNodeList; DataNodeList = next) { next = DataNodeList->next; EventDef *cmd = &eventDefList[DataNodeList->ev]; cmd->command = DataNodeList->command; cmd->flags = ((DataNodeList->flags == EV_DEFAULT) - 1) & DataNodeList->flags; cmd->formatspec = DataNodeList->formatspec; cmd->argument_names = DataNodeList->argument_names; cmd->documentation = DataNodeList->documentation; cmd->type = DataNodeList->type; c.command = DataNodeList->command; c.flags = DataNodeList->flags; c.type = cmd->type; DataNodeList->ev->eventnum = commandList.addKeyIndex(c); #ifdef _DEBUG DataNodeList->ev->name = DataNodeList->command; #endif delete DataNodeList; totalevents++; } } /* ======================= Event ======================= */ Event::Event() { fromScript = false; eventnum = 0; data = NULL; dataSize = 0; maxDataSize = 0; } /* ======================= Event Creates an event command ======================= */ Event::Event( const char *command, int flags, const char *formatspec, const char *argument_names, const char *documentation, uchar type ) { DataNode *node = new DataNode(); node->ev = this; node->command = command; node->flags = flags; node->formatspec = formatspec; node->argument_names = argument_names; node->documentation = documentation; node->type = type; node->next = DataNodeList; DataNodeList = node; fromScript = false; dataSize = 0; maxDataSize = 0; data = NULL; eventnum = 0; } /* ======================= Event ======================= */ Event::Event(const Event& ev) { fromScript = ev.fromScript; eventnum = ev.eventnum; dataSize = ev.dataSize; maxDataSize = ev.maxDataSize; if (dataSize) { data = new ScriptVariable[dataSize]; for (int i = 0; i < dataSize; i++) { data[i] = ev.data[i]; } } else { data = NULL; } #ifdef _DEBUG name = ev.name; #endif } Event::Event(const Event& ev, int numArgs) { fromScript = ev.fromScript; eventnum = ev.eventnum; dataSize = ev.dataSize; maxDataSize = ev.maxDataSize; if (dataSize) { data = new ScriptVariable[dataSize]; for (int i = 0; i < dataSize; i++) { data[i] = ev.data[i]; } } else { data = new ScriptVariable[numArgs]; dataSize = 0; maxDataSize = numArgs; } #ifdef _DEBUG name = ev.name; #endif } Event::Event(Event&& ev) { fromScript = ev.fromScript; eventnum = ev.eventnum; dataSize = ev.dataSize; maxDataSize = ev.maxDataSize; data = ev.data; ev.data = NULL; ev.dataSize = 0; ev.maxDataSize = 0; ev.eventnum = 0; } /* ======================= Event Initializes the event with the specified index ======================= */ Event::Event(int index) { fromScript = false; eventnum = index; data = NULL; dataSize = 0; maxDataSize = 0; #ifdef _DEBUG name = GetEventName(index); #endif } /* ======================= Event Initializes the event with the specified index ======================= */ Event::Event(int index, int numArgs) { fromScript = false; eventnum = index; data = new ScriptVariable[numArgs]; dataSize = 0; maxDataSize = numArgs; #ifdef _DEBUG name = GetEventName(index); #endif } /* ======================= Event Initializes the event with the specified command ======================= */ Event::Event(const char *command) { eventnum = FindEventNum(command); if (!eventnum) { EVENT_DPrintf("^~^~^ Event '%s' does not exist.\n", command); } fromScript = qfalse; maxDataSize = 0; dataSize = 0; data = NULL; #ifdef _DEBUG name = command; #endif } /* ======================= Event Initializes the event with the specified command ======================= */ Event::Event(const char *command, int numArgs) { eventnum = FindEventNum(command); if (!eventnum) { EVENT_DPrintf("^~^~^ Event '%s' does not exist.\n", command); } fromScript = qfalse; maxDataSize = numArgs; if (numArgs) { data = new ScriptVariable[numArgs]; dataSize = 0; } else { dataSize = 0; data = NULL; } #ifdef _DEBUG name = command; #endif } /* ======================= ~Event ======================= */ Event::~Event() { Clear(); } Event& Event::operator=(const Event& ev) { Clear(); fromScript = ev.fromScript; eventnum = ev.eventnum; dataSize = ev.dataSize; maxDataSize = ev.maxDataSize; if (dataSize) { data = new ScriptVariable[dataSize]; for (int i = 0; i < dataSize; i++) { data[i] = ev.data[i]; } } else { data = NULL; } #ifdef _DEBUG name = ev.name; #endif return *this; } Event& Event::operator=(Event&& ev) { Clear(); fromScript = ev.fromScript; eventnum = ev.eventnum; dataSize = ev.dataSize; maxDataSize = ev.maxDataSize; data = ev.data; ev.data = NULL; ev.dataSize = 0; ev.maxDataSize = 0; ev.eventnum = 0; return *this; } /* ======================= ErrorInternal ======================= */ void Event::ErrorInternal(Listener *l, str text) const { str classname; str eventname; EVENT_DPrintf("^~^~^ Game"); classname = l->getClassname(); eventname = getName(); EVENT_DPrintf(" (Event: '%s', Object: '%s') : %s\n", eventname.c_str(), classname.c_str(), text.c_str()); } /* ======================= AddContainer ======================= */ void Event::AddContainer(Container> *container) { ScriptVariable& variable = GetValue(); variable.setContainerValue(container); } /* ======================= AddEntity ======================= */ void Event::AddEntity(Entity *ent) { ScriptVariable& variable = GetValue(); variable.setListenerValue((Listener *)ent); } /* ======================= AddFloat ======================= */ void Event::AddFloat(float number) { ScriptVariable& variable = GetValue(); variable.setFloatValue(number); } /* ======================= AddInteger ======================= */ void Event::AddInteger(int number) { ScriptVariable& variable = GetValue(); variable.setIntValue(number); } /* ======================= AddListener ======================= */ void Event::AddListener(Listener *listener) { ScriptVariable& variable = GetValue(); variable.setListenerValue(listener); } /* ======================= AddNil ======================= */ void Event::AddNil(void) { ScriptVariable& variable = GetValue(); variable.Clear(); } #ifdef WITH_SCRIPT_ENGINE /* ======================= AddConstString ======================= */ void Event::AddConstString(const_str string) { ScriptVariable& variable = GetValue(); variable.setConstStringValue(string); } #endif /* ======================= AddString ======================= */ void Event::AddString(str string) { ScriptVariable& variable = GetValue(); variable.setStringValue(string); } /* ======================= AddToken ======================= */ void Event::AddToken(str token) { ScriptVariable& variable = GetValue(); variable.setStringValue(token); } /* ======================= AddTokens ======================= */ void Event::AddTokens(int argc, const char **argv) { for (int i = 0; i < argc; i++) { AddToken(argv[i]); } } /* ======================= AddValue ======================= */ void Event::AddValue(const ScriptVariable& value) { ScriptVariable& variable = GetValue(); variable = value; } /* ======================= AddVector ======================= */ void Event::AddVector(const Vector& vector) { ScriptVariable& variable = GetValue(); variable.setVectorValue(vector); } /* ======================= SetValue ======================= */ void Event::CopyValues(const ScriptVariable* values, size_t count) { assert(count <= maxDataSize); for (size_t i = 0; i < count; i++) { data[i] = values[i]; } dataSize = count; } /* ======================= Clear ======================= */ void Event::Clear(void) { if (data) { delete[] data; data = NULL; dataSize = 0; maxDataSize = 0; } } /* ======================= CheckPos ======================= */ void Event::CheckPos(int pos) { if (pos > NumArgs()) { ScriptError("Index %d out of range.", pos); } } /* ======================= GetBoolean ======================= */ bool Event::GetBoolean(int pos) { ScriptVariable& variable = GetValue(pos); return variable.booleanNumericValue(); } #ifdef WITH_SCRIPT_ENGINE /* ======================= GetConstString ======================= */ const_str Event::GetConstString(int pos) { ScriptVariable& variable = GetValue(pos); return variable.constStringValue(); } #endif /* ======================= GetEntity ======================= */ Entity *Event::GetEntity(int pos) { ScriptVariable& variable = GetValue(pos); return variable.entityValue(); } /* ======================= GetFloat ======================= */ float Event::GetFloat(int pos) { ScriptVariable& variable = GetValue(pos); return variable.floatValue(); } /* ======================= GetInteger ======================= */ int Event::GetInteger(int pos) { ScriptVariable& variable = GetValue(pos); return variable.intValue(); } /* ======================= GetListener ======================= */ Listener *Event::GetListener(int pos) { ScriptVariable& variable = GetValue(pos); return variable.listenerValue(); } #ifdef WITH_SCRIPT_ENGINE /* ======================= GetSimpleEntity ======================= */ SimpleEntity *Event::GetSimpleEntity(int pos) { ScriptVariable& variable = GetValue(pos); return variable.simpleEntityValue(); } #endif /* ======================= GetString ======================= */ str Event::GetString(int pos) { ScriptVariable& variable = GetValue(pos); return variable.stringValue(); } /* ======================= GetToken ======================= */ str Event::GetToken(int pos) { ScriptVariable& variable = GetValue(pos); return variable.stringValue(); } /* ======================= GetValue ======================= */ ScriptVariable& Event::GetValue(int pos) { if (pos < 0) { pos = NumArgs() + pos + 1; } CheckPos(pos); return data[pos - 1]; } static ScriptVariable m_null; /* ======================= GetValue ======================= */ ScriptVariable& Event::GetValue(void) { ScriptVariable* tmp; int i; if (fromScript) { // an event method will emit the return value // to the first index of the array // so there is no reallocation if (!data) { data = new ScriptVariable[1]; dataSize = 1; maxDataSize = 1; } return data[0]; } if (dataSize == maxDataSize) { tmp = data; maxDataSize += 3; data = new ScriptVariable[maxDataSize]; if (tmp != NULL) { for (i = 0; i < dataSize; i++) { data[i] = std::move(tmp[i]); } delete[] tmp; } } dataSize++; return data[dataSize - 1]; } /* ======================= GetVector ======================= */ Vector Event::GetVector(int pos) { ScriptVariable& variable = GetValue(pos); return variable.vectorValue(); } #if defined(GAME_DLL) /* ======================= GetPathNode ======================= */ PathNode *Event::GetPathNode(int pos) { ScriptVariable& variable = GetValue(pos); return variable.pathNodeValue(); } /* ======================= GetWaypoint ======================= */ Waypoint *Event::GetWaypoint(int pos) { ScriptVariable& variable = GetValue(pos); return variable.waypointValue(); } #endif /* ======================= IsEntityAt ======================= */ qboolean Event::IsEntityAt(int pos) { CheckPos(pos); return data[pos - 1].IsEntity(); } /* ======================= IsListenerAt ======================= */ qboolean Event::IsListenerAt(int pos) { CheckPos(pos); return data[pos - 1].IsListener(); } /* ======================= IsNilAt ======================= */ qboolean Event::IsNilAt(int pos) { CheckPos(pos); return data[pos - 1].GetType() == VARIABLE_NONE; } /* ======================= IsNumericAt ======================= */ qboolean Event::IsNumericAt(int pos) { CheckPos(pos); return data[pos - 1].IsNumeric(); } #ifdef WITH_SCRIPT_ENGINE /* ======================= IsSimpleEntityAt ======================= */ qboolean Event::IsSimpleEntityAt(int pos) { CheckPos(pos); return data[pos - 1].IsSimpleEntity(); } #endif /* ======================= IsStringAt ======================= */ qboolean Event::IsStringAt(int pos) { CheckPos(pos); return data[pos - 1].IsString(); } /* ======================= IsVectorAt ======================= */ qboolean Event::IsVectorAt(int pos) { CheckPos(pos); return data[pos - 1].IsVector(); } /* ======================= IsFromScript ======================= */ qboolean Event::IsFromScript() { return fromScript; } /* ======================= NumArgs ======================= */ int Event::NumArgs() { return dataSize; } /* ======================= getInfo ======================= */ EventDef *Event::getInfo() { return &eventDefList[this]; } /* ======================= getName ======================= */ const char *Event::getName() const { return GetEventName(eventnum); } //==================================== // Listener //==================================== qboolean ListenerDelete = false; /* ======================= Listener ======================= */ Listener::Listener() { #ifdef WITH_SCRIPT_ENGINE m_EndList = NULL; m_NotifyList = NULL; m_WaitForList = NULL; vars = NULL; #endif } /* ======================= ~Listener ======================= */ Listener::~Listener() { if (EventSystemStarted) { CancelPendingEvents(); } #ifdef WITH_SCRIPT_ENGINE UnregisterAll(); CancelWaitingAll(); if (vars) { delete vars; } #endif } #define L_ARCHIVE_NOTIFYLIST 1 #define L_ARCHIVE_WAITFORLIST 2 #define L_ARCHIVE_VARLIST 4 #define L_ARCHIVE_ENDLIST 8 /* ======================= Archive ======================= */ void Listener::Archive(Archiver& arc) { Class::Archive(arc); #ifdef WITH_SCRIPT_ENGINE byte flag = 0; if (!arc.Loading()) { if (m_NotifyList) { flag |= L_ARCHIVE_NOTIFYLIST; } if (m_WaitForList) { flag |= L_ARCHIVE_WAITFORLIST; } if (vars) { flag |= L_ARCHIVE_VARLIST; } if (m_EndList) { flag |= L_ARCHIVE_ENDLIST; } } arc.ArchiveByte(&flag); // archive the notify list if (flag & L_ARCHIVE_NOTIFYLIST) { if (arc.Loading()) { m_NotifyList = new con_set; } m_NotifyList->Archive(arc); } // archive the waiting thread list if (flag & L_ARCHIVE_WAITFORLIST) { if (arc.Loading()) { m_WaitForList = new con_set; } m_WaitForList->Archive(arc); } // archive the variable list if (flag & L_ARCHIVE_VARLIST) { if (arc.Loading()) { vars = new ScriptVariableList; } vars->Archive(arc); } // archive the end on event list if (flag & L_ARCHIVE_ENDLIST) { if (arc.Loading()) { m_EndList = new con_set; } m_EndList->Archive(arc); } #endif } /* ======================= CancelEventsOfType ======================= */ void Listener::CancelEventsOfType(Event *ev) { EventQueueNode *node; EventQueueNode *next; int eventnum; node = Event::EventQueue.next; eventnum = ev->eventnum; while (node != &Event::EventQueue) { next = node->next; if ((node->GetSourceObject() == this) && (node->event->eventnum == eventnum)) { LL_Remove(node, next, prev); delete node; } node = next; } } /* ======================= CancelEventsOfType ======================= */ void Listener::CancelEventsOfType(Event& ev) { this->CancelEventsOfType(&ev); } /* ======================= CancelFlaggedEvents ======================= */ void Listener::CancelFlaggedEvents(int flags) { EventQueueNode *node; EventQueueNode *next; node = Event::EventQueue.next; while (node != &Event::EventQueue) { next = node->next; if ((node->GetSourceObject() == this) && (node->flags & flags)) { LL_Remove(node, next, prev); delete node; } node = next; } } /* ======================= CancelPendingEvents ======================= */ void Listener::CancelPendingEvents(void) { EventQueueNode *node; EventQueueNode *next; node = Event::EventQueue.next; while (node != &Event::EventQueue) { next = node->next; if (node->GetSourceObject() == this) { LL_Remove(node, next, prev); delete node; } node = next; } } /* ======================= EventPending ======================= */ qboolean Listener::EventPending(Event& ev) { EventQueueNode *event; int eventnum; event = Event::EventQueue.next; eventnum = ev.eventnum; while (event != &Event::EventQueue) { if ((event->GetSourceObject() == this) && (event->event->eventnum == ev.eventnum)) { return true; } event = event->next; } return false; } /* ======================= PostEventInternal ======================= */ EventQueueNode *Listener::PostEventInternal(Event *ev, float delay, int flags) { EventQueueNode *node; EventQueueNode *i; int inttime; if (!classinfo()->responseLookup[ev->eventnum]) { if (!ev->eventnum) { #ifdef _DEBUG EVENT_DPrintf("^~^~^ Failed execution of event '%s' for class '%s'\n", ev->name, getClassname()); #else EVENT_DPrintf("^~^~^ Failed execution of event for class '%s'\n", getClassname()); #endif } delete ev; return NULL; } node = new EventQueueNode; i = Event::EventQueue.next; inttime = EVENT_msec + (delay * 1000.0f + 0.5f); while (i != &Event::EventQueue && inttime > i->inttime) { i = i->next; } node->inttime = inttime; node->event = ev; node->flags = flags; node->SetSourceObject(this); #ifdef _DEBUG node->name = ev->name; #endif LL_Add(i, node, next, prev); return node; } /* ======================= PostEvent ======================= */ void Listener::PostEvent(Event *ev, float delay, int flags) { PostEventInternal(ev, delay, flags); } /* ======================= PostEvent ======================= */ void Listener::PostEvent(const Event& ev, float delay, int flags) { Event *e = new Event(ev); PostEventInternal(e, delay, flags); } /* ======================= PostponeAllEvents ======================= */ qboolean Listener::PostponeAllEvents(float time) { EventQueueNode *event; EventQueueNode *node; event = Event::EventQueue.next; while (event != &Event::EventQueue) { if (event->GetSourceObject() == this) { event->inttime += time * 1000.0f + 0.5f; node = event->next; while ((node != &Event::EventQueue) && (event->inttime >= node->inttime)) { node = node->next; } LL_Remove(event, next, prev); LL_Add(node, event, next, prev); return true; } event = event->next; } return false; } /* ======================= PostponeEvent ======================= */ qboolean Listener::PostponeEvent(Event& ev, float time) { EventQueueNode *event; EventQueueNode *node; int eventnum; eventnum = ev.eventnum; event = Event::EventQueue.next; while (event != &Event::EventQueue) { if ((event->GetSourceObject() == this) && (event->event->eventnum == eventnum)) { event->inttime += time * 1000.0f + 0.5f; node = event->next; while ((node != &Event::EventQueue) && (event->inttime >= node->inttime)) { node = node->next; } LL_Remove(event, next, prev); LL_Add(node, event, next, prev); return true; } event = event->next; } return false; } /* ======================= ProcessEvent ======================= */ bool Listener::ProcessEvent(Event *ev) { try { return ProcessScriptEvent(ev); } catch (ScriptException& exc) { ev->ErrorInternal(this, exc.string); EVENT_DPrintf("%s\n", exc.string.c_str()); // at this point the event didn't get deleted delete ev; return false; } } /* ======================= ProcessEvent ======================= */ bool Listener::ProcessEvent(Event& ev) { try { return ProcessScriptEvent(ev); } catch (ScriptException& exc) { ev.ErrorInternal(this, exc.string); EVENT_DPrintf("%s\n", exc.string.c_str()); return false; } } /* ======================= ProcessEvent ======================= */ bool Listener::ProcessEvent(const Event& ev) { try { Event event(ev); return ProcessScriptEvent(event); } catch (ScriptException& exc) { ev.ErrorInternal(this, exc.string); EVENT_DPrintf("%s\n", exc.string.c_str()); return false; } } /* ======================= ProcessEventReturn ======================= */ ScriptVariable& Listener::ProcessEventReturn(Event *ev) { ClassDef *c = classinfo(); ResponseDef *responses = NULL; Response response = NULL; static ScriptVariable m_Return; if (!ev->eventnum) { #ifdef _DEBUG EVENT_Printf("^~^~^ Failed execution of event '%s' for class '%s'\n", ev->name, c->classname); #else EVENT_Printf("^~^~^ Failed execution of event for class '%s'\n", c->classname); #endif delete ev; return m_Return; } responses = c->responseLookup[ev->eventnum]; if (responses == NULL) { EVENT_Printf( "^~^~^ Failed execution of command '%s' for class '%s'\n", Event::GetEventName(ev->eventnum), c->classname ); delete ev; return m_Return; } response = responses->response; int previousArgs = ev->NumArgs(); if (response) { (this->*response)(ev); } if (previousArgs != ev->NumArgs() && ev->NumArgs() != 0) { m_Return = ev->GetValue(ev->NumArgs()); } delete ev; return m_Return; } /* ======================= ProcessScriptEvent ======================= */ bool Listener::ProcessScriptEvent(Event *ev) { bool result = ProcessScriptEvent(*ev); delete ev; return result; } /* ======================= ProcessScriptEvent ======================= */ bool Listener::ProcessScriptEvent(Event& ev) { ClassDef *c = classinfo(); ResponseDef *responses = NULL; Response response = NULL; if (!ev.eventnum) { #ifdef _DEBUG EVENT_Printf("^~^~^ Failed execution of event '%s' for class '%s'\n", ev.name, c->classname); #else EVENT_Printf("^~^~^ Failed execution of event for class '%s'\n", c->classname); #endif return false; } responses = c->responseLookup[ev.eventnum]; if (responses == NULL) { return true; } response = responses->response; if (response) { (this->*response)(&ev); } return true; } void Listener::ProcessContainerEvent(const Container& conev) { int num = conev.NumObjects(); for (int i = 1; i <= num; i++) { ProcessEvent(conev.ObjectAt(i)); } } /* ======================= ProcessPendingEvents ======================= */ qboolean Listener::ProcessPendingEvents(void) { EventQueueNode *event; qboolean processedEvents; float t; processedEvents = false; t = EVENT_msec; event = Event::EventQueue.next; while (event != &Event::EventQueue) { Listener *obj; assert(event); obj = event->GetSourceObject(); if (event->inttime > t) { break; } if (obj != this) { // traverse normally event = event->next; } else { // the event is removed from its list and temporarily added to the active list LL_Remove(event, next, prev); // ProcessEvent will dispose of this event when it is done obj->ProcessEvent(event->event); // free up the node delete event; // start over, since can't guarantee that we didn't process any previous or following events event = Event::EventQueue.next; processedEvents = true; } } return processedEvents; } /* ======================= GetScriptOwner listener.owner ======================= */ Listener *Listener::GetScriptOwner(void) { return NULL; } #ifdef WITH_SCRIPT_ENGINE /* ======================= CreateVars ======================= */ void Listener::CreateVars(void) { vars = new ScriptVariableList; } /* ======================= ClearVars ======================= */ void Listener::ClearVars(void) { if (vars) { delete vars; CreateVars(); } } /* ======================= Vars ======================= */ ScriptVariableList *Listener::Vars(void) { if (!vars) { CreateVars(); } return vars; } /* ======================= BroadcastEvent ======================= */ bool Listener::BroadcastEvent(str name, Event& event) { return BroadcastEvent(Director.AddString(name), event); } /* ======================= BroadcastEvent ======================= */ bool Listener::BroadcastEvent(const_str name, Event& event) { ConList* listeners; if (!m_NotifyList) { return false; } listeners = m_NotifyList->findKeyValue(name); if (!listeners) { return false; } return BroadcastEvent(event, listeners); } /* ======================= BroadcastEvent Broadcast an event to the notify list ======================= */ bool Listener::BroadcastEvent(Event& event, ConList *listeners) { Listener* listener; int num = listeners->NumObjects(); int i; bool found; if (!num) { return false; } if (num == 1) { Listener *listener = listeners->ObjectAt(1); if (listener) { listener->ProcessEvent(event); return true; } return false; } ConList listenersCopy; found = false; listenersCopy.Resize(num); for (i = num; i > 0; i--) { listener = listeners->ObjectAt(i); if (listener) { listenersCopy.AddObject(listener); } } for (i = listenersCopy.NumObjects(); i > 0; i--) { listener = listenersCopy.ObjectAt(i); if (listener) { listener->ProcessEvent(event); found = true; } } return found; } /* ======================= CancelWaiting ======================= */ void Listener::CancelWaiting(str name) { CancelWaiting(Director.AddString(name)); } /* ======================= CancelWaiting ======================= */ void Listener::CancelWaiting(const_str name) { ConList* list; if (!m_WaitForList) { return; } list = m_WaitForList->findKeyValue(name); if (!list) { return; } ConList stoppedListeners; CancelWaitingSources(name, *list, stoppedListeners); m_WaitForList->remove(name); if (m_WaitForList->isEmpty()) { delete m_WaitForList; m_WaitForList = NULL; if (!DisableListenerNotify) { StoppedWaitFor(name, false); } } for (int i = stoppedListeners.NumObjects(); i > 0; i--) { Listener *listener = stoppedListeners.ObjectAt(i); if (listener && !DisableListenerNotify) { listener->StoppedNotify(); } } } /* ======================= CancelWaitingAll ======================= */ void Listener::CancelWaitingAll() { CancelWaiting(0); if (!m_WaitForList) { return; } con_set_enum en = *m_WaitForList; con_set::Entry *e; ConList stoppedListeners; for (e = en.NextElement(); e != NULL; e = en.NextElement()) { CancelWaitingSources(e->key, e->value, stoppedListeners); } delete m_WaitForList; m_WaitForList = NULL; if (!DisableListenerNotify) { StoppedWaitFor(STRING_NULL, false); } for (int i = stoppedListeners.NumObjects(); i > 0; i--) { Listener *listener = stoppedListeners.ObjectAt(i); if (listener && !DisableListenerNotify) { listener->StoppedNotify(); } } } /* ======================= CancelWaitingSources ======================= */ void Listener::CancelWaitingSources(const_str name, ConList& listeners, ConList& stoppedListeners) { for (int i = listeners.NumObjects(); i > 0; i--) { Listener *listener = listeners.ObjectAt(i); if (listener && listener->UnregisterSource(name, this)) { stoppedListeners.AddObject(listener); } } } /* ======================= Notify ======================= */ void Listener::Notify(const char *name) { Unregister(name); } /* ======================= StoppedNotify Called when stopped notifying for a listener ======================= */ void Listener::StoppedNotify(void) {} /* ======================= StartedWaitFor Called when started waiting for a listener ======================= */ void Listener::StartedWaitFor(void) {} /* ======================= StoppedWaitFor Called when stopped wait for a listener ======================= */ void Listener::StoppedWaitFor(const_str name, bool bDeleting) {} /* ======================= EndOn ======================= */ void Listener::EndOn(str name, Listener *listener) { EndOn(Director.AddString(name), listener); } /* ======================= EndOn Removes the specified listener when notifying ======================= */ void Listener::EndOn(const_str name, Listener *listener) { if (!m_EndList) { m_EndList = new con_set; } ConList& list = m_EndList->addKeyValue(name); list.AddUniqueObject(listener); } /* ======================= Register ======================= */ void Listener::Register(str name, Listener *listener) { Register(Director.AddString(name), listener); } /* ======================= Register Registers a listener, that will be notified with Unregister Listener is usually a ScriptThread ======================= */ void Listener::Register(const_str name, Listener *listener) { RegisterSource(name, listener); listener->RegisterTarget(name, this); } /* ======================= RegisterSource ======================= */ void Listener::RegisterSource(const_str name, Listener *listener) { if (!m_NotifyList) { m_NotifyList = new con_set; } ConList& list = m_NotifyList->addKeyValue(name); list.AddObject(listener); } /* ======================= RegisterTarget ======================= */ void Listener::RegisterTarget(const_str name, Listener *listener) { if (!m_WaitForList) { StartedWaitFor(); m_WaitForList = new con_set; } ConList& list = m_WaitForList->addKeyValue(name); list.AddObject(listener); } /* ======================= Unregister ======================= */ void Listener::Unregister(str name) { Unregister(Director.AddString(name)); } /* ======================= Unregister Unregister listeners from the specified event ======================= */ void Listener::Unregister(const_str name) { if (m_EndList) { ConList *list = m_EndList->findKeyValue(name); bool bDeleteSelf = false; if (list) { ConList listeners = *list; m_EndList->remove(name); if (m_EndList->isEmpty()) { delete m_EndList; m_EndList = NULL; } for (int i = listeners.NumObjects(); i > 0; i--) { Listener *listener = listeners.ObjectAt(i); if (listener) { if (listener == this && (bDeleteSelf)) { continue; } if (listener == this) { bDeleteSelf = true; } delete listener; } } } if (bDeleteSelf) { return; } } if (!m_NotifyList) { return; } ConList *list = m_NotifyList->findKeyValue(name); ConList stoppedListeners; Container stoppedNames; if (!list) { return; } UnregisterTargets(name, *list, stoppedListeners, stoppedNames); m_NotifyList->remove(name); if (m_NotifyList->isEmpty()) { delete m_NotifyList; m_NotifyList = NULL; if (!DisableListenerNotify) { StoppedNotify(); } } for (int i = stoppedListeners.NumObjects(); i > 0; i--) { Listener* listener = stoppedListeners.ObjectAt(i); if (listener && !DisableListenerNotify) { listener->StoppedWaitFor(name, false); } } } /* ======================= Unregister ======================= */ void Listener::Unregister(str name, Listener *listener) { Unregister(Director.AddString(name)); } /* ======================= Unregister Unregister a specified listener with the specified label ======================= */ void Listener::Unregister(const_str name, Listener *listener) { if (UnregisterSource(name, listener) && !DisableListenerNotify) { StoppedNotify(); } if (listener->UnregisterTarget(name, this) && !DisableListenerNotify) { listener->StoppedWaitFor(name, false); } } /* ======================= UnregisterAll ======================= */ void Listener::UnregisterAll(void) { Unregister(0); if (m_EndList) { delete m_EndList; m_EndList = NULL; } if (!m_NotifyList) { return; } con_set_enum en = *m_NotifyList; con_set::Entry *e; ConList stoppedListeners; Container stoppedNames; en = *m_NotifyList; for (e = en.NextElement(); e != NULL; e = en.NextElement()) { UnregisterTargets(e->key, e->value, stoppedListeners, stoppedNames); } delete m_NotifyList; m_NotifyList = NULL; if (!DisableListenerNotify) { StoppedNotify(); } for (int i = stoppedListeners.NumObjects(); i > 0; i--) { Listener *listener = stoppedListeners.ObjectAt(i); if (listener && !DisableListenerNotify) { listener->StoppedWaitFor(stoppedNames.ObjectAt(i), true); } } } /* ======================= UnregisterSource ======================= */ bool Listener::UnregisterSource(const_str name, Listener *listener) { ConList* list; if (!m_NotifyList) { return false; } list = m_NotifyList->findKeyValue(name); if (!list) { return false; } list->RemoveObject(listener); if (list->NumObjects()) { return false; } m_NotifyList->remove(name); if (!m_NotifyList->isEmpty()) { // still has objects in it return false; } delete m_NotifyList; m_NotifyList = NULL; return true; } /* ======================= UnregisterTarget ======================= */ bool Listener::UnregisterTarget(const_str name, Listener *listener) { ConList* list; if (!m_WaitForList) { return false; } list = m_WaitForList->findKeyValue(name); if (!list) { return false; } list->RemoveObject(listener); if (list->NumObjects()) { return false; } m_WaitForList->remove(name); if (!m_WaitForList->isEmpty()) { // still has objects in it return false; } delete m_WaitForList; m_WaitForList = NULL; return true; } /* ======================= UnregisterTargets ======================= */ void Listener::UnregisterTargets( const_str name, ConList& listeners, ConList& stoppedListeners, Container& stoppedNames ) { for (int i = listeners.NumObjects(); i > 0; i--) { Listener *listener = listeners.ObjectAt(i); if (listener && listener->UnregisterTarget(name, this)) { stoppedListeners.AddObject(listener); stoppedNames.AddObject(name); } } } /* ======================= AbortRegistration Abort the listener from registration of the specified label Doesn't notify ======================= */ void Listener::AbortRegistration(const_str name, Listener *l) { UnregisterSource(name, l); l->UnregisterTarget(name, this); } /* ======================= RegisterSize ======================= */ int Listener::RegisterSize(str name) const { return RegisterSize(Director.AddString(name)); } /* ======================= RegisterSize Returns how many listeners in the notify list ======================= */ int Listener::RegisterSize(const_str name) const { ConList *listeners; if (!m_NotifyList) { return 0; } listeners = m_NotifyList->findKeyValue(name); // return the number of listeners waiting for this listener if (listeners) { return listeners->NumObjects(); } else { return 0; } } /* ======================= WaitingSize ======================= */ int Listener::WaitingSize(str name) const { return WaitingSize(Director.AddString(name)); } /* ======================= WaitingSize Returns how many listeners in the wait list ======================= */ int Listener::WaitingSize(const_str name) const { ConList *listeners; if (!m_WaitForList) { return 0; } listeners = m_WaitForList->findKeyValue(name); // return the number of listeners this listener is waiting for if (listeners) { return listeners->NumObjects(); } else { return 0; } } /* ======================= WaitTillDisabled ======================= */ bool Listener::WaitTillDisabled(str s) { return WaitTillDisabled(Director.AddString(s)); } /* ======================= WaitTillDisabled Returns true if the specified waittill is disabled ======================= */ bool Listener::WaitTillDisabled(const_str s) { return !WaitTillAllowed(s); } #endif /* ======================= GetFlags ======================= */ int Listener::GetFlags(Event *event) const { return classinfo()->GetFlags(event); } /* ======================= ValidEvent ======================= */ qboolean Listener::ValidEvent(str name) const { int num; EventDef *def; num = Event::FindEventNum(name); if (!num) { return qfalse; } def = classinfo()->GetDef(num); if (!def) { return qfalse; } return qtrue; } //========================== // Listener's events //========================== /* ======================= Remove ======================= */ void Listener::Remove(Event *ev) { if (ev->NumArgs()) { ScriptError("Arguments not allowed."); } delete this; } /* ======================= ScriptRemove ======================= */ void Listener::ScriptRemove(Event *ev) { PostEvent(EV_Remove, 0); } /* ======================= EventInheritsFrom ======================= */ void Listener::EventInheritsFrom(Event *ev) { ev->AddInteger(inheritsFrom(ev->GetString(1))); } /* ======================= EventIsInheritedBy ======================= */ void Listener::EventIsInheritedBy(Event *ev) { ev->AddInteger(isInheritedBy(ev->GetString(1))); } /* ======================= GetClassname ======================= */ void Listener::GetClassname(Event *ev) { ev->AddString(getClassname()); } /* ======================= CommandDelay ======================= */ void Listener::CommandDelay(Event *ev) { if (ev->NumArgs() < 2) { ScriptError("Not enough arguments."); } Event *e = new Event(ev->GetString(2)); for (int i = 3; i <= ev->NumArgs(); i++) { e->AddValue(ev->GetValue(i)); } PostEvent(e, ev->GetFloat(1)); } #ifdef WITH_SCRIPT_ENGINE /* ======================= CancelFor Removes all listeners in the notify list ======================= */ void Listener::CancelFor(Event *ev) { BroadcastEvent(ev->GetConstString(1), EV_Remove); } /* ======================= EventDelayThrow Same as EventThrow ======================= */ void Listener::EventDelayThrow(Event *ev) { BroadcastEvent(0, *ev); } /* ======================= EventEndOn ======================= */ void Listener::EventEndOn(Event *ev) { const_str name = ev->GetConstString(1); if (Director.CurrentThread() == this) { ScriptError("cannot end for the current thread!"); } EndOn(name, Director.CurrentThread()); } /* ======================= EventGetOwner ======================= */ void Listener::EventGetOwner(Event *ev) { ev->AddListener(GetScriptOwner()); } /* ======================= EventNotify ======================= */ void Listener::EventNotify(Event *ev) { str name = ev->GetString(1); Notify(name); } /* ======================= EventThrow ======================= */ void Listener::EventThrow(Event *ev) { BroadcastEvent(0, *ev); } /* ======================= EventUnregister ======================= */ void Listener::EventUnregister(Event *ev) { Unregister(ev->GetConstString(1)); } /* ======================= WaitTill Wait until event of type name ======================= */ void Listener::WaitTill(Event *ev) { const_str name; if (Director.CurrentThread() == this) { ScriptError("cannot waittill on the current thread!"); } name = ev->GetConstString(1); if (!WaitTillAllowed(name)) { ScriptError("invalid waittill %s for '%s'", Director.GetString(name).c_str(), getClassname()); } Register(name, Director.CurrentThread()); } /* ======================= WaitTillTimeout Wait until event of type name with a timeout time ======================= */ void Listener::WaitTillTimeout(Event *ev) { const_str name; float timeout_time; if (Director.CurrentThread() == this) { ScriptError("cannot waittill on the current thread!"); } timeout_time = ev->GetFloat(1); name = ev->GetConstString(2); if (!WaitTillAllowed(name)) { ScriptError("invalid waittill %s for '%s'", Director.GetString(name).c_str(), getClassname()); } Register(name, Director.CurrentThread()); Director.CurrentThread()->PostEvent(EV_ScriptThread_CancelWaiting, timeout_time); } /* ======================= WaitTillAny Wait until any event of type name ======================= */ void Listener::WaitTillAny(Event *ev) { const_str name; if (Director.CurrentThread() == this) { ScriptError("cannot waittill any on the current thread!"); } for (int i = 1; i <= ev->NumArgs(); i++) { name = ev->GetConstString(i); if (!WaitTillAllowed(name)) { ScriptError("invalid waittill %s for '%s'", Director.GetString(name).c_str(), getClassname()); } Register(name, Director.CurrentThread()); } } /* ======================= WaitTillAnyTimeout Wait until any event of type name with a timeout time ======================= */ void Listener::WaitTillAnyTimeout(Event *ev) { const_str name; float timeout_time; if (Director.CurrentThread() == this) { ScriptError("cannot waittill any on the current thread!"); } timeout_time = ev->GetFloat(1); for (int i = 1; i <= ev->NumArgs(); i++) { name = ev->GetConstString(i); if (!WaitTillAllowed(name)) { ScriptError("invalid waittill %s for '%s'", Director.GetString(name).c_str(), getClassname()); } Register(name, Director.CurrentThread()); } Director.CurrentThread()->PostEvent(EV_ScriptThread_CancelWaiting, timeout_time); } /* ======================= ExecuteScriptInternal ======================= */ void Listener::ExecuteScriptInternal(Event *ev, ScriptVariable& returnValue) { ScriptThread* thread = CreateScriptInternal(ev->GetValue(1)); thread->ScriptExecute(&ev->data[1], ev->dataSize - 1, returnValue); } /* ======================= ExecuteThreadInternal ======================= */ void Listener::ExecuteThreadInternal(Event *ev, ScriptVariable& returnValue) { ScriptThread* thread = CreateThreadInternal(ev->GetValue(1)); thread->ScriptExecute(&ev->data[1], ev->dataSize - 1, returnValue); } /* ======================= WaitExecuteScriptInternal ======================= */ void Listener::WaitExecuteScriptInternal(Event *ev, ScriptVariable& returnValue) { ScriptThread *currentThread = Director.CurrentScriptThread(); ScriptThread *thread = CreateScriptInternal(ev->GetValue(1)); thread->GetScriptClass()->Register(0, currentThread); thread->ScriptExecute(&ev->data[1], ev->dataSize - 1, returnValue); } /* ======================= WaitExecuteThreadInternal ======================= */ void Listener::WaitExecuteThreadInternal(Event *ev, ScriptVariable& returnValue) { ScriptThread *currentThread = Director.CurrentScriptThread(); ScriptThread *thread = CreateThreadInternal(ev->GetValue(1)); thread->Register(0, currentThread); thread->ScriptExecute(&ev->data[1], ev->dataSize - 1, returnValue); } /* ======================= CreateScriptInternal ======================= */ ScriptThread *Listener::CreateScriptInternal(const ScriptVariable& label) { GameScript *scr; ScriptThread *thread = NULL; if (label.GetType() == VARIABLE_STRING || label.GetType() == VARIABLE_CONSTSTRING) { if (label.GetType() == VARIABLE_CONSTSTRING) { scr = Director.GetGameScript(label.constStringValue()); } else { scr = Director.GetGameScript(label.stringValue()); } thread = Director.CreateScriptThread(scr, this, ""); } else if (label.GetType() == VARIABLE_CONSTARRAY && label.arraysize() > 1) { ScriptVariable *script = label[1]; ScriptVariable *labelname = label[2]; if (script->GetType() == VARIABLE_CONSTSTRING) { scr = Director.GetGameScript(script->constStringValue()); } else { scr = Director.GetGameScript(script->stringValue()); } if (labelname->GetType() == VARIABLE_CONSTSTRING) { thread = Director.CreateScriptThread(scr, this, labelname->constStringValue()); } else { thread = Director.CreateScriptThread(scr, this, labelname->stringValue()); } } else { ScriptError("Listener::CreateScriptInternal: bad label type '%s'", label.GetTypeName()); } return thread; } /* ======================= CreateThreadInternal ======================= */ ScriptThread *Listener::CreateThreadInternal(const ScriptVariable& label) { GameScript *scr; ScriptThread *thread = NULL; ScriptClass* scriptClass; if (label.GetType() == VARIABLE_STRING || label.GetType() == VARIABLE_CONSTSTRING) { scriptClass = Director.CurrentScriptClass(); scr = scriptClass->GetScript(); if (label.GetType() == VARIABLE_CONSTSTRING) { thread = Director.CreateScriptThread(scr, this, label.constStringValue()); } else { thread = Director.CreateScriptThread(scr, this, label.stringValue()); } } else if (label.GetType() == VARIABLE_CONSTARRAY && label.arraysize() > 1) { ScriptVariable *script = label[1]; ScriptVariable *labelname = label[2]; if (script->GetType() == VARIABLE_CONSTSTRING) { scr = Director.GetGameScript(script->constStringValue()); } else { scr = Director.GetGameScript(script->stringValue()); } if (labelname->GetType() == VARIABLE_CONSTSTRING) { thread = Director.CreateScriptThread(scr, this, labelname->constStringValue()); } else { thread = Director.CreateScriptThread(scr, this, labelname->stringValue()); } } else { ScriptError("Listener::CreateThreadInternal: bad argument format"); } return thread; } /* ======================= CreateReturnThread ======================= */ void Listener::CreateReturnThread(Event *ev) { ScriptVariable returnValue; returnValue.newPointer(); ExecuteThreadInternal(ev, returnValue); ev->AddValue(returnValue); } /* ======================= CreateThread ======================= */ void Listener::CreateThread(Event *ev) { ScriptVariable returnValue; ExecuteThreadInternal(ev, returnValue); } /* ======================= ExecuteReturnScript ======================= */ void Listener::ExecuteReturnScript(Event *ev) { ScriptVariable returnValue; returnValue.newPointer(); ExecuteScriptInternal(ev, returnValue); ev->AddValue(returnValue); } /* ======================= ExecuteScript ======================= */ void Listener::ExecuteScript(Event *ev) { ScriptVariable returnValue; ExecuteScriptInternal(ev, returnValue); } /* ======================= WaitCreateReturnThread ======================= */ void Listener::WaitCreateReturnThread(Event *ev) { ScriptVariable returnValue; returnValue.newPointer(); WaitExecuteThreadInternal(ev, returnValue); ev->AddValue(returnValue); } /* ======================= WaitCreateThread ======================= */ void Listener::WaitCreateThread(Event *ev) { ScriptVariable returnValue; WaitExecuteThreadInternal(ev, returnValue); } /* ======================= WaitExecuteReturnScript ======================= */ void Listener::WaitExecuteReturnScript(Event *ev) { ScriptVariable returnValue; returnValue.newPointer(); WaitExecuteScriptInternal(ev, returnValue); ev->AddValue(returnValue); } /* ======================= WaitExecuteScript ======================= */ void Listener::WaitExecuteScript(Event *ev) { ScriptVariable returnValue; WaitExecuteScriptInternal(ev, returnValue); } /* ======================= ExecuteThread Execute a thread with optionally parameters ======================= */ void Listener::ExecuteThread(str scriptName, str labelName, Event *params) { ScriptThread *thread = Director.CreateThread(scriptName, labelName, this); try { if (!thread) { return; } thread->Execute(params); } catch (ScriptException& exc) { EVENT_DPrintf("Listener::ExecuteThread: %s\n", exc.string.c_str()); } } /* ======================= ExecuteThread Execute a thread with optionally parameters ======================= */ void Listener::ExecuteThread(str scriptName, str labelName, Event& params) { ScriptThread *thread = Director.CreateThread(scriptName, labelName, this); try { if (!thread) { return; } thread->Execute(params); } catch (ScriptException& exc) { EVENT_DPrintf("Listener::ExecuteThread: %s\n", exc.string.c_str()); } } #endif command_t::command_t() {} command_t::command_t(const char *name, byte t) : command(name) , type(t) {}