/* 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. * * $URL$ * $Id$ * * Handles things to do with actors, delegates much moving actor stuff. */ #include "tinsel/actors.h" #include "tinsel/events.h" #include "tinsel/film.h" // for FREEL #include "tinsel/handle.h" #include "tinsel/inventory.h" // INV_NOICON #include "tinsel/move.h" #include "tinsel/multiobj.h" #include "tinsel/object.h" // for POBJECT #include "tinsel/pcode.h" #include "tinsel/pid.h" #include "tinsel/polygons.h" #include "tinsel/rince.h" #include "tinsel/sched.h" #include "tinsel/serializer.h" #include "tinsel/token.h" #include "common/util.h" namespace Tinsel { //----------------- LOCAL DEFINES -------------------- #include "common/pack-start.h" // START STRUCT PACKING /** actor struct - one per actor */ struct ACTOR_STRUC { int32 masking; //!< type of actor masking SCNHANDLE hActorId; //!< handle actor ID string index SCNHANDLE hActorCode; //!< handle to actor script } PACKED_STRUCT; #include "common/pack-end.h" // END STRUCT PACKING //----------------- LOCAL GLOBAL DATA -------------------- static int LeadActorId = 0; // The lead actor static int NumActors = 0; // The total number of actors in the game struct ACTORINFO { bool alive; // TRUE == alive bool hidden; // TRUE == hidden bool completed; // TRUE == script played out int x, y, z; int32 mtype; // DEFAULT(b'ground), MASK, ALWAYS SCNHANDLE actorCode; // The actor's script const FREEL *presReel; // the present reel int presRnum; // the present reel number SCNHANDLE presFilm; // the film that reel belongs to OBJECT *presObj; // reference for position information int presX, presY; bool tagged; // actor tagged? SCNHANDLE hTag; // handle to tag text int tType; // e.g. TAG_Q1TO3 bool escOn; int escEv; COLORREF tColour; // Text colour SCNHANDLE playFilm; // revert to this after talks SCNHANDLE talkFilm; // this be deleted in the future! SCNHANDLE latestFilm; // the last film ordered bool talking; int steps; }; static ACTORINFO *actorInfo = 0; static COLORREF defaultColour = 0; // Text colour static bool bActorsOn = false; static int ti = 0; /** * Called once at start-up time, and again at restart time. * Registers the total number of actors in the game. * @param num Chunk Id */ void RegisterActors(int num) { if (actorInfo == NULL) { // Store the total number of actors in the game NumActors = num; // Check we can save so many assert(NumActors <= MAX_SAVED_ALIVES); // Allocate RAM for actorInfo // FIXME: For now, we always allocate MAX_SAVED_ALIVES blocks, // as this makes the save/load code simpler actorInfo = (ACTORINFO *)calloc(MAX_SAVED_ALIVES, sizeof(ACTORINFO)); // make sure memory allocated if (actorInfo == NULL) { error("Cannot allocate memory for actors"); } } else { // Check the total number of actors is still the same assert(num == NumActors); memset(actorInfo, 0, MAX_SAVED_ALIVES * sizeof(ACTORINFO)); } // All actors start off alive. while (num--) actorInfo[num].alive = true; } void FreeActors() { if (actorInfo) { free(actorInfo); actorInfo = NULL; } } /** * Called from dec_lead(), i.e. normally once at start of master script. * @param leadID Lead Id */ void setleadid(int leadID) { LeadActorId = leadID; actorInfo[leadID-1].mtype = ACT_MASK; } /** * No comment. */ int LeadId(void) { return LeadActorId; } struct ATP_INIT { int id; // Actor number USER_EVENT event; // Event BUTEVENT bev; // Causal mouse event }; /** * Runs actor's glitter code. */ static void ActorTinselProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; INT_CONTEXT *pic; CORO_END_CONTEXT(_ctx); // get the stuff copied to process when it was created ATP_INIT *atp = (ATP_INIT *)param; CORO_BEGIN_CODE(_ctx); CORO_INVOKE_1(AllowDclick, atp->bev); // May kill us if single click // Run the Glitter code assert(actorInfo[atp->id - 1].actorCode); // no code to run _ctx->pic = InitInterpretContext(GS_ACTOR, actorInfo[atp->id - 1].actorCode, atp->event, NOPOLY, atp->id, NULL); CORO_INVOKE_1(Interpret, _ctx->pic); // If it gets here, actor's code has run to completion actorInfo[atp->id - 1].completed = true; CORO_END_CODE; } //--------------------------------------------------------------------------- struct RATP_INIT { INT_CONTEXT *pic; int id; // Actor number }; static void ActorRestoredProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; INT_CONTEXT *pic; CORO_END_CONTEXT(_ctx); // get the stuff copied to process when it was created RATP_INIT *r = (RATP_INIT *)param; CORO_BEGIN_CODE(_ctx); _ctx->pic = RestoreInterpretContext(r->pic); CORO_INVOKE_1(Interpret, _ctx->pic); // If it gets here, actor's code has run to completion actorInfo[r->id - 1].completed = true; CORO_END_CODE; } void RestoreActorProcess(int id, INT_CONTEXT *pic) { RATP_INIT r = { pic, id }; g_scheduler->createProcess(PID_TCODE, ActorRestoredProcess, &r, sizeof(r)); } /** * Starts up process to runs actor's glitter code. * @param ano Actor Id * @param event Event structure * @param be ButEvent */ void actorEvent(int ano, USER_EVENT event, BUTEVENT be) { ATP_INIT atp; // Only if there is Glitter code associated with this actor. if (actorInfo[ano - 1].actorCode) { atp.id = ano; atp.event = event; atp.bev = be; g_scheduler->createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp)); } } /** * Called at the start of each scene for each actor with a code block. * @param as Actor structure * @param bRunScript Flag for whether to run actor's script for the scene */ void StartActor(const ACTOR_STRUC *as, bool bRunScript) { SCNHANDLE hActorId = FROM_LE_32(as->hActorId); // Zero-out many things actorInfo[hActorId - 1].hidden = false; actorInfo[hActorId - 1].completed = false; actorInfo[hActorId - 1].x = 0; actorInfo[hActorId - 1].y = 0; actorInfo[hActorId - 1].presReel = NULL; actorInfo[hActorId - 1].presFilm = 0; actorInfo[hActorId - 1].presObj = NULL; // Store current scene's parameters for this actor actorInfo[hActorId - 1].mtype = FROM_LE_32(as->masking); actorInfo[hActorId - 1].actorCode = FROM_LE_32(as->hActorCode); // Run actor's script for this scene if (bRunScript) { if (bActorsOn) actorInfo[hActorId - 1].alive = true; if (actorInfo[hActorId - 1].alive && FROM_LE_32(as->hActorCode)) actorEvent(hActorId, STARTUP, BE_NONE); } } /** * Called at the start of each scene. Start each actor with a code block. * @param ah Scene handle * @param numActors Number of actors * @param bRunScript Flag for whether to run actor scene scripts */ void StartActors(SCNHANDLE ah, int numActors, bool bRunScript) { int i; // Only actors with code blocks got (x, y) re-initialised, so... for (i = 0; i < NumActors; i++) { actorInfo[i].x = actorInfo[i].y = 0; actorInfo[i].mtype = 0; } const ACTOR_STRUC *as = (const ACTOR_STRUC *)LockMem(ah); for (i = 0; i < numActors; i++, as++) { StartActor(as, bRunScript); } } /** * Called between scenes, zeroises all actors. */ void DropActors(void) { for (int i = 0; i < NumActors; i++) { actorInfo[i].actorCode = 0; // No script actorInfo[i].presReel = NULL; // No reel running actorInfo[i].presFilm = 0; // ditto actorInfo[i].presObj = NULL; // No object actorInfo[i].x = 0; // No position actorInfo[i].y = 0; // ditto actorInfo[i].talkFilm = 0; actorInfo[i].latestFilm = 0; actorInfo[i].playFilm = 0; actorInfo[i].talking = false; } } /** * Kill actors. * @param ano Actor Id */ void DisableActor(int ano) { PMACTOR pActor; assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].alive = false; // Record as dead actorInfo[ano - 1].x = actorInfo[ano - 1].y = 0; // Kill off moving actor properly pActor = GetMover(ano); if (pActor) KillMActor(pActor); } /** * Enable actors. * @param ano Actor Id */ void EnableActor(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number // Re-incarnate only if it's dead, or it's script ran to completion if (!actorInfo[ano - 1].alive || actorInfo[ano - 1].completed) { actorInfo[ano - 1].alive = true; actorInfo[ano - 1].hidden = false; actorInfo[ano - 1].completed = false; // Re-run actor's script for this scene if (actorInfo[ano-1].actorCode) actorEvent(ano, STARTUP, BE_NONE); } } /** * Returns the aliveness (to coin a word) of the actor. * @param ano Actor Id */ bool actorAlive(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].alive; } /** * Define an actor as being tagged. * @param ano Actor Id * @param tagtext Scene handle * @param tp tType */ void Tag_Actor(int ano, SCNHANDLE tagtext, int tp) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano-1].tagged = true; actorInfo[ano-1].hTag = tagtext; actorInfo[ano-1].tType = tp; } /** * Undefine an actor as being tagged. * @param ano Actor Id * @param tagtext Scene handle * @param tp tType */ void UnTagActor(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano-1].tagged = false; } /** * Redefine an actor as being tagged. * @param ano Actor Id * @param tagtext Scene handle * @param tp tType */ void ReTagActor(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number if (actorInfo[ano-1].hTag) actorInfo[ano-1].tagged = true; } /** * Returns a tagged actor's tag type. e.g. TAG_Q1TO3 * @param ano Actor Id */ int TagType(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano-1].tType; } /** * Returns handle to tagged actor's tag text * @param ano Actor Id */ SCNHANDLE GetActorTag(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].hTag; } /** * Called from TagProcess, FirstTaggedActor() resets the index, then * NextTagged Actor is repeatedly called until the caller gets fed up * or there are no more tagged actors to look at. */ void FirstTaggedActor(void) { ti = 0; } /** * Called from TagProcess, FirstTaggedActor() resets the index, then * NextTagged Actor is repeatedly called until the caller gets fed up * or there are no more tagged actors to look at. */ int NextTaggedActor(void) { PMACTOR pActor; bool hid; do { if (actorInfo[ti].tagged) { pActor = GetMover(ti+1); if (pActor) hid = getMActorHideState(pActor); else hid = actorInfo[ti].hidden; if (!hid) { return ++ti; } } } while (++ti < NumActors); return 0; } /** * Returns the masking type of the actor. * @param ano Actor Id */ int32 actorMaskType(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].mtype; } /** * Store/Return the currently stored co-ordinates of the actor. * Delegate the task for moving actors. * @param ano Actor Id * @param x X position * @param y Y position */ void storeActorPos(int ano, int x, int y) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].x = x; actorInfo[ano - 1].y = y; } void storeActorSteps(int ano, int steps) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].steps = steps; } int getActorSteps(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].steps; } void storeActorZpos(int ano, int z) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].z = z; } void GetActorPos(int ano, int *x, int *y) { PMACTOR pActor; assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor pActor = GetMover(ano); if (pActor) GetMActorPosition(pActor, x, y); else { *x = actorInfo[ano - 1].x; *y = actorInfo[ano - 1].y; } } /** * Returns the position of the mid-top of the actor. * Delegate the task for moving actors. * @param ano Actor Id * @param x Output x * @param y Output y */ void GetActorMidTop(int ano, int *x, int *y) { // Not used in JAPAN version PMACTOR pActor; assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor pActor = GetMover(ano); if (pActor) GetMActorMidTopPosition(pActor, x, y); else if (actorInfo[ano - 1].presObj) { *x = (MultiLeftmost(actorInfo[ano - 1].presObj) + MultiRightmost(actorInfo[ano - 1].presObj)) / 2; *y = MultiHighest(actorInfo[ano - 1].presObj); } else GetActorPos(ano, x, y); // The best we can do! } /** * Return the appropriate co-ordinate of the actor. * @param ano Actor Id */ int GetActorLeft(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number if (!actorInfo[ano - 1].presObj) return 0; return MultiLeftmost(actorInfo[ano - 1].presObj); } /** * Return the appropriate co-ordinate of the actor. * @param ano Actor Id */ int GetActorRight(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number if (!actorInfo[ano - 1].presObj) return 0; return MultiRightmost(actorInfo[ano - 1].presObj); } /** * Return the appropriate co-ordinate of the actor. * @param ano Actor Id */ int GetActorTop(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number if (!actorInfo[ano - 1].presObj) return 0; return MultiHighest(actorInfo[ano - 1].presObj); } /** * Return the appropriate co-ordinate of the actor. */ int GetActorBottom(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number if (!actorInfo[ano - 1].presObj) return 0; return MultiLowest(actorInfo[ano - 1].presObj); } /** * Set actor hidden status to true. * For a moving actor, actually hide it. * @param ano Actor Id */ void HideActor(int ano) { PMACTOR pActor; assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor // Get moving actor involved pActor = GetMover(ano); if (pActor) hideMActor(pActor, 0); else actorInfo[ano - 1].hidden = true; } /** * Hide an actor if it's a moving actor. * @param ano Actor Id * @param sf sf */ bool HideMovingActor(int ano, int sf) { PMACTOR pActor; assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor // Get moving actor involved pActor = GetMover(ano); if (pActor) { hideMActor(pActor, sf); return true; } else { if (actorInfo[ano - 1].presObj != NULL) MultiHideObject(actorInfo[ano - 1].presObj); // Hidee object return false; } } /** * Unhide an actor if it's a moving actor. * @param ano Actor Id */ void unHideMovingActor(int ano) { PMACTOR pActor; assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor // Get moving actor involved pActor = GetMover(ano); assert(pActor); // not a moving actor unhideMActor(pActor); } /** * Called after a moving actor had been replaced by an splay(). * Moves the actor to where the splay() left it, and continues the * actor's walk (if any) from the new co-ordinates. */ void restoreMovement(int ano) { PMACTOR pActor; assert(ano > 0 && ano <= NumActors); // illegal actor number // Get moving actor involved pActor = GetMover(ano); assert(pActor); // not a moving actor if (pActor->objx == actorInfo[ano - 1].x && pActor->objy == actorInfo[ano - 1].y) return; pActor->objx = actorInfo[ano - 1].x; pActor->objy = actorInfo[ano - 1].y; if (pActor->actorObj) SSetActorDest(pActor); } /** * More properly should be called: * 'store_actor_reel_and/or_film_and/or_object()' */ void storeActorReel(int ano, const FREEL *reel, SCNHANDLE film, OBJECT *pobj, int reelnum, int x, int y) { PMACTOR pActor; assert(ano > 0 && ano <= NumActors); // illegal actor number pActor = GetMover(ano); // Only store the reel and film for a moving actor if NOT called from MActorProcess() // (MActorProcess() calls with reel=film=NULL, pobj not NULL) if (!pActor || !(reel == NULL && film == 0 && pobj != NULL)) { actorInfo[ano - 1].presReel = reel; // Store reel actorInfo[ano - 1].presRnum = reelnum; // Store reel number actorInfo[ano - 1].presFilm = film; // Store film actorInfo[ano - 1].presX = x; actorInfo[ano - 1].presY = y; } // Only store the object for a moving actor if called from MActorProcess() if (!pActor) { actorInfo[ano - 1].presObj = pobj; // Store object } else if (reel == NULL && film == 0 && pobj != NULL) { actorInfo[ano - 1].presObj = pobj; // Store object } } /** * Return the present reel/film of the actor. */ const FREEL *actorReel(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].presReel; // the present reel } /***************************************************************************/ void setActorPlayFilm(int ano, SCNHANDLE film) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].playFilm = film; } SCNHANDLE getActorPlayFilm(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].playFilm; } void setActorTalkFilm(int ano, SCNHANDLE film) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].talkFilm = film; } SCNHANDLE getActorTalkFilm(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].talkFilm; } void setActorTalking(int ano, bool tf) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].talking = tf;; } bool isActorTalking(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].talking; } void setActorLatestFilm(int ano, SCNHANDLE film) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].latestFilm = film; actorInfo[ano - 1].steps = 0; } SCNHANDLE getActorLatestFilm(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].latestFilm; } /***************************************************************************/ void updateActorEsc(int ano, bool escOn, int escEvent) { assert(ano > 0 && ano <= NumActors); // illegal actor number actorInfo[ano - 1].escOn = escOn; actorInfo[ano - 1].escEv = escEvent; } bool actorEsc(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].escOn; } int actorEev(int ano) { assert(ano > 0 && ano <= NumActors); // illegal actor number return actorInfo[ano - 1].escEv; } /** * Guess what these do. */ int AsetZPos(OBJECT *pObj, int y, int32 z) { int zPos; z += z ? -1 : 0; zPos = y + (z << 10); MultiSetZPosition(pObj, zPos); return zPos; } /** * Guess what these do. */ void MAsetZPos(PMACTOR pActor, int y, int32 zFactor) { if (!pActor->aHidden) AsetZPos(pActor->actorObj, y, zFactor); } /** * Stores actor's attributes. * Currently only the speech colours. */ void storeActorAttr(int ano, int r1, int g1, int b1) { assert((ano > 0 && ano <= NumActors) || ano == -1); // illegal actor number if (r1 > MAX_INTENSITY) r1 = MAX_INTENSITY; // } Ensure if (g1 > MAX_INTENSITY) g1 = MAX_INTENSITY; // } within limits if (b1 > MAX_INTENSITY) b1 = MAX_INTENSITY; // } if (ano == -1) defaultColour = RGB(r1, g1, b1); else actorInfo[ano - 1].tColour = RGB(r1, g1, b1); } /** * Get the actor's stored speech colour. * @param ano Actor Id */ COLORREF getActorTcol(int ano) { // Not used in JAPAN version assert(ano > 0 && ano <= NumActors); // illegal actor number if (actorInfo[ano - 1].tColour) return actorInfo[ano - 1].tColour; else return defaultColour; } /** * Store relevant information pertaining to currently existing actors. */ int SaveActors(SAVED_ACTOR *sActorInfo) { int i, j; for (i = 0, j = 0; i < NumActors; i++) { if (actorInfo[i].presObj != NULL) { assert(j < MAX_SAVED_ACTORS); // Saving too many actors // sActorInfo[j].hidden = actorInfo[i].hidden; sActorInfo[j].bAlive = actorInfo[i].alive; // sActorInfo[j].x = (short)actorInfo[i].x; // sActorInfo[j].y = (short)actorInfo[i].y; sActorInfo[j].z = (short)actorInfo[i].z; // sActorInfo[j].presReel = actorInfo[i].presReel; sActorInfo[j].presRnum = (short)actorInfo[i].presRnum; sActorInfo[j].presFilm = actorInfo[i].presFilm; sActorInfo[j].presX = (short)actorInfo[i].presX; sActorInfo[j].presY = (short)actorInfo[i].presY; sActorInfo[j].actorID = (short)(i+1); j++; } } return j; } void setactorson(void) { bActorsOn = true; } void ActorsLife(int ano, bool bAlive) { assert((ano > 0 && ano <= NumActors) || ano == -1); // illegal actor number actorInfo[ano-1].alive = bAlive; } void syncAllActorsAlive(Serializer &s) { for (int i = 0; i < MAX_SAVED_ALIVES; i++) { s.syncAsByte(actorInfo[i].alive); s.syncAsByte(actorInfo[i].tagged); s.syncAsByte(actorInfo[i].tType); s.syncAsUint32LE(actorInfo[i].hTag); } } } // end of namespace Tinsel