/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Should really be called "moving actors.c" */ #include "tinsel/actors.h" #include "tinsel/anim.h" #include "tinsel/background.h" #include "tinsel/config.h" #include "tinsel/dw.h" #include "tinsel/film.h" #include "tinsel/handle.h" #include "tinsel/dialogs.h" #include "tinsel/mareels.h" #include "tinsel/move.h" #include "tinsel/multiobj.h" // multi-part object defintions etc. #include "tinsel/object.h" #include "tinsel/pcode.h" #include "tinsel/pid.h" #include "tinsel/play.h" #include "tinsel/polygons.h" #include "tinsel/movers.h" #include "tinsel/sched.h" #include "tinsel/sysvar.h" #include "tinsel/timers.h" #include "tinsel/tinsel.h" #include "tinsel/token.h" #include "common/textconsole.h" #include "common/util.h" namespace Tinsel { //----------------- LOCAL GLOBAL DATA -------------------- // These vars are reset upon engine destruction static MOVER g_Movers[MAX_MOVERS]; //----------------- FUNCTIONS ---------------------------- /** * Called from ActorPalette(), normally once just after the beginning of time. */ void StoreMoverPalette(PMOVER pMover, int startColor, int length) { pMover->startColor = startColor; pMover->paletteLength = length; } /** * Called from the moving actor's main loop. */ static void CheckBrightness(PMOVER pMover) { int brightness; if (pMover->hCpath == NOPOLY || pMover->bHidden) return; brightness = GetBrightness(pMover->hCpath, pMover->objY); if (brightness != pMover->brightness) { // Do it all immediately on first appearance, // otherwise do it iteratively if (pMover->brightness == BOGUS_BRIGHTNESS) pMover->brightness = brightness; // all the way else if (brightness > pMover->brightness) pMover->brightness++; // ramp up else pMover->brightness--; // ramp down DimPartPalette(_vm->_bg->BgPal(), pMover->startColor, pMover->paletteLength, pMover->brightness); } } /** * Called from ActorBrightness() Glitter call. * Typically called before the moving actor is created * at the start of a scene to cover a walk-in Play(). */ void MoverBrightness(PMOVER pMover, int brightness) { // Note: Like with some of the Tinsel1 code, this routine original had a process yield // if BgPal is NULL, and has been changed for ScummVM to a simple assert // This is changed from a ProcessGiveWay in DW2 to an assert in ScummVM assert(_vm->_bg->BgPal()); // Do it all immediately DimPartPalette(_vm->_bg->BgPal(), pMover->startColor, pMover->paletteLength, brightness); // The actor is probably hidden at this point, pMover->brightness = brightness; } /** * RebootMovers */ void RebootMovers() { memset(g_Movers, 0, sizeof(g_Movers)); } /** * Given an actor number, return pointer to its moving actor structure, * if it is a moving actor. */ PMOVER GetMover(int ano) { int i; // Slot 0 is reserved for lead actor if (ano == _vm->_actor->GetLeadId() || ano == LEAD_ACTOR) return &g_Movers[0]; for (i = 1; i < MAX_MOVERS; i++) if (g_Movers[i].actorID == ano) return &g_Movers[i]; return NULL; } /** * Register an actor as being a moving one. */ PMOVER RegisterMover(int ano) { int i; // Slot 0 is reserved for lead actor if (ano == _vm->_actor->GetLeadId() || ano == LEAD_ACTOR) { g_Movers[0].actorToken = TOKEN_LEAD; g_Movers[0].actorID = _vm->_actor->GetLeadId(); return &g_Movers[0]; } // Check it hasn't already been declared for (i = 1; i < MAX_MOVERS; i++) { if (g_Movers[i].actorID == ano) { // Actor is already a moving actor return &g_Movers[i]; } } // Find an empty slot for (i = 1; i < MAX_MOVERS; i++) if (!g_Movers[i].actorID) { g_Movers[i].actorToken = TOKEN_LEAD + i; g_Movers[i].actorID = ano; return &g_Movers[i]; } error("Too many moving actors"); } /** * Given an index, returns the associated moving actor. * * At the time of writing, used by the effect process. */ PMOVER GetLiveMover(int index) { assert(index >= 0 && index < MAX_MOVERS); // out of range if (g_Movers[index].bActive) return &g_Movers[index]; else return NULL; } bool IsMAinEffectPoly(int index) { assert(index >= 0 && index < MAX_MOVERS); // out of range return g_Movers[index].bInEffect; } void SetMoverInEffect(int index, bool tf) { assert(index >= 0 && index < MAX_MOVERS); // out of range g_Movers[index].bInEffect = tf; } /** * Remove a moving actor from the current scene. */ void KillMover(PMOVER pMover) { if (pMover->bActive) { pMover->bActive = false; MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), pMover->actorObj); pMover->actorObj = nullptr; assert(CoroScheduler.getCurrentProcess() != pMover->pProc); CoroScheduler.killProcess(pMover->pProc); } } /** * getMActorState */ bool getMActorState(PMOVER pActor) { return pActor->bActive; } /** * If the actor's object exists, move it behind the background. * MultiHideObject() is deliberately not used, as StepAnimScript() calls * cause the object to re-appear. */ void HideMover(PMOVER pMover, int sf) { assert(pMover); // Hiding null moving actor pMover->bHidden = true; if (!TinselV2) { // sf is only passed in Tinsel v1 pMover->SlowFactor = sf; } else { // Tinsel 2 specific code if (_vm->_actor->IsTaggedActor(pMover->actorID)) { // It may be pointed to _vm->_actor->SetActorPointedTo(pMover->actorID, false); _vm->_actor->SetActorTagWanted(pMover->actorID, false, false, 0); } } if (pMover->actorObj) MultiSetZPosition(pMover->actorObj, -1); } /** * MoverHidden */ bool MoverHidden(PMOVER pMover) { if (pMover) return pMover->bHidden; else return false; } /** * To be or not to be? If it be, then it is. */ bool MoverIs(PMOVER pMover) { if (TinselV2) return pMover->actorObj ? true : false; else return getMActorState(pMover); } /** * To be SWalk()ing or not to be SWalk()ing? */ bool MoverIsSWalking(PMOVER pMover) { return (MoverMoving(pMover) && pMover->bIgPath); } /** * MoverMoving() */ bool MoverMoving(PMOVER pMover) { if (!TinselV2) return pMover->bMoving; if (pMover->UtargetX == -1 && pMover->UtargetY == -1) return false; else return true; } /** * Return an actor's walk ticket. */ int GetWalkNumber(PMOVER pMover) { return pMover->walkNumber; } /** * GetMoverId */ int GetMoverId(PMOVER pMover) { return pMover->actorID; } /** * Sets the mover Z position */ void SetMoverZ(PMOVER pMover, int y, uint32 zFactor) { if (!pMover->bHidden) { if (!TinselV2) _vm->_actor->AsetZPos(pMover->actorObj, y, zFactor); else if (MoverIsSWalking(pMover) && pMover->zOverride != -1) { // Special for SWalk() MultiSetZPosition(pMover->actorObj, (pMover->zOverride << ZSHIFT) + y); } else { // Normal case MultiSetZPosition(pMover->actorObj, (zFactor << ZSHIFT) + y); } } } void SetMoverZoverride(PMOVER pMover, uint32 zFactor) { pMover->zOverride = zFactor; } /** * UnHideMover */ void UnHideMover(PMOVER pMover) { assert(pMover); // unHiding null moving actor if (!TinselV2 || pMover->bHidden) { pMover->bHidden = false; // Make visible on the screen if (pMover->actorObj) { // 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())); } } } /** * Clear everything out at actor start-up time. */ static void InitMover(PMOVER pMover) { pMover->bActive = false; pMover->actorObj = nullptr; pMover->objX = pMover->objY = 0; pMover->hRpath = NOPOLY; pMover->targetX = pMover->targetY = -1; pMover->ItargetX = pMover->ItargetY = -1; pMover->hIpath = NOPOLY; pMover->UtargetX = pMover->UtargetY = -1; pMover->hUpath = NOPOLY; pMover->hCpath = NOPOLY; pMover->over = false; pMover->InDifficulty = NO_PROB; pMover->hFnpath = NOPOLY; pMover->npstatus = NOT_IN; pMover->line = 0; pMover->Tline = 0; if (pMover->direction != FORWARD && pMover->direction != AWAY && pMover->direction != LEFTREEL && pMover->direction != RIGHTREEL) pMover->direction = FORWARD; if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES) pMover->scale = 1; pMover->brightness = BOGUS_BRIGHTNESS; // Force initial setup pMover->bNoPath = false; pMover->bIgPath = false; pMover->bHidden = false; // 20/2/95 pMover->bStop = false; pMover->walkNumber= 0; pMover->stepCount = 0; pMover->bWalkReel = false; pMover->bSpecReel = false; pMover->hLastFilm = 0; pMover->hPushedFilm = 0; pMover->bInEffect = false; pMover->walkedFromX = pMover->walkedFromY = 0; } /** * Get it into our heads that there's nothing doing. * Called at the end of a scene. */ void DropMovers() { for (int i = 0; i < MAX_MOVERS; i++) InitMover(&g_Movers[i]); } /** * Reposition a moving actor. */ void PositionMover(PMOVER pMover, int x, int y) { int z; int node; HPOLYGON hPath; assert(pMover); // Moving null moving actor assert(pMover->actorObj); pMover->objX = x; pMover->objY = y; MultiSetAniXY(pMover->actorObj, x, y); hPath = InPolygon(x, y, PATH); if (hPath != NOPOLY) { pMover->hCpath = hPath; if (PolySubtype(hPath) == NODE) { node = NearestNodeWithin(hPath, x, y); getNpathNode(hPath, node, &pMover->objX, &pMover->objY); pMover->hFnpath = hPath; pMover->line = node; pMover->npstatus = GOING_UP; } else { pMover->hFnpath = NOPOLY; pMover->npstatus = NOT_IN; } z = GetScale(hPath, pMover->objY); pMover->scale = z; SetMoverStanding(pMover); } else { pMover->bNoPath = true; pMover->hFnpath = NOPOLY; // Ain't in one pMover->npstatus = NOT_IN; // Ensure legal reel and scale if (pMover->direction < 0 || pMover->direction > 3) pMover->direction = FORWARD; if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES) pMover->scale = 1; } } /** * Get position of a moving actor. */ void GetMoverPosition(PMOVER pMover, int *paniX, int *paniY) { assert(pMover); // Getting null moving actor's position if (pMover->actorObj != NULL) GetAniPosition(pMover->actorObj, paniX, paniY); else { *paniX = 0; *paniY = 0; } } /** * Moving actor's mid-top position. */ void GetMoverMidTop(PMOVER pMover, int *aniX, int *aniY) { assert(pMover); // Getting null moving actor's mid-top position assert(pMover->actorObj); // Getting null moving actor's mid-top position *aniX = (MultiLeftmost(pMover->actorObj) + MultiRightmost(pMover->actorObj)) / 2; *aniY = MultiHighest(pMover->actorObj); } /** * Moving actor's left-most co-ordinate. */ int GetMoverLeft(PMOVER pMover) { assert(pMover); // Getting null moving actor's leftmost position if (pMover->type == MOVER_3D) { warning("TODO: Finish implementation of GetMoverLeft() for Noir"); return 0; } else { assert(pMover->actorObj); // Getting null moving actor's leftmost position return MultiLeftmost(pMover->actorObj); } } /** * Moving actor's right-most co-ordinate. */ int GetMoverRight(PMOVER pMover) { assert(pMover); // Getting null moving actor's rightmost position if (pMover->type == MOVER_3D) { warning("TODO: Finish implementation of GetMoverRight() for Noir"); return 0; } else { assert(pMover->actorObj); // Getting null moving actor's rightmost position return MultiRightmost(pMover->actorObj); } } /** * Moving actor's top co-ordinate. */ int GetMoverTop(PMOVER pMover) { assert(pMover); // Getting null moving actor's topmost position if (pMover->type == MOVER_3D) { warning("TODO: Finish implementation of GetMoverTop() for Noir"); return 0; } else { assert(pMover->actorObj); // Getting null moving actor's topmost position return MultiHighest(pMover->actorObj); } } /** * Moving actor's bottom co-ordinate. */ int GetMoverBottom(PMOVER pMover) { assert(pMover); // Getting null moving actor's bottommost position if (pMover->type == MOVER_3D) { warning("TODO: Finish implementation of GetMoverBottom() for Noir"); return 0; } else { assert(pMover->actorObj); // Getting null moving actor's bottommost position return MultiLowest(pMover->actorObj); } } /** * See if moving actor is stood within a polygon. */ bool MoverIsInPolygon(PMOVER pMover, HPOLYGON hp) { assert(pMover); // Checking if null moving actor is in polygon assert(pMover->actorObj); // Checking if null moving actor is in polygon int aniX, aniY; GetAniPosition(pMover->actorObj, &aniX, &aniY); return IsInPolygon(aniX, aniY, hp); } /** * Change which reel is playing for a moving actor. */ void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn) { const FILM *pfilm; assert(pMover->actorObj); // Altering null moving actor's animation script if (fn == AR_POPREEL) { // Use the saved film film = pMover->hPushedFilm; } if (fn == AR_PUSHREEL) { // Save the one we're replacing pMover->hPushedFilm = (pMover->bSpecReel) ? pMover->hLastFilm : 0; } if (film == 0) { if (pMover->bSpecReel) { // Revert to 'normal' actor SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true); pMover->bSpecReel = false; } } else { // Remember this one in case the actor talks pMover->hLastFilm = film; pfilm = (const FILM *)_vm->_handle->LockMem(film); assert(pfilm != NULL); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate)); if (!TinselV2) pMover->stepCount = 0; // 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())); if (fn == AR_WALKREEL) { pMover->bSpecReel = false; pMover->bWalkReel = true; } else { pMover->bSpecReel = true; pMover->bWalkReel = false; #ifdef DEBUG assert(StepAnimScript(&pMover->actorAnim) != ScriptFinished); // Actor reel has finished! #else StepAnimScript(&pMover->actorAnim); // 04/01/95 #endif } // Hang on, we may not want him yet! 04/01/95 if (pMover->bHidden) MultiSetZPosition(pMover->actorObj, -1); } } /** * Return the actor's direction. */ DIRECTION GetMoverDirection(PMOVER pMover) { return pMover->direction; } /** * Return the actor's scale. */ int GetMoverScale(PMOVER pMover) { return pMover->scale; } /** * Point actor in specified derection */ void SetMoverDirection(PMOVER pMover, DIRECTION dirn) { pMover->direction = dirn; } /** * Get actor to adopt its appropriate standing reel. */ void SetMoverStanding(PMOVER pMover) { if (TinselV3) { warning("TODO: Finish implementation of GetMoverStanding() for Noir"); return; } assert(pMover->actorObj); AlterMover(pMover, pMover->standReels[pMover->scale - 1][pMover->direction], AR_NORMAL); } /** * Get actor to adopt its appropriate walking reel. */ void SetMoverWalkReel(PMOVER pMover, DIRECTION reel, int scale, bool force) { SCNHANDLE whichReel; const FILM *pfilm; // Kill off any play that may be going on for this actor // and restore the real actor _vm->_actor->storeActorReel(pMover->actorID, NULL, 0, NULL, 0, 0, 0); UnHideMover(pMover); // Don't do it if using a special walk reel if (pMover->bWalkReel) return; if (force || pMover->scale != scale || pMover->direction != reel) { assert(reel >= 0 && reel <= 3 && scale > 0 && scale <= TOTAL_SCALES); // out of range scale or reel // If scale change and both are regular scales // and there's a scaling reel in the right direction if (pMover->scale != scale && scale <= NUM_MAINSCALES && pMover->scale <= NUM_MAINSCALES && (whichReel = ScalingReel(pMover->actorID, pMover->scale, scale, reel)) != 0) { // error("Cripes"); ; // Use what is now in 'whichReel' } else { whichReel = pMover->walkReels[scale-1][reel]; assert(whichReel); // no reel } pfilm = (const FILM *)_vm->_handle->LockMem(whichReel); assert(pfilm != NULL); // no film InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), 1); // Synchronised walking reels assert(pMover->stepCount >= 0); SkipFrames(&pMover->actorAnim, pMover->stepCount); pMover->scale = scale; pMover->direction = reel; } } /** * Sort some stuff out at actor start-up time. */ static void InitialPathChecks(PMOVER pMover, int xpos, int ypos) { HPOLYGON hPath; int node; int z; pMover->objX = xpos; pMover->objY = ypos; /*-------------------------------------- | If Actor is in a follow nodes path, | | position it at the nearest node. | --------------------------------------*/ hPath = InPolygon(xpos, ypos, PATH); if (hPath != NOPOLY) { pMover->hCpath = hPath; if (PolySubtype(hPath) == NODE) { node = NearestNodeWithin(hPath, xpos, ypos); getNpathNode(hPath, node, &pMover->objX, &pMover->objY); pMover->hFnpath = hPath; pMover->line = node; pMover->npstatus = GOING_UP; } z = GetScale(hPath, pMover->objY); } else { pMover->bNoPath = true; z = GetScale(FirstPathPoly(), pMover->objY); } if (TinselV3) { warning("TODO: Finish implementation of InitialPathChecks() for Noir"); } else { SetMoverWalkReel(pMover, FORWARD, z, false); } } static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) { const FILM *pfilm; const MULTI_INIT *pmi; const FRAME *pFrame; IMAGE *pim; assert(_vm->_bg->BgPal()); // Can't start actor without a background palette assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels InitMover(pMover); InitialPathChecks(pMover, X, Y); pfilm = (const FILM *)_vm->_handle->LockMem(pMover->walkReels[0][FORWARD]); pmi = (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(pfilm->reels[0].mobj)); //--- pFrame = (const FRAME *)_vm->_handle->LockMem(FROM_32(pmi->hMulFrame)); // get pointer to image pim = (IMAGE *)_vm->_handle->LockMem(READ_32(pFrame)); // handle to image pim->hImgPal = TO_32(_vm->_bg->BgPal()); //--- pMover->actorObj = MultiInitObject(pmi); /**/ assert(pMover->actorID == id); pMover->actorID = id; // add it to display list MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), pMover->actorObj); _vm->_actor->storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(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); //**** if added 18/11/94, am if (X != MAGICX && Y != MAGICY) { HideMover(pMover, 0); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden } pMover->bActive = true; } /** * 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; } /** * 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 *)_vm->_handle->LockMem(pMover->walkReels[i][FORWARD]); // Any old reel pmi = (PMULTI_INIT)_vm->_handle->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(_vm->_bg->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; } /** * Tinsel 3 Moving actor process * - 1 per moving actor in current scene. */ void T3MoverProcess(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; CORO_BEGIN_CODE(_ctx); warning("TODO: Finish implementation of T3MoverProcess() for Noir"); InitMover(pMover); InitialPathChecks(pMover, rpos->X, rpos->Y); HideMover(pMover); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden for (;;) { DoMoveActor(pMover); CheckBrightness(pMover); CORO_SLEEP(1); } CORO_END_CODE; } /** * Creates a handling process for a moving actor */ void MoverProcessCreate(int X, int Y, int id, PMOVER pMover) { if (TinselV2 || TinselV3) { MAINIT iStruct; iStruct.X = X; iStruct.Y = Y; iStruct.pMover = pMover; CoroScheduler.createProcess(PID_MOVER, TinselV3 ? T3MoverProcess : T2MoverProcess, &iStruct, sizeof(MAINIT)); } else { MoverProcessHelper(X, Y, id, pMover); pMover->pProc = CoroScheduler.createProcess(PID_MOVER, T1MoverProcess, &pMover, sizeof(PMOVER)); } } /** * Check for moving actor collision. */ PMOVER InMoverBlock(PMOVER pMover, int x, int y) { int caX; // Calling actor's pos'n int caL, caR; // Calling actor's left and right int taX, taY; // Test actor's pos'n int taL, taR; // Test actor's left and right caX = pMover->objX; if (pMover->hFnpath != NOPOLY || GetNoBlocking()) return NULL; caL = GetMoverLeft(pMover) + x - caX; caR = GetMoverRight(pMover) + x - caX; for (int i = 0; i < MAX_MOVERS; i++) { if (pMover == &g_Movers[i] || (TinselV2 && (g_Movers[i].actorObj == NULL)) || (!TinselV2 && !g_Movers[i].bActive)) continue; // At around the same height? GetMoverPosition(&g_Movers[i], &taX, &taY); if (g_Movers[i].hFnpath != NOPOLY) continue; if (ABS(y - taY) > 2) // 2 was 8 continue; // To the left? taL = GetMoverLeft(&g_Movers[i]); if (caR <= taL) continue; // To the right? taR = GetMoverRight(&g_Movers[i]); if (caL >= taR) continue; return &g_Movers[i]; } return NULL; } /** * Copies key information for savescn.c to store away. */ void SaveMovers(SAVED_MOVER *sMoverInfo) { for (int i = 0; i < MAX_MOVERS; i++) { sMoverInfo[i].bActive = !TinselV2 ? g_Movers[i].bActive : g_Movers[i].actorObj != NULL; sMoverInfo[i].actorID = g_Movers[i].actorID; sMoverInfo[i].objX = g_Movers[i].objX; sMoverInfo[i].objY = g_Movers[i].objY; sMoverInfo[i].hLastfilm = g_Movers[i].hLastFilm; if (TinselV2) { sMoverInfo[i].bHidden = g_Movers[i].bHidden; sMoverInfo[i].brightness = g_Movers[i].brightness; sMoverInfo[i].startColor = g_Movers[i].startColor; sMoverInfo[i].paletteLength = g_Movers[i].paletteLength; } memcpy(sMoverInfo[i].walkReels, g_Movers[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE)); memcpy(sMoverInfo[i].standReels, g_Movers[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE)); memcpy(sMoverInfo[i].talkReels, g_Movers[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE)); } } void RestoreAuxScales(SAVED_MOVER *sMoverInfo) { for (int i = 0; i < MAX_MOVERS; i++) { if (TinselV2) g_Movers[i].actorID = sMoverInfo[i].actorID; memcpy(g_Movers[i].walkReels, sMoverInfo[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE)); memcpy(g_Movers[i].standReels, sMoverInfo[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE)); memcpy(g_Movers[i].talkReels, sMoverInfo[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE)); } } PMOVER NextMover(PMOVER pMover) { int next; if (pMover == NULL) next = 0; else next = pMover - g_Movers + 1; if (g_Movers[next].actorID) return &g_Movers[next]; else return NULL; } void StopMover(PMOVER pMover) { pMover->bStop = true; DoMoveActor(pMover); } } // End of namespace Tinsel