/** * Moving actor process - 1 per moving actor in current scene. */ void T1MoverProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); const PMOVER pActor = *(const PMOVER *)param; CORO_BEGIN_CODE(_ctx); while (1) { if (pActor->bSpecReel) { if (!pActor->bHidden) #ifdef DEBUG assert(StepAnimScript(&pActor->actorAnim) != ScriptFinished); // Actor reel has finished! #else StepAnimScript(&pActor->actorAnim); #endif } else DoMoveActor(pActor); CORO_SLEEP(1); // allow rescheduling } CORO_END_CODE; }
/** * Decide when to scroll and scroll when decided to. */ void ScrollProcess(CORO_PARAM, const void *) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); // In Tinsel v2, scenes may play movies, so the background may not always // already be initialized like it is in v1 while (!GetBgObject()) CORO_SLEEP(1); g_ImageH = BgHeight(); // Dimensions g_ImageW = BgWidth(); // of this scene. // Give up if there'll be no purpose in this process if (g_ImageW == SCREEN_WIDTH && g_ImageH == SCREEN_HEIGHT) CORO_KILL_SELF(); if (!TinselV2) { g_LeftScroll = g_DownScroll = 0; // No iterations outstanding g_oldx = g_oldy = 0; g_scrollPixelsX = g_scrollPixelsY = SCROLLPIXELS; } if (!g_scrollActor) g_scrollActor = GetLeadId(); g_pScrollMover = GetMover(g_scrollActor); while (1) { MonitorScroll(); // Set scroll requirement if (g_LeftScroll || g_DownScroll) // Scroll if required ScrollImage(); CORO_SLEEP(1); // allow re-scheduling } CORO_END_CODE; }
/** * WaitInterpret */ void WaitInterpret(CORO_PARAM, PPROCESS pWaitProc, bool *result) { int i; PPROCESS currentProcess = g_scheduler->getCurrentProcess(); assert(currentProcess); assert(currentProcess != pWaitProc); if (result) *result = false; /* * Calling process is the waiter, find its interpret context. */ CORO_BEGIN_CONTEXT; PINT_CONTEXT picWaiter, picWaitee; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); for (i = 0, _ctx->picWaiter = icList; i < NUM_INTERPRET; i++, _ctx->picWaiter++) { if (_ctx->picWaiter->GSort != GS_NONE && _ctx->picWaiter->pProc == currentProcess) { break; } } /* * Find the interpret context of the process we're waiting for */ for (i = 0, _ctx->picWaitee = icList; i < NUM_INTERPRET; i++, _ctx->picWaitee++) { if (_ctx->picWaitee->GSort != GS_NONE && _ctx->picWaitee->pProc == pWaitProc) { break; } } /* * Set the first as waiting for the second */ assert(_ctx->picWaitee->waitNumber2 == 0); _ctx->picWaiter->waitNumber1 = _ctx->picWaitee->waitNumber2 = UniqueWaitNumber(); _ctx->picWaiter->resumeCode = RES_WAITING; /* * Wait for it */ CORO_GIVE_WAY; while (_ctx->picWaiter->resumeCode == RES_WAITING) { CORO_SLEEP(1); } if (result) *result = (_ctx->picWaiter->resumeCode == RES_FINISHED); CORO_END_CODE; }
/** * Perform a walk directly initiated by a click. */ static void WalkProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; PMOVER pMover; int thisWalk; CORO_END_CONTEXT(_ctx); const WP_INIT *to = (const WP_INIT *)param; // get the co-ordinates - copied to process when it was created CORO_BEGIN_CODE(_ctx); _ctx->pMover = GetMover(LEAD_ACTOR); if (TinselV2 && MoverIs(_ctx->pMover) && !MoverIsSWalking(_ctx->pMover)) { assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path _ctx->thisWalk = SetActorDest(_ctx->pMover, to->x, to->y, false, 0); DontScrollCursor(); while (MoverMoving(_ctx->pMover) && (_ctx->thisWalk == GetWalkNumber(_ctx->pMover))) CORO_SLEEP(1); } else if (!TinselV2 && _ctx->pMover->bActive) { assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path GetToken(TOKEN_LEAD); SetActorDest(_ctx->pMover, to->x, to->y, false, 0); DontScrollCursor(); while (MoverMoving(_ctx->pMover)) CORO_SLEEP(1); FreeToken(TOKEN_LEAD); } CORO_END_CODE; }
/** * Process to fade one palette. * A pointer to a 'FADE' structure must be passed to this process when * it is created. */ static void FadeProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; COLORREF fadeRGB[MAX_COLORS]; // local copy of palette const long *pColMult; // pointer to color multiplier table PALETTE *pPalette; // pointer to palette CORO_END_CONTEXT(_ctx); // get the fade data structure - copied to process when it was created const FADE *pFade = (const FADE *)param; CORO_BEGIN_CODE(_ctx); if (TinselV2) // Note that this palette is being faded FadingPalette(pFade->pPalQ, true); // get pointer to palette - reduce pointer indirection a bit _ctx->pPalette = (PALETTE *)LockMem(FROM_32(pFade->pPalQ->hPal)); for (_ctx->pColMult = pFade->pColorMultTable; *_ctx->pColMult >= 0; _ctx->pColMult++) { // go through all multipliers in table - until a negative entry // fade palette using next multiplier if (TinselV2) FadePalette(_ctx->fadeRGB, pFade->pPalQ->palRGB, pFade->pPalQ->numColors, (uint32) *_ctx->pColMult); else FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB, FROM_32(_ctx->pPalette->numColors), (uint32) *_ctx->pColMult); // send new palette to video DAC UpdateDACqueue(pFade->pPalQ->posInDAC, FROM_32(_ctx->pPalette->numColors), _ctx->fadeRGB); // allow time for video DAC to be updated CORO_SLEEP(1); } if (TinselV2) // Note that this palette is being faded FadingPalette(pFade->pPalQ, false); CORO_END_CODE; }
/** * Handles launching a single click action result if the timeout for a double-click * expires */ static void SingleLeftProcess(CORO_PARAM, const void *) { CORO_BEGIN_CONTEXT; uint32 endTicks; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); // Work out when to wait until _ctx->endTicks = DwGetCurrentTime() + (uint32)dclickSpeed; // Timeout a double click (may not work once every 49 days!) do { CORO_SLEEP(1); } while (DwGetCurrentTime() < _ctx->endTicks); if (GetProvNotProcessed()) PlayerEvent(PLR_WALKTO, clickPos); CORO_KILL_SELF(); CORO_END_CODE; }
void CdCD(CORO_PARAM) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); while (g_bChangingCD) { if (g_scheduler->getCurrentProcess()) { // FIXME: CdCD gets passed a nullContext in RegisterGlobals() and // PrimeSceneHopper(), because I didn't know how to get a proper // context without converting the whole calling stack to CORO'd // functions. If these functions really get called while a CD // change is requested, this needs to be resolved. if (coroParam == nullContext) error("CdCD needs context"); CORO_SLEEP(1); } else error("No current process in CdCD()"); } CORO_END_CODE; }
/** * If this is a single click, wait to check it's not the first half of a * double click. * If this is a double click, the process from the waiting single click * gets killed. */ void AllowDclick(CORO_PARAM, PLR_EVENT be) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); if (be == PLR_SLEFT) { GetToken(TOKEN_LEFT_BUT); CORO_SLEEP(_vm->_config->_dclickSpeed+1); FreeToken(TOKEN_LEFT_BUT); // Prevent activation of 2 events on the same tick if (++g_eCount != 1) CORO_KILL_SELF(); break; } else if (be == PLR_DLEFT) { GetToken(TOKEN_LEFT_BUT); FreeToken(TOKEN_LEFT_BUT); } CORO_END_CODE; }
/** * Runs secondary reels for a scene background */ static void BGotherProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; OBJECT *pObj; ANIM anim; CORO_END_CONTEXT(_ctx); const FREEL *pReel = (const FREEL *)param; const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); CORO_BEGIN_CODE(_ctx); // Initialise and insert the object, and initialise its script. _ctx->pObj = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), _ctx->pObj); InitStepAnimScript(&_ctx->anim, pBG[0], FROM_LE_32(pReel->script), BGspeed); while (StepAnimScript(&_ctx->anim) != ScriptFinished) CORO_SLEEP(1); CORO_END_CODE; }
/** * Run main animation that comprises the scene background. */ static void BGmainProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); const FILM *pFilm; const FREEL *pReel; const MULTI_INIT *pmi; // get the stuff copied to process when it was created if (pBG[0] == NULL) { /*** At start of scene ***/ if (!TinselV2) { pReel = (const FREEL *)param; // Get the MULTI_INIT structure pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); // Initialise and insert the object, and initialise its script. pBG[0] = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[0]); InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed); bgReels = 1; } else { /*** At start of scene ***/ pFilm = (const FILM *)LockMem(hBackground); bgReels = FROM_LE_32(pFilm->numreels); int i; for (i = 0; i < bgReels; i++) { // Get the MULTI_INIT structure pmi = (PMULTI_INIT) LockMem(FROM_LE_32(pFilm->reels[i].mobj)); // Initialise and insert the object, and initialise its script. pBG[i] = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[i]); MultiSetZPosition(pBG[i], 0); InitStepAnimScript(&thisAnim[i], pBG[i], FROM_LE_32(pFilm->reels[i].script), BGspeed); if (i > 0) pBG[i-1]->pSlave = pBG[i]; } } if (bDoFadeIn) { FadeInFast(NULL); bDoFadeIn = false; } else if (TinselV2) PokeInTagColor(); for (;;) { for (int i = 0; i < bgReels; i++) { if (StepAnimScript(&thisAnim[i]) == ScriptFinished) error("Background animation has finished"); } CORO_SLEEP(1); } } else { // New background during scene if (!TinselV2) { pReel = (const FREEL *)param; InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed); StepAnimScript(&thisAnim[0]); } else { pFilm = (const FILM *)LockMem(hBackground); assert(bgReels == (int32)FROM_LE_32(pFilm->numreels)); // Just re-initialise the scripts. for (int i = 0; i < bgReels; i++) { InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed); StepAnimScript(&thisAnim[i]); } } } CORO_END_CODE; }
/** * Process to handle changes in the mouse buttons. */ static void MouseProcess(CORO_PARAM, const void *) { // COROUTINE CORO_BEGIN_CONTEXT; bool lastLWasDouble; bool lastRWasDouble; uint32 lastLeftClick, lastRightClick; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); _ctx->lastLWasDouble = false; _ctx->lastRWasDouble = false; _ctx->lastLeftClick = _ctx->lastRightClick = DwGetCurrentTime(); while (true) { if (mouseButtons.empty()) { // allow scheduling CORO_SLEEP(1); continue; } // get next mouse button event Common::EventType type = *mouseButtons.begin(); mouseButtons.erase(mouseButtons.begin()); int xp, yp; GetCursorXYNoWait(&xp, &yp, true); const Common::Point mousePos(xp, yp); switch (type) { case Common::EVENT_LBUTTONDOWN: // left button press if (DwGetCurrentTime() - _ctx->lastLeftClick < (uint32)dclickSpeed) { // Left button double-click if (TinselV2) { // Kill off the button process and fire off the action command g_scheduler->killMatchingProcess(PID_BTN_CLICK, -1); PlayerEvent(PLR_ACTION, clickPos); } else { // signal left drag start ProcessButEvent(PLR_DRAG1_START); // signal left double click event ProcessButEvent(PLR_DLEFT); } _ctx->lastLWasDouble = true; } else { // Initial mouse down - either for a single click, or potentially // the start of a double-click action if (TinselV2) { PlayerEvent(PLR_DRAG1_START, mousePos); ProvNotProcessed(); PlayerEvent(PLR_PROV_WALKTO, mousePos); } else { // signal left drag start ProcessButEvent(PLR_DRAG1_START); // signal left single click event ProcessButEvent(PLR_SLEFT); } _ctx->lastLWasDouble = false; } break; case Common::EVENT_LBUTTONUP: // left button release // update click timer if (_ctx->lastLWasDouble == false) { _ctx->lastLeftClick = DwGetCurrentTime(); // If player control is enabled, start a process which, if it times out, // will activate a single button click if (TinselV2 && ControlIsOn()) { clickPos = mousePos; g_scheduler->createProcess(PID_BTN_CLICK, SingleLeftProcess, NULL, 0); } } else _ctx->lastLeftClick -= dclickSpeed; if (TinselV2) // Signal left drag end PlayerEvent(PLR_DRAG1_END, mousePos); else // signal left drag end ProcessButEvent(PLR_DRAG1_END); break; case Common::EVENT_RBUTTONDOWN: // right button press if (DwGetCurrentTime() - _ctx->lastRightClick < (uint32)dclickSpeed) { // Right button double-click if (TinselV2) { PlayerEvent(PLR_NOEVENT, clickPos); } else { // signal right drag start ProcessButEvent(PLR_DRAG2_START); // signal right double click event ProcessButEvent(PLR_DRIGHT); } _ctx->lastRWasDouble = true; } else { if (TinselV2) { PlayerEvent(PLR_DRAG2_START, mousePos); PlayerEvent(PLR_LOOK, mousePos); } else { // signal right drag start ProcessButEvent(PLR_DRAG2_START); // signal right single click event ProcessButEvent(PLR_SRIGHT); } _ctx->lastRWasDouble = false; } break; case Common::EVENT_RBUTTONUP: // right button release // update click timer if (_ctx->lastRWasDouble == false) _ctx->lastRightClick = DwGetCurrentTime(); else _ctx->lastRightClick -= dclickSpeed; if (TinselV2) // Signal left drag end PlayerEvent(PLR_DRAG2_END, mousePos); else // signal right drag end ProcessButEvent(PLR_DRAG2_END); break; default: break; } } CORO_END_CODE; }
/** * Process to handle keypresses */ void KeyboardProcess(CORO_PARAM, const void *) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); while (true) { if (keypresses.empty()) { // allow scheduling CORO_SLEEP(1); continue; } // Get the next keyboard event off the stack Common::Event evt = *keypresses.begin(); keypresses.erase(keypresses.begin()); const Common::Point mousePos = _vm->getMousePosition(); // Switch for special keys switch (evt.kbd.keycode) { // Drag action case Common::KEYCODE_LALT: case Common::KEYCODE_RALT: if (evt.type == Common::EVENT_KEYDOWN) { if (!bSwapButtons) ProcessButEvent(PLR_DRAG2_START); else ProcessButEvent(PLR_DRAG1_START); } else { if (!bSwapButtons) ProcessButEvent(PLR_DRAG1_END); else ProcessButEvent(PLR_DRAG2_END); } continue; case Common::KEYCODE_LCTRL: case Common::KEYCODE_RCTRL: if (evt.type == Common::EVENT_KEYDOWN) { ProcessKeyEvent(PLR_LOOK); } else { // Control key release } continue; default: break; } // At this point only key down events need processing if (evt.type == Common::EVENT_KEYUP) continue; if (_vm->_keyHandler != NULL) // Keyboard is hooked, so pass it on to that handler first if (!_vm->_keyHandler(evt.kbd)) continue; switch (evt.kbd.keycode) { /*** SPACE = WALKTO ***/ case Common::KEYCODE_SPACE: ProcessKeyEvent(PLR_WALKTO); continue; /*** RETURN = ACTION ***/ case Common::KEYCODE_RETURN: case Common::KEYCODE_KP_ENTER: ProcessKeyEvent(PLR_ACTION); continue; /*** l = LOOK ***/ case Common::KEYCODE_l: // LOOK ProcessKeyEvent(PLR_LOOK); continue; case Common::KEYCODE_ESCAPE: ProcessKeyEvent(PLR_ESCAPE); continue; #ifdef SLOW_RINCE_DOWN case '>': AddInterlude(1); continue; case '<': AddInterlude(-1); continue; #endif case Common::KEYCODE_F1: // Options dialog ProcessKeyEvent(PLR_MENU); continue; case Common::KEYCODE_F5: // Save game ProcessKeyEvent(PLR_SAVE); continue; case Common::KEYCODE_F7: // Load game ProcessKeyEvent(PLR_LOAD); continue; case Common::KEYCODE_m: // Debug facility - scene hopper if (TinselV2 && (evt.kbd.flags == Common::KBD_ALT)) ProcessKeyEvent(PLR_JUMP); break; case Common::KEYCODE_q: if ((evt.kbd.flags == Common::KBD_CTRL) || (evt.kbd.flags == Common::KBD_ALT)) ProcessKeyEvent(PLR_QUIT); continue; case Common::KEYCODE_PAGEUP: case Common::KEYCODE_KP9: ProcessKeyEvent(PLR_PGUP); continue; case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_KP3: ProcessKeyEvent(PLR_PGDN); continue; case Common::KEYCODE_HOME: case Common::KEYCODE_KP7: ProcessKeyEvent(PLR_HOME); continue; case Common::KEYCODE_END: case Common::KEYCODE_KP1: ProcessKeyEvent(PLR_END); continue; default: ProcessKeyEvent(PLR_NOEVENT); break; } } CORO_END_CODE; }
/** * Tinsel 2 Moving actor process * - 1 per moving actor in current scene. */ void T2MoverProcess(CORO_PARAM, const void *param) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); // Get the co-ordinates - copied to process when it was created const MAINIT *rpos = (const MAINIT *)param; PMOVER pMover = rpos->pMover; int i; FILM *pFilm; PMULTI_INIT pmi; CORO_BEGIN_CODE(_ctx); for (i = 0; i < TOTAL_SCALES; i++) { if (pMover->walkReels[i][FORWARD]) break; } assert(i < TOTAL_SCALES); InitMover(pMover); InitialPathChecks(pMover, rpos->X, rpos->Y); pFilm = (FILM *)LockMem(pMover->walkReels[i][FORWARD]); // Any old reel pmi = (PMULTI_INIT)LockMem(FROM_32(pFilm->reels[0].mobj)); // Poke in the background palette PokeInPalette(pmi); pMover->actorObj = MultiInitObject(pmi); // FIXME: This is what the original did. A bug, perhaps? // pMover->actorID = pMover->actorID; pMover->bActive = true; // add it to display list MultiInsertObject( GetPlayfieldList(FIELD_WORLD), pMover->actorObj ); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, pFilm->reels[0].script, ONE_SECOND/pFilm->frate); pMover->stepCount = 0; MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY); // If no path, just use first path in the scene if (pMover->hCpath != NOPOLY) SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath)); else SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly())); // Make him the right size SetMoverStanding(pMover); HideMover(pMover); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden for (;;) { if (pMover->bSpecReel) { if (!pMover->bHidden) StepAnimScript(&pMover->actorAnim); } else DoMoveActor(pMover); CheckBrightness(pMover); CORO_SLEEP(1); } CORO_END_CODE; }