diff options
author | Paul Gilbert | 2012-05-08 09:32:21 +1000 |
---|---|---|
committer | Paul Gilbert | 2012-05-08 09:32:21 +1000 |
commit | a254f100253bda1b23f280226b7b4d909206d49c (patch) | |
tree | 224708431a2c2a61451f98909fda747a154c7e8a /engines/tony | |
parent | 8527302057052e784c3ea32ca8eebb0220bf15e6 (diff) | |
download | scummvm-rg350-a254f100253bda1b23f280226b7b4d909206d49c.tar.gz scummvm-rg350-a254f100253bda1b23f280226b7b4d909206d49c.tar.bz2 scummvm-rg350-a254f100253bda1b23f280226b7b4d909206d49c.zip |
TONY: Added support for Windows-style threading events to scheduler
Diffstat (limited to 'engines/tony')
-rw-r--r-- | engines/tony/custom.cpp | 9 | ||||
-rw-r--r-- | engines/tony/loc.cpp | 30 | ||||
-rw-r--r-- | engines/tony/loc.h | 4 | ||||
-rw-r--r-- | engines/tony/mpal/mpal.cpp | 50 | ||||
-rw-r--r-- | engines/tony/sched.cpp | 71 | ||||
-rw-r--r-- | engines/tony/sched.h | 4 | ||||
-rw-r--r-- | engines/tony/tony.cpp | 2 | ||||
-rw-r--r-- | engines/tony/tonychar.cpp | 14 |
8 files changed, 137 insertions, 47 deletions
diff --git a/engines/tony/custom.cpp b/engines/tony/custom.cpp index 800b5d8d50..18a92899df 100644 --- a/engines/tony/custom.cpp +++ b/engines/tony/custom.cpp @@ -1310,10 +1310,17 @@ DECLARE_CUSTOM_FUNCTION(SetTonyPosition)(CORO_PARAM, uint32 nX, uint32 nY, uint3 } DECLARE_CUSTOM_FUNCTION(MoveTonyAndWait)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + Tony->Move(RMPoint(nX, nY)); if (!bSkipIdle) - Tony->WaitForEndMovement(); + CORO_INVOKE_0(Tony->WaitForEndMovement); + + CORO_END_CODE; } DECLARE_CUSTOM_FUNCTION(MoveTony)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { diff --git a/engines/tony/loc.cpp b/engines/tony/loc.cpp index e6914ebc87..4eced3fbd5 100644 --- a/engines/tony/loc.cpp +++ b/engines/tony/loc.cpp @@ -835,12 +835,12 @@ RMItem::RMItem() { m_bPal = 0; m_nCurSprite = 0; - m_hEndPattern = _vm->_scheduler.createEvent(false, false); + m_hEndPattern = g_scheduler->createEvent(false, false); } RMItem::~RMItem() { Unload(); - _vm->_scheduler.closeEvent(m_hEndPattern); + g_scheduler->closeEvent(m_hEndPattern); } //FIXME: Pass uint32 directly for hCustomSkip @@ -853,11 +853,11 @@ void RMItem::WaitForEndPattern(CORO_PARAM, HANDLE hCustomSkip) { if (m_nCurPattern != 0) { if (hCustomSkip == INVALID_HANDLE_VALUE) - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, m_hEndPattern, INFINITE); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, m_hEndPattern, INFINITE); else { _ctx->h[0] = (uint32)hCustomSkip; _ctx->h[1] = m_hEndPattern; - CORO_INVOKE_4(_vm->_scheduler.waitForMultipleObjects, 2, &_ctx->h[0], false, INFINITE); + CORO_INVOKE_4(g_scheduler->waitForMultipleObjects, 2, &_ctx->h[0], false, INFINITE); } } @@ -1082,7 +1082,7 @@ void RMCharacter::GoTo(RMPoint destcoord, bool bReversed) { if (m_pos == destcoord) { if (minpath == 0) { Stop(); - PulseEvent(hEndOfPath); + g_scheduler->pulseEvent(hEndOfPath); return; } } @@ -1458,7 +1458,7 @@ void RMCharacter::DoFrame(RMGfxTargetBuffer* bigBuf, int loc) { if (!bEndOfPath) Stop(); bEndOfPath = true; - PulseEvent(hEndOfPath); + g_scheduler->pulseEvent(hEndOfPath); } walkcount++; @@ -1496,7 +1496,7 @@ void RMCharacter::DoFrame(RMGfxTargetBuffer* bigBuf, int loc) { if (!bEndOfPath) Stop(); bEndOfPath = true; - PulseEvent(hEndOfPath); + g_scheduler->pulseEvent(hEndOfPath); } } else { // Se siamo già entrati nell'ultimo box, dobbiamo solo muoverci in linea retta verso il @@ -1636,6 +1636,18 @@ void RMCharacter::SetPosition(RMPoint pt, int newloc) { bRemoveFromOT = true; } +void RMCharacter::WaitForEndMovement(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (bMoving) + CORO_INVOKE_2(g_scheduler->waitForSingleObject, hEndOfPath, INFINITE); + + CORO_END_CODE; +} + bool RMCharacter::RemoveThis(void) { if (bRemoveFromOT) return true; @@ -1645,7 +1657,7 @@ bool RMCharacter::RemoveThis(void) { RMCharacter::RMCharacter() { csMove = g_system->createMutex(); - hEndOfPath = CreateEvent(NULL, false, false, NULL); + hEndOfPath = g_scheduler->createEvent(false, false); minpath = 0; curSpeed = 3; bRemoveFromOT = false; @@ -1666,7 +1678,7 @@ RMCharacter::RMCharacter() { RMCharacter::~RMCharacter() { g_system->deleteMutex(csMove); - CloseHandle(hEndOfPath); + g_scheduler->closeEvent(hEndOfPath); } void RMCharacter::LinkToBoxes(RMGameBoxes *boxes) { diff --git a/engines/tony/loc.h b/engines/tony/loc.h index 3a312a0d3c..153ef7f18c 100644 --- a/engines/tony/loc.h +++ b/engines/tony/loc.h @@ -423,7 +423,7 @@ private: STATUS status; int curSpeed; bool bEndOfPath; - HANDLE hEndOfPath; + uint32 hEndOfPath; OSystem::MutexRef csMove; int curLocation; bool bRemoveFromOT; @@ -482,7 +482,7 @@ public: void SetPosition(RMPoint pt, int newloc=-1); // Aspetta la fine del movimento - void WaitForEndMovement(void) { if (bMoving) WaitForSingleObject(hEndOfPath, INFINITE); } + void WaitForEndMovement(CORO_PARAM); void SetFixedScroll(RMPoint fix) { m_fixedScroll = fix; } void SetSpeed(int speed) { curSpeed=speed; } diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp index c5ded67c81..0cea50a3d3 100644 --- a/engines/tony/mpal/mpal.cpp +++ b/engines/tony/mpal/mpal.cpp @@ -961,7 +961,7 @@ void ActionThread(CORO_PARAM, const void *param) { if (item->Command[_ctx->k].type == 1) { // Custom function debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Call=%s params=%d,%d,%d,%d", - _vm->_scheduler.getCurrentPID(), lplpFunctionStrings[item->Command[_ctx->k].nCf].c_str(), + g_scheduler->getCurrentPID(), lplpFunctionStrings[item->Command[_ctx->k].nCf].c_str(), item->Command[_ctx->k].arg1, item->Command[_ctx->k].arg2, item->Command[_ctx->k].arg3, item->Command[_ctx->k].arg4 ); @@ -976,7 +976,7 @@ void ActionThread(CORO_PARAM, const void *param) { } else if (item->Command[_ctx->k].type == 2) { // Variable assign debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Variable=%s", - _vm->_scheduler.getCurrentPID(), item->Command[_ctx->k].lpszVarName); + g_scheduler->getCurrentPID(), item->Command[_ctx->k].lpszVarName); LockVar(); varSetValue(item->Command[_ctx->k].lpszVarName, EvaluateExpression(item->Command[_ctx->k].expr)); @@ -991,7 +991,7 @@ void ActionThread(CORO_PARAM, const void *param) { GlobalFree(item); - debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", _vm->_scheduler.getCurrentPID()); + debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", g_scheduler->getCurrentPID()); CORO_KILL_SELF(); @@ -1012,7 +1012,7 @@ void ShutUpActionThread(CORO_PARAM, const void *param) { CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, pid, INFINITE); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, pid, INFINITE); bExecutingAction = false; @@ -1190,7 +1190,7 @@ void LocationPollThread(CORO_PARAM, const void *param) { for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) if (_ctx->MyThreads[_ctx->i].nItem != 0) { - CORO_INVOKE_3(_vm->_scheduler.waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 0, &_ctx->delayExpired); + CORO_INVOKE_3(g_scheduler->waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 0, &_ctx->delayExpired); // if result ) == WAIT_OBJECT_0) if (!_ctx->delayExpired) @@ -1261,7 +1261,7 @@ void LocationPollThread(CORO_PARAM, const void *param) { _ctx->MyThreads[_ctx->i].nItem = _ctx->MyActions[_ctx->k].nItem; // !!! Nuova gestione dei thread - if ((_ctx->MyThreads[_ctx->i].hThread = _vm->_scheduler.createProcess(ActionThread, &_ctx->newItem, sizeof(LPMPALITEM))) == 0) { + if ((_ctx->MyThreads[_ctx->i].hThread = g_scheduler->createProcess(ActionThread, &_ctx->newItem, sizeof(LPMPALITEM))) == 0) { //if ((_ctx->MyThreads[_ctx->i].hThread=(void*)_beginthread(ActionThread, 10240,(void *)_ctx->newItem))==(void*)-1) GlobalFree(_ctx->newItem); GlobalFree(_ctx->MyThreads); @@ -1295,14 +1295,14 @@ void LocationPollThread(CORO_PARAM, const void *param) { for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) if (_ctx->MyThreads[_ctx->i].nItem != 0) { - CORO_INVOKE_3(_vm->_scheduler.waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 5000, &_ctx->delayExpired); + CORO_INVOKE_3(g_scheduler->waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 5000, &_ctx->delayExpired); /* //if (result != WAIT_OBJECT_0) if (_ctx->delayExpired) TerminateThread(_ctx->MyThreads[_ctx->i].hThread, 0); */ - _vm->_scheduler.killMatchingProcess(_ctx->MyThreads[_ctx->i].hThread); + g_scheduler->killMatchingProcess(_ctx->MyThreads[_ctx->i].hThread); } // Set idle skip off @@ -1343,13 +1343,13 @@ void ShutUpDialogThread(CORO_PARAM, const void *param) { CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, pid, INFINITE); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, pid, INFINITE); bExecutingDialog = false; nExecutingDialog = 0; nExecutingChoice = 0; - _vm->_scheduler.setEvent(hAskChoice); + g_scheduler->setEvent(hAskChoice); CORO_KILL_SELF(); @@ -1501,9 +1501,9 @@ void DoChoice(CORO_PARAM, uint32 nChoice) { /* Avvertiamo il gioco che c'e' una scelta da far fare all'utente, e restiamo in attesa della risposta */ - _vm->_scheduler.resetEvent(hDoneChoice); - _vm->_scheduler.setEvent(hAskChoice); - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, hDoneChoice, INFINITE); + g_scheduler->resetEvent(hDoneChoice); + g_scheduler->setEvent(hAskChoice); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, hDoneChoice, INFINITE); /* Ora che la scelta e' stata effettuata, possiamo eseguire _ctx->i gruppi associati con la scelta */ @@ -1644,19 +1644,19 @@ static uint32 DoDialog(uint32 nDlgOrd, uint32 nGroup) { // Enables the flag to indicate that there is' a running dialogue bExecutingDialog = true; - _vm->_scheduler.resetEvent(hAskChoice); - _vm->_scheduler.resetEvent(hDoneChoice); + g_scheduler->resetEvent(hAskChoice); + g_scheduler->resetEvent(hDoneChoice); // Create a thread that performs the dialogue group // Create the process - if ((h = _vm->_scheduler.createProcess(GroupThread, &nGroup, sizeof(uint32))) == 0) + if ((h = g_scheduler->createProcess(GroupThread, &nGroup, sizeof(uint32))) == 0) return 0; // Create a thread that waits until the end of the dialog process, and will restore the global variables - if (_vm->_scheduler.createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == 0) { + if (g_scheduler->createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == 0) { // Something went wrong, so kill the previously started dialog process - _vm->_scheduler.killMatchingProcess(h); + g_scheduler->killMatchingProcess(h); return 0; } @@ -1690,7 +1690,7 @@ bool DoSelection(uint32 i, uint32 dwData) { return false; nSelectedChoice = j; - _vm->_scheduler.setEvent(hDoneChoice); + g_scheduler->setEvent(hDoneChoice); return true; } @@ -1867,8 +1867,8 @@ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, /* Crea l'evento che verra' utilizzato per avvertire il gioco che c'e' da effettuare una scelta */ - hAskChoice = _vm->_scheduler.createEvent(true, false); - hDoneChoice = _vm->_scheduler.createEvent(true, false); + hAskChoice = g_scheduler->createEvent(true, false); + hDoneChoice = g_scheduler->createEvent(true, false); return true; } @@ -2031,9 +2031,9 @@ void mpalQueryInner(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, va_list v) { /* * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE); */ - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, hAskChoice, INFINITE); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, hAskChoice, INFINITE); - _vm->_scheduler.resetEvent(hAskChoice); + g_scheduler->resetEvent(hAskChoice); if (bExecutingDialog) *dwRet = (uint32)nExecutingChoice; @@ -2217,7 +2217,7 @@ bool mpalStartIdlePoll(int nLoc) { hEndPollingLocations[i] = CreateEvent(NULL, true, false, NULL); // !!! Nuova gestione dei thread - if ((PollingThreads[i] = _vm->_scheduler.createProcess(LocationPollThread, &i, sizeof(uint32))) == 0) + if ((PollingThreads[i] = g_scheduler->createProcess(LocationPollThread, &i, sizeof(uint32))) == 0) // if ((hEndPollingLocations[i]=(void*)_beginthread(LocationPollThread, 10240,(void *)i))==(void*)-1) return false; @@ -2254,7 +2254,7 @@ void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result) { if (nPollingLocations[_ctx->i] == (uint32)nLoc) { SetEvent(hEndPollingLocations[_ctx->i]); - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, PollingThreads[_ctx->i], INFINITE); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, PollingThreads[_ctx->i], INFINITE); CloseHandle(hEndPollingLocations[_ctx->i]); nPollingLocations[_ctx->i] = 0; diff --git a/engines/tony/sched.cpp b/engines/tony/sched.cpp index 199f5991ce..2671b01b7f 100644 --- a/engines/tony/sched.cpp +++ b/engines/tony/sched.cpp @@ -51,6 +51,8 @@ Scheduler::Scheduler() { active->pNext = NULL; g_scheduler = this; // FIXME HACK + + reset(); } Scheduler::~Scheduler() { @@ -102,6 +104,7 @@ void Scheduler::reset() { while (pProc != NULL) { delete pProc->state; pProc->state = 0; + pProc->waiting = false; pProc = pProc->pNext; } @@ -315,6 +318,9 @@ void Scheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool * CORO_BEGIN_CODE(_ctx); + // Signal as waiting + pCurrent->waiting = true; + _ctx->endTime = (duration == INFINITE) ? INFINITE : g_system->getMillis() + duration; if (expired) // Presume it will expire @@ -350,6 +356,9 @@ void Scheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool * CORO_SLEEP(1); } + // Signal waiting is done + pCurrent->waiting = false; + CORO_END_CODE; } @@ -378,6 +387,9 @@ void Scheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, CORO_BEGIN_CODE(_ctx); + // Signal as waiting + pCurrent->waiting = true; + _ctx->endTime = (duration == INFINITE) ? INFINITE : g_system->getMillis() + duration; if (expired) // Presume that delay will expire @@ -419,6 +431,9 @@ void Scheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, CORO_SLEEP(1); } + // Signal waiting is done + pCurrent->waiting = false; + CORO_END_CODE; } @@ -567,12 +582,12 @@ int Scheduler::getCurrentPID() const { * @param pidMask mask to apply to process identifiers before comparison * @return The number of processes killed is returned. */ -int Scheduler::killMatchingProcess(int pidKill, int pidMask) { +int Scheduler::killMatchingProcess(uint32 pidKill, int pidMask) { int numKilled = 0; PROCESS *pProc, *pPrev; // process list pointers for (pProc = active->pNext, pPrev = active; pProc != NULL; pPrev = pProc, pProc = pProc->pNext) { - if ((pProc->pid & pidMask) == pidKill) { + if ((pProc->pid & (uint32)pidMask) == pidKill) { // found a matching process // dont kill the current process @@ -697,4 +712,56 @@ void Scheduler::resetEvent(uint32 pidEvent) { evt->signalled = false; } +/** + * Temporarily sets a given event to true, and then runs all waiting processes, allowing any + * processes waiting on the event to be fired. It then immediately resets the event again. + * @param pidEvent Event PID + * + * @remarks Should not be run inside of another process + */ +void Scheduler::pulseEvent(uint32 pidEvent) { + EVENT *evt = getEvent(pidEvent); + if (!evt) + return; + + // Set the event as true + evt->signalled = true; + + // start dispatching active process list for any processes that are currently waiting + PROCESS *pOriginal = pCurrent; + PROCESS *pNext; + PROCESS *pProc = active->pNext; + while (pProc != NULL) { + pNext = pProc->pNext; + + // Only call processes that are currently waiting (either in waitForSingleObject or + // waitForMultipleObjects). If one is found, execute it immediately + if (pProc->waiting) { + // Dispatch the process + pCurrent = pProc; + pProc->coroAddr(pProc->state, pProc->param); + + if (!pProc->state || pProc->state->_sleep <= 0) { + // Coroutine finished + pCurrent = pCurrent->pPrevious; + killProcess(pProc); + } else { + pProc->sleepTime = pProc->state->_sleep; + } + + // pCurrent may have been changed + pNext = pCurrent->pNext; + pCurrent = NULL; + } + + pProc = pNext; + } + + // Restore the original current process (if one was active) + pCurrent = pOriginal; + + // Reset the event back to non-signalled + evt->signalled = false; +} + } // End of namespace Tony diff --git a/engines/tony/sched.h b/engines/tony/sched.h index 80df1eb942..3997cb2c47 100644 --- a/engines/tony/sched.h +++ b/engines/tony/sched.h @@ -50,6 +50,7 @@ struct PROCESS { int sleepTime; ///< number of scheduler cycles to sleep uint32 pid; ///< process ID + bool waiting; ///< process is currently in a waiting state char param[PARAM_SIZE]; ///< process specific info }; typedef PROCESS *PPROCESS; @@ -134,7 +135,7 @@ public: PROCESS *getCurrentProcess(); int getCurrentPID() const; - int killMatchingProcess(int pidKill, int pidMask = -1); + int killMatchingProcess(uint32 pidKill, int pidMask = -1); void setResourceCallback(VFPTRPP pFunc); @@ -143,6 +144,7 @@ public: void closeEvent(uint32 pidEvent); void setEvent(uint32 pidEvent); void resetEvent(uint32 pidEvent); + void pulseEvent(uint32 pidEvent); }; extern Scheduler *g_scheduler; // FIXME: Temporary global var, to be used until everything has been OOifyied diff --git a/engines/tony/tony.cpp b/engines/tony/tony.cpp index a4a478d40d..d0bce68c3b 100644 --- a/engines/tony/tony.cpp +++ b/engines/tony/tony.cpp @@ -36,7 +36,7 @@ namespace Tony { TonyEngine *_vm; TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Engine(syst), - _gameDescription(gameDesc), _randomSource("tony") { + _gameDescription(gameDesc), _randomSource("tony"), _scheduler() { _vm = this; DebugMan.addDebugChannel(kTonyDebugAnimations, "animations", "Animations debugging"); diff --git a/engines/tony/tonychar.cpp b/engines/tony/tonychar.cpp index 073ebf2be4..ad13d49d54 100644 --- a/engines/tony/tonychar.cpp +++ b/engines/tony/tonychar.cpp @@ -67,7 +67,7 @@ void RMTony::WaitEndOfAction(CORO_PARAM, const void *param) { CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, pid, INFINITE); + CORO_INVOKE_2(g_scheduler->waitForSingleObject, pid, INFINITE); m_bAction = false; @@ -240,26 +240,26 @@ void RMTony::ExecuteAction(int nAction, int nActionItem, int nParm) { if (hThread != INVALID_HANDLE_VALUE) { m_bAction = true; pid = (uint32)hThread; - _vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32)); + g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32)); hActionThread = hThread; } else if (nAction != TA_GOTO) { if (nAction == TA_TALK) { hThread = mpalQueryDoAction(6, 1, 0); m_bAction = true; pid = (uint32)hThread; - _vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32)); + g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32)); hActionThread = hThread; } else if (nAction == TA_PALESATI) { hThread = mpalQueryDoAction(7, 1, 0); m_bAction = true; pid = (uint32)hThread; - _vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32)); + g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32)); hActionThread=hThread; } else { hThread = mpalQueryDoAction(5, 1, 0); m_bAction = true; pid = (uint32)hThread; - _vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32)); + g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32)); hActionThread = hThread; } } @@ -1074,7 +1074,9 @@ bool RMTony::StartTalkCalculate(TALKTYPE nTalkType, int &headStartPat, int &body break; } break; - } + } + + return true; } void RMTony::StartTalk(CORO_PARAM, TALKTYPE nTalkType) { |