aboutsummaryrefslogtreecommitdiff
path: root/engines/tinsel
diff options
context:
space:
mode:
authorFilippos Karapetis2008-12-01 20:35:36 +0000
committerFilippos Karapetis2008-12-01 20:35:36 +0000
commitaf945ac7881ae7e414f004bd0e99e8c3b5d76be9 (patch)
tree72e9c6fd43406e2021973b4f163ab4faa10143fb /engines/tinsel
parentf10f151ff742801e12534bb052bd89419bf906cb (diff)
downloadscummvm-rg350-af945ac7881ae7e414f004bd0e99e8c3b5d76be9.tar.gz
scummvm-rg350-af945ac7881ae7e414f004bd0e99e8c3b5d76be9.tar.bz2
scummvm-rg350-af945ac7881ae7e414f004bd0e99e8c3b5d76be9.zip
Merged the tinsel 2 engine with tinsel 1. Both Discworld 1 and Discworld 2 should be completable
svn-id: r35196
Diffstat (limited to 'engines/tinsel')
-rw-r--r--engines/tinsel/actors.cpp1135
-rw-r--r--engines/tinsel/actors.h114
-rw-r--r--engines/tinsel/anim.cpp104
-rw-r--r--engines/tinsel/anim.h28
-rw-r--r--engines/tinsel/background.cpp29
-rw-r--r--engines/tinsel/background.h20
-rw-r--r--engines/tinsel/bg.cpp170
-rw-r--r--engines/tinsel/bmv.cpp1272
-rw-r--r--engines/tinsel/config.cpp19
-rw-r--r--engines/tinsel/config.h6
-rw-r--r--engines/tinsel/coroutine.h28
-rw-r--r--engines/tinsel/cursor.cpp128
-rw-r--r--engines/tinsel/cursor.h3
-rw-r--r--engines/tinsel/debugger.cpp2
-rw-r--r--engines/tinsel/detection.cpp147
-rw-r--r--engines/tinsel/dialogs.cpp (renamed from engines/tinsel/inventory.cpp)3200
-rw-r--r--engines/tinsel/dialogs.h (renamed from engines/tinsel/inventory.h)66
-rw-r--r--engines/tinsel/drives.cpp129
-rw-r--r--engines/tinsel/drives.h63
-rw-r--r--engines/tinsel/dw.h23
-rw-r--r--engines/tinsel/effect.cpp31
-rw-r--r--engines/tinsel/events.cpp576
-rw-r--r--engines/tinsel/events.h96
-rw-r--r--engines/tinsel/faders.cpp87
-rw-r--r--engines/tinsel/faders.h16
-rw-r--r--engines/tinsel/font.cpp50
-rw-r--r--engines/tinsel/font.h24
-rw-r--r--engines/tinsel/graphics.cpp449
-rw-r--r--engines/tinsel/graphics.h11
-rw-r--r--engines/tinsel/handle.cpp213
-rw-r--r--engines/tinsel/handle.h17
-rw-r--r--engines/tinsel/heapmem.cpp15
-rw-r--r--engines/tinsel/mareels.cpp81
-rw-r--r--engines/tinsel/mareels.h56
-rw-r--r--engines/tinsel/module.mk5
-rw-r--r--engines/tinsel/move.cpp894
-rw-r--r--engines/tinsel/move.h15
-rw-r--r--engines/tinsel/multiobj.cpp46
-rw-r--r--engines/tinsel/multiobj.h9
-rw-r--r--engines/tinsel/music.cpp481
-rw-r--r--engines/tinsel/music.h99
-rw-r--r--engines/tinsel/object.cpp32
-rw-r--r--engines/tinsel/object.h10
-rw-r--r--engines/tinsel/palette.cpp203
-rw-r--r--engines/tinsel/palette.h40
-rw-r--r--engines/tinsel/pcode.cpp245
-rw-r--r--engines/tinsel/pcode.h38
-rw-r--r--engines/tinsel/pdisplay.cpp356
-rw-r--r--engines/tinsel/pdisplay.h37
-rw-r--r--engines/tinsel/pid.h10
-rw-r--r--engines/tinsel/play.cpp853
-rw-r--r--engines/tinsel/play.h64
-rw-r--r--engines/tinsel/polygons.cpp1212
-rw-r--r--engines/tinsel/polygons.h68
-rw-r--r--engines/tinsel/rince.cpp781
-rw-r--r--engines/tinsel/rince.h186
-rw-r--r--engines/tinsel/saveload.cpp145
-rw-r--r--engines/tinsel/savescn.cpp311
-rw-r--r--engines/tinsel/savescn.h25
-rw-r--r--engines/tinsel/scene.cpp261
-rw-r--r--engines/tinsel/scene.h26
-rw-r--r--engines/tinsel/sched.cpp513
-rw-r--r--engines/tinsel/sched.h48
-rw-r--r--engines/tinsel/scn.cpp19
-rw-r--r--engines/tinsel/scn.h50
-rw-r--r--engines/tinsel/scroll.cpp201
-rw-r--r--engines/tinsel/scroll.h25
-rw-r--r--engines/tinsel/sound.cpp290
-rw-r--r--engines/tinsel/sound.h52
-rw-r--r--engines/tinsel/strres.cpp290
-rw-r--r--engines/tinsel/strres.h33
-rw-r--r--engines/tinsel/sysvar.cpp218
-rw-r--r--engines/tinsel/sysvar.h149
-rw-r--r--engines/tinsel/text.cpp9
-rw-r--r--engines/tinsel/text.h28
-rw-r--r--engines/tinsel/timers.cpp2
-rw-r--r--engines/tinsel/timers.h2
-rw-r--r--engines/tinsel/tinlib.cpp4535
-rw-r--r--engines/tinsel/tinlib.h31
-rw-r--r--engines/tinsel/tinsel.cpp490
-rw-r--r--engines/tinsel/tinsel.h70
81 files changed, 17253 insertions, 4662 deletions
diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp
index c2f01added..c151bdac9b 100644
--- a/engines/tinsel/actors.cpp
+++ b/engines/tinsel/actors.cpp
@@ -25,19 +25,23 @@
*/
#include "tinsel/actors.h"
+#include "tinsel/background.h"
#include "tinsel/events.h"
#include "tinsel/film.h" // for FREEL
#include "tinsel/handle.h"
-#include "tinsel/inventory.h" // INV_NOICON
+#include "tinsel/dialogs.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/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/sched.h"
#include "tinsel/serializer.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/tinsel.h"
#include "tinsel/token.h"
#include "common/util.h"
@@ -51,25 +55,37 @@ namespace Tinsel {
#include "common/pack-start.h" // START STRUCT PACKING
/** actor struct - one per actor */
-struct ACTOR_STRUC {
+struct T1_ACTOR_STRUC {
int32 masking; //!< type of actor masking
SCNHANDLE hActorId; //!< handle actor ID string index
SCNHANDLE hActorCode; //!< handle to actor script
} PACKED_STRUCT;
+struct T2_ACTOR_STRUC {
+ SCNHANDLE hActorId; // handle actor ID string index
+ SCNHANDLE hTagText; // tag
+ int32 tagPortionV; // defines tag area
+ int32 tagPortionH; // defines tag area
+ SCNHANDLE hActorCode; // handle to actor script
+} PACKED_STRUCT;
+
#include "common/pack-end.h" // END STRUCT PACKING
+//----------------- LOCAL MACROS ----------------------------
+#define RANGE_CHECK(num) assert(num > 0 && num <= NumActors);
//----------------- LOCAL GLOBAL DATA --------------------
+#define MAX_REELS 6
+
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 bAlive; // TRUE == alive
+ bool bHidden; // TRUE == hidden
bool completed; // TRUE == script played out
int x, y, z;
@@ -81,27 +97,46 @@ struct ACTORINFO {
int presRnum; // the present reel number
SCNHANDLE presFilm; // the film that reel belongs to
OBJECT *presObj; // reference for position information
- int presX, presY;
+ int presPlayX, presPlayY;
bool tagged; // actor tagged?
SCNHANDLE hTag; // handle to tag text
int tType; // e.g. TAG_Q1TO3
- bool escOn;
- int escEv;
+ bool bEscOn;
+ int escEvent;
- COLORREF tColour; // Text colour
+ COLORREF textColour; // 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;
+ bool bTalking;
int steps;
+ int loopCount;
+
+ // DW2 new fields and alternates
+ int presColumns[MAX_REELS]; // the present columns
+ OBJECT *presObjs[MAX_REELS]; // reference for position information
+ int filmNum;
+};
+struct TAGACTOR {
+ // Copies of compiled data
+ int id;
+ SCNHANDLE hTagText; // handle to tag text
+ int32 tagPortionV; // which portion is active
+ int32 tagPortionH; // which portion is active
+ SCNHANDLE hActorCode; // The actor's script
+
+ int tagFlags;
+ SCNHANDLE hOverrideTag; // Override tag.
};
+typedef TAGACTOR *PTAGACTOR;
+
-static ACTORINFO *actorInfo = 0;
+static ACTORINFO *actorInfo = NULL;
static COLORREF defaultColour = 0; // Text colour
@@ -109,6 +144,18 @@ static bool bActorsOn = false;
static int ti = 0;
+#define MAX_TAGACTORS 10
+
+static TAGACTOR taggedActors[MAX_TAGACTORS];
+
+static int numTaggedActors = 0;
+
+static uint8 *zFactors = NULL;
+
+static Z_POSITIONS zPositions[NUM_ZPOSITIONS];
+
+//-------------------- METHOD LIST -----------------------
+
/**
* Called once at start-up time, and again at restart time.
* Registers the total number of actors in the game.
@@ -122,10 +169,13 @@ void RegisterActors(int num) {
// Check we can save so many
assert(NumActors <= MAX_SAVED_ALIVES);
- // Allocate RAM for actorInfo
+ // Allocate RAM for actor structures
+
// 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));
+ if (TinselV2)
+ zFactors = (uint8 *)malloc(MAX_SAVED_ALIVES);
// make sure memory allocated
if (actorInfo == NULL) {
@@ -136,11 +186,13 @@ void RegisterActors(int num) {
assert(num == NumActors);
memset(actorInfo, 0, MAX_SAVED_ALIVES * sizeof(ACTORINFO));
+ if (TinselV2)
+ memset(zFactors, 0, MAX_SAVED_ALIVES);
}
// All actors start off alive.
while (num--)
- actorInfo[num].alive = true;
+ actorInfo[num].bAlive = true;
}
void FreeActors() {
@@ -154,7 +206,7 @@ void FreeActors() {
* Called from dec_lead(), i.e. normally once at start of master script.
* @param leadID Lead Id
*/
-void setleadid(int leadID) {
+void SetLeadId(int leadID) {
LeadActorId = leadID;
actorInfo[leadID-1].mtype = ACT_MASK;
}
@@ -162,40 +214,83 @@ void setleadid(int leadID) {
/**
* No comment.
*/
-int LeadId(void) {
+int GetLeadId(void) {
return LeadActorId;
}
+bool ActorIsGhost(int actor) {
+ return actor == SysVar(ISV_GHOST_ACTOR);
+}
+
struct ATP_INIT {
int id; // Actor number
- USER_EVENT event; // Event
- BUTEVENT bev; // Causal mouse event
+ TINSEL_EVENT event; // Event
+ PLR_EVENT bev; // Causal mouse event
+
+ PINT_CONTEXT pic;
};
/**
+ * Convert actor id to index into TaggedActors[]
+ */
+static int TaggedActorIndex(int actor) {
+ int i;
+
+ for (i = 0; i < numTaggedActors; i++) {
+ if (taggedActors[i].id == actor)
+ return i;
+ }
+
+ error("You may say to yourself \"this is not my tagged actor\"");
+}
+
+/**
* Runs actor's glitter code.
*/
static void ActorTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
+ bool bTookControl;
CORO_END_CONTEXT(_ctx);
// get the stuff copied to process when it was created
- ATP_INIT *atp = (ATP_INIT *)param;
+ const ATP_INIT *atp = (const ATP_INIT *)param;
CORO_BEGIN_CODE(_ctx);
- CORO_INVOKE_1(AllowDclick, atp->bev); // May kill us if single click
+ if (TinselV2) {
+ // Take control for CONVERSE events
+ if (atp->event == CONVERSE) {
+ _ctx->bTookControl = GetControl();
+ HideConversation(true);
+ } else
+ _ctx->bTookControl = false;
- // Run the Glitter code
- assert(actorInfo[atp->id - 1].actorCode); // no code to run
+ // Run the Glitter code
+ CORO_INVOKE_1(Interpret, atp->pic);
- _ctx->pic = InitInterpretContext(GS_ACTOR, actorInfo[atp->id - 1].actorCode, atp->event, NOPOLY, atp->id, NULL);
- CORO_INVOKE_1(Interpret, _ctx->pic);
+ // Restore conv window if applicable
+ if (atp->event == CONVERSE) {
+ // Free control if we took it
+ if (_ctx->bTookControl)
+ ControlOn();
- // If it gets here, actor's code has run to completion
- actorInfo[atp->id - 1].completed = true;
+ HideConversation(false);
+ }
+ } else {
+ 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;
}
@@ -215,7 +310,7 @@ static void ActorRestoredProcess(CORO_PARAM, const void *param) {
CORO_END_CONTEXT(_ctx);
// get the stuff copied to process when it was created
- RATP_INIT *r = (RATP_INIT *)param;
+ const RATP_INIT *r = (const RATP_INIT *)param;
CORO_BEGIN_CODE(_ctx);
@@ -240,7 +335,7 @@ void RestoreActorProcess(int id, INT_CONTEXT *pic) {
* @param event Event structure
* @param be ButEvent
*/
-void actorEvent(int ano, USER_EVENT event, BUTEVENT be) {
+void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be) {
ATP_INIT atp;
// Only if there is Glitter code associated with this actor.
@@ -248,20 +343,59 @@ void actorEvent(int ano, USER_EVENT event, BUTEVENT be) {
atp.id = ano;
atp.event = event;
atp.bev = be;
+ atp.pic = NULL;
g_scheduler->createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp));
}
}
/**
+ * Starts up process to run actor's glitter code.
+ */
+void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result) {
+ ATP_INIT atp;
+ int index;
+ CORO_BEGIN_CONTEXT;
+ PPROCESS pProc;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ index = TaggedActorIndex(ano);
+ assert(taggedActors[index].hActorCode);
+ if (result) *result = false;
+
+ atp.id = 0;
+ atp.event = tEvent;
+ atp.pic = InitInterpretContext(GS_ACTOR,
+ taggedActors[index].hActorCode,
+ tEvent,
+ NOPOLY, // No polygon
+ ano, // Actor
+ NULL, // No object
+ myEscape);
+
+ if (atp.pic != NULL) {
+ _ctx->pProc = g_scheduler->createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp));
+ AttachInterpret(atp.pic, _ctx->pProc);
+
+ if (bWait)
+ CORO_INVOKE_2(WaitInterpret,_ctx->pProc, result);
+ }
+
+ CORO_END_CODE;
+}
+
+
+/**
* 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) {
+void StartActor(const T1_ACTOR_STRUC *as, bool bRunScript) {
SCNHANDLE hActorId = FROM_LE_32(as->hActorId);
// Zero-out many things
- actorInfo[hActorId - 1].hidden = false;
+ actorInfo[hActorId - 1].bHidden = false;
actorInfo[hActorId - 1].completed = false;
actorInfo[hActorId - 1].x = 0;
actorInfo[hActorId - 1].y = 0;
@@ -276,10 +410,10 @@ void StartActor(const ACTOR_STRUC *as, bool bRunScript) {
// Run actor's script for this scene
if (bRunScript) {
if (bActorsOn)
- actorInfo[hActorId - 1].alive = true;
+ actorInfo[hActorId - 1].bAlive = true;
- if (actorInfo[hActorId - 1].alive && FROM_LE_32(as->hActorCode))
- actorEvent(hActorId, STARTUP, BE_NONE);
+ if (actorInfo[hActorId - 1].bAlive && FROM_LE_32(as->hActorCode))
+ ActorEvent(hActorId, STARTUP, PLR_NOEVENT);
}
}
@@ -289,18 +423,47 @@ void StartActor(const ACTOR_STRUC *as, bool bRunScript) {
* @param numActors Number of actors
* @param bRunScript Flag for whether to run actor scene scripts
*/
-void StartActors(SCNHANDLE ah, int numActors, bool bRunScript) {
+void StartTaggedActors(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;
+ if (TinselV2) {
+ // Clear it all out for a fresh start
+ memset(taggedActors, 0, sizeof(taggedActors));
+ numTaggedActors = numActors;
+ } else {
+ // 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);
+ if (!TinselV2) {
+ // Tinsel 1 load variation
+ const T1_ACTOR_STRUC *as = (const T1_ACTOR_STRUC *)LockMem(ah);
+ for (i = 0; i < numActors; i++, as++) {
+ StartActor(as, bRunScript);
+ }
+ } else if (numActors > 0) {
+ // Tinsel 2 load variation
+ const T2_ACTOR_STRUC *as = (T2_ACTOR_STRUC *)LockMem(ah);
+ for (i = 0; i < numActors; i++, as++) {
+ assert(as->hActorCode);
+
+ // Store current scene's parameters for this tagged actor
+ taggedActors[i].id = FROM_LE_32(as->hActorId);
+ taggedActors[i].hTagText = FROM_LE_32(as->hTagText);
+ taggedActors[i].tagPortionV = FROM_LE_32(as->tagPortionV);
+ taggedActors[i].tagPortionH = FROM_LE_32(as->tagPortionH);
+ taggedActors[i].hActorCode = FROM_LE_32(as->hActorCode);
+
+ // Run actor's script for this scene
+ if (bRunScript) {
+ // Send in reverse order - they get swapped round in the scheduler
+ ActorEvent(nullContext, taggedActors[i].id, SHOWEVENT, false, 0);
+ ActorEvent(nullContext, taggedActors[i].id, STARTUP, false, 0);
+ }
+ }
}
}
@@ -308,18 +471,34 @@ void StartActors(SCNHANDLE ah, int numActors, bool 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;
+ for (int i = 0; i < NumActors; i++) {
+ if (TinselV2) {
+ // Save text colour
+ COLORREF tColour = actorInfo[i].textColour;
+
+ memset(&actorInfo[i], 0, sizeof(ACTORINFO));
+
+ // Restor text colour
+ actorInfo[i].textColour = tColour;
+
+ // Clear extra arrays
+ memset(zFactors, 0, NumActors);
+ memset(zPositions, 0, sizeof(zPositions));
+ } else {
+ // In Tinsel v1, only certain fields get reset
+ 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].bTalking = false;
+ }
}
}
@@ -328,17 +507,17 @@ void DropActors(void) {
* @param ano Actor Id
*/
void DisableActor(int ano) {
- PMACTOR pActor;
+ PMOVER pActor;
assert(ano > 0 && ano <= NumActors); // illegal actor number
- actorInfo[ano - 1].alive = false; // Record as dead
+ actorInfo[ano - 1].bAlive = 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);
+ KillMover(pActor);
}
/**
@@ -349,14 +528,14 @@ 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;
+ if (!actorInfo[ano - 1].bAlive || actorInfo[ano - 1].completed) {
+ actorInfo[ano - 1].bAlive = true;
+ actorInfo[ano - 1].bHidden = false;
actorInfo[ano - 1].completed = false;
// Re-run actor's script for this scene
if (actorInfo[ano-1].actorCode)
- actorEvent(ano, STARTUP, BE_NONE);
+ ActorEvent(ano, STARTUP, PLR_NOEVENT);
}
}
@@ -367,7 +546,7 @@ void EnableActor(int ano) {
bool actorAlive(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- return actorInfo[ano - 1].alive;
+ return actorInfo[ano - 1].bAlive;
}
/**
@@ -444,16 +623,16 @@ void FirstTaggedActor(void) {
* or there are no more tagged actors to look at.
*/
int NextTaggedActor(void) {
- PMACTOR pActor;
+ PMOVER pActor;
bool hid;
do {
if (actorInfo[ti].tagged) {
pActor = GetMover(ti+1);
if (pActor)
- hid = getMActorHideState(pActor);
+ hid = MoverHidden(pActor);
else
- hid = actorInfo[ti].hidden;
+ hid = actorInfo[ti].bHidden;
if (!hid) {
return ++ti;
@@ -465,6 +644,41 @@ int NextTaggedActor(void) {
}
/**
+ * Called from TagProcess, NextTaggedActor() is
+ * called repeatedly until the caller gets fed up or
+ * there are no more tagged actors to look at.
+ */
+int NextTaggedActor(int previous) {
+ PMOVER pMover;
+
+ // Convert actor number to index
+ if (!previous)
+ previous = -1;
+ else
+ previous = TaggedActorIndex(previous);
+
+ while (++previous < numTaggedActors) {
+ pMover = GetMover(taggedActors[previous].id);
+
+ // No tag on lead actor while he's moving
+ if ((taggedActors[previous].id) == GetLeadId() && MoverMoving(pMover)) {
+ taggedActors[previous].tagFlags &= ~(POINTING | TAGWANTED);
+ continue;
+ }
+
+ // Not if the actor doesn't exist at the moment
+ if (pMover && !MoverIs(pMover))
+ continue;
+
+ if (!(pMover ? MoverHidden(pMover) : ActorHidden(taggedActors[previous].id))) {
+ return taggedActors[previous].id;
+ }
+ }
+
+ return 0;
+}
+
+/**
* Returns the masking type of the actor.
* @param ano Actor Id
*/
@@ -481,41 +695,88 @@ int32 actorMaskType(int ano) {
* @param x X position
* @param y Y position
*/
-void storeActorPos(int ano, int x, int y) {
+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) {
+void StoreActorSteps(int ano, int steps) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
actorInfo[ano - 1].steps = steps;
}
-int getActorSteps(int ano) {
+int GetActorSteps(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
return actorInfo[ano - 1].steps;
}
-void storeActorZpos(int ano, int z) {
+void StoreActorZpos(int ano, int z, int column) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- actorInfo[ano - 1].z = z;
+ if (!TinselV2) {
+ // Prior to Tinsel 2, only a single z value was stored
+ actorInfo[ano - 1].z = z;
+ } else {
+ // Alter existing entry, if there is one
+ for (int i = 0; i < NUM_ZPOSITIONS; i++) {
+ if (zPositions[i].actor == ano && zPositions[i].column == column) {
+ zPositions[i].z = z;
+ return;
+ }
+ }
+
+ // No existing entry found, so find an empty slot
+ for (int i = 0; i < NUM_ZPOSITIONS; i++) {
+ if (zPositions[i].actor == 0) {
+ zPositions[i].actor = (short)ano;
+ zPositions[i].column = (short)column;
+ zPositions[i].z = z;
+ return;
+ }
+ }
+
+ error("NUM_ZPOSITIONS exceeded");
+ }
}
+int GetActorZpos(int ano, int column) {
+ RANGE_CHECK(ano);
+
+ // Find entry, there should be one
+ for (int i = 0; i < NUM_ZPOSITIONS; i++) {
+ if (zPositions[i].actor == ano && zPositions[i].column == column) {
+ return zPositions[i].z;
+ }
+ }
+
+ return 1000; // Nominal value
+}
+
+void IncLoopCount(int ano) {
+ RANGE_CHECK(ano);
+
+ actorInfo[ano - 1].loopCount++;
+}
+
+int GetLoopCount(int ano) {
+ RANGE_CHECK(ano);
+
+ return actorInfo[ano - 1].loopCount;
+}
void GetActorPos(int ano, int *x, int *y) {
- PMACTOR pActor;
+ PMOVER pActor;
assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor
pActor = GetMover(ano);
if (pActor)
- GetMActorPosition(pActor, x, y);
+ GetMoverPosition(pActor, x, y);
else {
*x = actorInfo[ano - 1].x;
*y = actorInfo[ano - 1].y;
@@ -531,15 +792,18 @@ void GetActorPos(int ano, int *x, int *y) {
*/
void GetActorMidTop(int ano, int *x, int *y) {
// Not used in JAPAN version
- PMACTOR pActor;
+ PMOVER 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) {
+ GetMoverMidTop(pActor, x, y);
+ else if (TinselV2) {
+ *x = (GetActorLeft(ano) + GetActorRight(ano)) / 2;
+ *y = GetActorTop(ano);
+ } else if (actorInfo[ano - 1].presObj) {
*x = (MultiLeftmost(actorInfo[ano - 1].presObj)
+ MultiRightmost(actorInfo[ano - 1].presObj)) / 2;
*y = MultiHighest(actorInfo[ano - 1].presObj);
@@ -554,10 +818,39 @@ void GetActorMidTop(int ano, int *x, int *y) {
int GetActorLeft(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- if (!actorInfo[ano - 1].presObj)
- return 0;
+ if (!TinselV2) {
+ // Tinsel 1 version
+ if (!actorInfo[ano - 1].presObj)
+ return 0;
- return MultiLeftmost(actorInfo[ano - 1].presObj);
+ return MultiLeftmost(actorInfo[ano - 1].presObj);
+ }
+
+ // Tinsel 2 version
+ PMOVER pMover = GetMover(ano);
+ int i;
+ bool bIsObj;
+ int left = 0;
+
+ if (pMover != NULL) {
+ return GetMoverLeft(pMover);
+ } else {
+ for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+ // If there's an object
+ // and it is not a blank frame for it...
+ if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano - 1].presObjs[i])) {
+ if (!bIsObj) {
+ bIsObj = true;
+ left = MultiLeftmost(actorInfo[ano - 1].presObjs[i]);
+ } else {
+ if (MultiLeftmost(actorInfo[ano - 1].presObjs[i]) < left)
+ left = MultiLeftmost(actorInfo[ano - 1].presObjs[i]);
+ }
+ }
+ }
+
+ return bIsObj ? left : 0;
+ }
}
/**
@@ -567,10 +860,38 @@ int GetActorLeft(int ano) {
int GetActorRight(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- if (!actorInfo[ano - 1].presObj)
- return 0;
+ if (!TinselV2) {
+ // Tinsel 1 version
+ if (!actorInfo[ano - 1].presObj)
+ return 0;
+
+ return MultiRightmost(actorInfo[ano - 1].presObj);
+ }
+
+ // Tinsel 2 version
+ PMOVER pMover = GetMover(ano);
+ int i;
+ bool bIsObj;
+ int right = 0;
- return MultiRightmost(actorInfo[ano - 1].presObj);
+ if (pMover != NULL) {
+ return GetMoverRight(pMover);
+ } else {
+ for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+ // If there's an object
+ // and it is not a blank frame for it...
+ if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
+ if (!bIsObj) {
+ bIsObj = true;
+ right = MultiRightmost(actorInfo[ano-1].presObjs[i]);
+ } else {
+ if (MultiRightmost(actorInfo[ano-1].presObjs[i]) > right)
+ right = MultiRightmost(actorInfo[ano-1].presObjs[i]);
+ }
+ }
+ }
+ return bIsObj ? right : 0;
+ }
}
/**
@@ -580,10 +901,39 @@ int GetActorRight(int ano) {
int GetActorTop(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- if (!actorInfo[ano - 1].presObj)
- return 0;
+ if (!TinselV2) {
+ // Tinsel 1 version
+ if (!actorInfo[ano - 1].presObj)
+ return 0;
+
+ return MultiHighest(actorInfo[ano - 1].presObj);
+ }
- return MultiHighest(actorInfo[ano - 1].presObj);
+ // Tinsel 2 version
+ PMOVER pMover = GetMover(ano);
+ int i;
+ bool bIsObj;
+ int top = 0;
+
+ if (pMover != NULL) {
+ return GetMoverTop(pMover);
+ } else {
+ for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+ // If there's an object
+ // and it is not a blank frame for it...
+ if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
+ if (!bIsObj) {
+ bIsObj = true;
+ top = MultiHighest(actorInfo[ano-1].presObjs[i]);
+ } else {
+ if (MultiHighest(actorInfo[ano-1].presObjs[i]) < top)
+ top = MultiHighest(actorInfo[ano-1].presObjs[i]);
+ }
+ }
+ }
+
+ return bIsObj ? top : 0;
+ }
}
/**
@@ -592,10 +942,65 @@ int GetActorTop(int ano) {
int GetActorBottom(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- if (!actorInfo[ano - 1].presObj)
- return 0;
+ if (!TinselV2) {
+ // Tinsel 1 version
+ if (!actorInfo[ano - 1].presObj)
+ return 0;
+
+ return MultiLowest(actorInfo[ano - 1].presObj);
+ }
+
+ // Tinsel 2 version
+ PMOVER pMover = GetMover(ano);
+ int i;
+ bool bIsObj;
+ int bottom = 0;
+
+ if (pMover != NULL) {
+ return GetMoverBottom(pMover);
+ } else {
+ for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+ // If there's an object
+ // and it is not a blank frame for it...
+ if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
+ if (!bIsObj) {
+ bIsObj = true;
+ bottom = MultiLowest(actorInfo[ano-1].presObjs[i]);
+ } else {
+ if (MultiLowest(actorInfo[ano-1].presObjs[i]) > bottom)
+ bottom = MultiLowest(actorInfo[ano-1].presObjs[i]);
+ }
+ }
+ }
+ return bIsObj ? bottom : 0;
+ }
+}
+
+/**
+ * Shows the given actor
+ */
+void ShowActor(CORO_PARAM, int ano) {
+ PMOVER pMover;
+ RANGE_CHECK(ano);
+
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
- return MultiLowest(actorInfo[ano - 1].presObj);
+ CORO_BEGIN_CODE(_ctx);
+
+ // reset hidden flag
+ actorInfo[ano - 1].bHidden = false;
+
+ // Send event to tagged actors
+ if (IsTaggedActor(ano))
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, ano, SHOWEVENT, true, 0));
+
+ // If moving actor involved, un-hide it
+ pMover = GetMover(ano);
+ if (pMover)
+ UnHideMover(pMover);
+
+ CORO_END_CODE;
}
/**
@@ -603,18 +1008,47 @@ int GetActorBottom(int ano) {
* For a moving actor, actually hide it.
* @param ano Actor Id
*/
-void HideActor(int ano) {
- PMACTOR pActor;
-
+void HideActor(CORO_PARAM, int ano) {
+ PMOVER pMover;
assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (TinselV2) {
+ actorInfo[ano - 1].bHidden = true;
+
+ // Send event to tagged actors
+ // (this is duplicated in HideMover())
+ if (IsTaggedActor(ano)) {
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, ano, HIDEEVENT, true, 0));
+
+ // It may be pointed to
+ SetActorPointedTo(ano, false);
+ SetActorTagWanted(ano, false, false, 0);
+ }
+ }
+
// Get moving actor involved
- pActor = GetMover(ano);
+ pMover = GetMover(ano);
- if (pActor)
- hideMActor(pActor, 0);
- else
- actorInfo[ano - 1].hidden = true;
+ if (pMover)
+ HideMover(pMover, 0);
+ else if (!TinselV2)
+ actorInfo[ano - 1].bHidden = true;
+
+ CORO_END_CODE;
+}
+
+/**
+ * Return actor hidden status.
+ */
+bool ActorHidden(int ano) {
+ RANGE_CHECK(ano);
+
+ return actorInfo[ano - 1].bHidden;
}
/**
@@ -623,7 +1057,7 @@ void HideActor(int ano) {
* @param sf sf
*/
bool HideMovingActor(int ano, int sf) {
- PMACTOR pActor;
+ PMOVER pActor;
assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
@@ -631,7 +1065,7 @@ bool HideMovingActor(int ano, int sf) {
pActor = GetMover(ano);
if (pActor) {
- hideMActor(pActor, sf);
+ HideMover(pActor, sf);
return true;
} else {
if (actorInfo[ano - 1].presObj != NULL)
@@ -645,7 +1079,7 @@ bool HideMovingActor(int ano, int sf) {
* @param ano Actor Id
*/
void unHideMovingActor(int ano) {
- PMACTOR pActor;
+ PMOVER pActor;
assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
@@ -654,7 +1088,7 @@ void unHideMovingActor(int ano) {
assert(pActor); // not a moving actor
- unhideMActor(pActor);
+ UnHideMover(pActor);
}
/**
@@ -663,7 +1097,7 @@ void unHideMovingActor(int ano) {
* actor's walk (if any) from the new co-ordinates.
*/
void restoreMovement(int ano) {
- PMACTOR pActor;
+ PMOVER pActor;
assert(ano > 0 && ano <= NumActors); // illegal actor number
@@ -672,11 +1106,11 @@ void restoreMovement(int ano) {
assert(pActor); // not a moving actor
- if (pActor->objx == actorInfo[ano - 1].x && pActor->objy == actorInfo[ano - 1].y)
+ 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;
+ pActor->objX = actorInfo[ano - 1].x;
+ pActor->objY = actorInfo[ano - 1].y;
if (pActor->actorObj)
SSetActorDest(pActor);
@@ -686,28 +1120,28 @@ void restoreMovement(int ano) {
* 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;
+void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y) {
+ PMOVER 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)
+ // Only store the reel and film for a moving actor if NOT called from MoverProcess()
+ // (MoverProcess() calls with reel=film=NULL, pobj not NULL)
if (!pActor
- || !(reel == NULL && film == 0 && pobj != NULL)) {
+ || !(reel == NULL && hFilm == 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;
+ actorInfo[ano - 1].presFilm = hFilm; // Store film
+ actorInfo[ano - 1].presPlayX = x;
+ actorInfo[ano - 1].presPlayY = y;
}
- // Only store the object for a moving actor if called from MActorProcess()
+ // Only store the object for a moving actor if called from MoverProcess()
if (!pActor) {
actorInfo[ano - 1].presObj = pobj; // Store object
- } else if (reel == NULL && film == 0 && pobj != NULL) {
+ } else if (reel == NULL && hFilm == 0 && pobj != NULL) {
actorInfo[ano - 1].presObj = pobj; // Store object
}
}
@@ -723,50 +1157,50 @@ const FREEL *actorReel(int ano) {
/***************************************************************************/
-void setActorPlayFilm(int ano, SCNHANDLE film) {
+void SetActorPlayFilm(int ano, SCNHANDLE hFilm) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- actorInfo[ano - 1].playFilm = film;
+ actorInfo[ano - 1].playFilm = hFilm;
}
-SCNHANDLE getActorPlayFilm(int ano) {
+SCNHANDLE GetActorPlayFilm(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
return actorInfo[ano - 1].playFilm;
}
-void setActorTalkFilm(int ano, SCNHANDLE film) {
+void SetActorTalkFilm(int ano, SCNHANDLE hFilm) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- actorInfo[ano - 1].talkFilm = film;
+ actorInfo[ano - 1].talkFilm = hFilm;
}
-SCNHANDLE getActorTalkFilm(int ano) {
+SCNHANDLE GetActorTalkFilm(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
return actorInfo[ano - 1].talkFilm;
}
-void setActorTalking(int ano, bool tf) {
+void SetActorTalking(int ano, bool tf) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- actorInfo[ano - 1].talking = tf;;
+ actorInfo[ano - 1].bTalking = tf;;
}
-bool isActorTalking(int ano) {
+bool ActorIsTalking(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- return actorInfo[ano - 1].talking;
+ return actorInfo[ano - 1].bTalking;
}
-void setActorLatestFilm(int ano, SCNHANDLE film) {
+void SetActorLatestFilm(int ano, SCNHANDLE hFilm) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- actorInfo[ano - 1].latestFilm = film;
+ actorInfo[ano - 1].latestFilm = hFilm;
actorInfo[ano - 1].steps = 0;
}
-SCNHANDLE getActorLatestFilm(int ano) {
+SCNHANDLE GetActorLatestFilm(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
return actorInfo[ano - 1].latestFilm;
@@ -774,23 +1208,36 @@ SCNHANDLE getActorLatestFilm(int ano) {
/***************************************************************************/
-void updateActorEsc(int ano, bool escOn, int escEvent) {
+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;
+ actorInfo[ano - 1].bEscOn = escOn;
+ actorInfo[ano - 1].escEvent = escEvent;
}
-bool actorEsc(int ano) {
+void UpdateActorEsc(int ano, int escEvent) {
+ RANGE_CHECK(ano);
+
+ if (escEvent) {
+ actorInfo[ano - 1].bEscOn = true;
+ actorInfo[ano - 1].escEvent = escEvent;
+ } else {
+ actorInfo[ano - 1].bEscOn = false;
+ actorInfo[ano - 1].escEvent = GetEscEvents();
+ }
+
+}
+
+bool ActorEsc(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- return actorInfo[ano - 1].escOn;
+ return actorInfo[ano - 1].bEscOn;
}
-int actorEev(int ano) {
+int ActorEev(int ano) {
assert(ano > 0 && ano <= NumActors); // illegal actor number
- return actorInfo[ano - 1].escEv;
+ return actorInfo[ano - 1].escEvent;
}
/**
@@ -801,7 +1248,7 @@ int AsetZPos(OBJECT *pObj, int y, int32 z) {
z += z ? -1 : 0;
- zPos = y + (z << 10);
+ zPos = y + (z << ZSHIFT);
MultiSetZPosition(pObj, zPos);
return zPos;
}
@@ -809,9 +1256,18 @@ int AsetZPos(OBJECT *pObj, int y, int32 z) {
/**
* Guess what these do.
*/
-void MAsetZPos(PMACTOR pActor, int y, int32 zFactor) {
- if (!pActor->aHidden)
- AsetZPos(pActor->actorObj, y, zFactor);
+void SetMoverZ(PMOVER pMover, int y, int32 zFactor) {
+ if (!pMover->bHidden) {
+ if (!TinselV2)
+ 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);
+ }
+ }
}
/**
@@ -828,51 +1284,127 @@ void storeActorAttr(int ano, int r1, int g1, int b1) {
if (ano == -1)
defaultColour = RGB(r1, g1, b1);
else
- actorInfo[ano - 1].tColour = RGB(r1, g1, b1);
+ actorInfo[ano - 1].textColour = RGB(r1, g1, b1);
+}
+
+/**
+ * Called from ActorRGB() - Stores actor's speech colour.
+ */
+
+void SetActorRGB(int ano, COLORREF colour) {
+ assert(ano >= 0 && ano <= NumActors);
+
+ if (ano)
+ actorInfo[ano - 1].textColour = colour;
+ else
+ defaultColour = colour;
}
/**
* Get the actor's stored speech colour.
* @param ano Actor Id
*/
-COLORREF getActorTcol(int ano) {
+COLORREF GetActorRGB(int ano) {
// Not used in JAPAN version
- assert(ano > 0 && ano <= NumActors); // illegal actor number
+ assert((ano >= -1) && (ano <= NumActors)); // illegal actor number
- if (actorInfo[ano - 1].tColour)
- return actorInfo[ano - 1].tColour;
- else
+ if ((ano == -1) || !actorInfo[ano - 1].textColour)
return defaultColour;
+ else
+ return actorInfo[ano - 1].textColour;
+}
+
+/**
+ * Set the actor's Z-factor
+ */
+void SetActorZfactor(int ano, uint32 zFactor) {
+ RANGE_CHECK(ano);
+
+ zFactors[ano - 1] = (uint8)zFactor;
+}
+
+uint32 GetActorZfactor(int ano) {
+ RANGE_CHECK(ano);
+
+ return zFactors[ano - 1];
}
/**
* Store relevant information pertaining to currently existing actors.
*/
int SaveActors(SAVED_ACTOR *sActorInfo) {
- int i, j;
+ int i, j, k;
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++;
+ for (k = 0; k < (TinselV2 ? MAX_REELS : 1); ++k) {
+ bool presFlag = !TinselV2 ? actorInfo[i].presObj != NULL :
+ (actorInfo[i].presObjs[k] != NULL) && !IsCdPlayHandle(actorInfo[i].presFilm);
+ if (presFlag) {
+
+ assert(j < MAX_SAVED_ACTORS); // Saving too many actors
+
+ if (!TinselV2) {
+ sActorInfo[j].bAlive = actorInfo[i].bAlive;
+ sActorInfo[j].zFactor = (short)actorInfo[i].z;
+ sActorInfo[j].presRnum = (short)actorInfo[i].presRnum;
+ }
+
+ sActorInfo[j].actorID = (short)(i+1);
+ if (TinselV2)
+ sActorInfo[j].bHidden = actorInfo[i].bHidden;
+ // sActorInfo[j].x = (short)actorInfo[i].x;
+ // sActorInfo[j].y = (short)actorInfo[i].y;
+ // sActorInfo[j].presReel = actorInfo[i].presReel;
+ sActorInfo[j].presFilm = actorInfo[i].presFilm;
+ sActorInfo[j].presPlayX = (short)actorInfo[i].presPlayX;
+ sActorInfo[j].presPlayY = (short)actorInfo[i].presPlayY;
+ j++;
+
+ break;
+ }
}
}
return j;
}
+/**
+ * Restore actor data
+ */
+void RestoreActors(int numActors, PSAVED_ACTOR sActorInfo) {
+ int i, aIndex;
+
+ for (i = 0; i < numActors; i++) {
+ aIndex = sActorInfo[i].actorID - 1;
+
+ actorInfo[aIndex].bHidden = sActorInfo[i].bHidden;
+
+ // Play the same reel.
+ if (sActorInfo[i].presFilm != 0) {
+ RestoreActorReels(sActorInfo[i].presFilm, sActorInfo[i].actorID,
+ sActorInfo[i].presPlayX, sActorInfo[i].presPlayY);
+ }
+ }
+}
+
+void SaveZpositions(void *zpp) {
+ memcpy(zpp, zPositions, sizeof(zPositions));
+}
+
+void RestoreZpositions(void *zpp) {
+ memcpy(zPositions, zpp, sizeof(zPositions));
+}
+
+void SaveActorZ(byte *saveActorZ) {
+ assert(NumActors <= MAX_SAVED_ACTOR_Z);
+
+ memcpy(saveActorZ, zFactors, NumActors);
+}
+
+void RestoreActorZ(byte *saveActorZ) {
+ memcpy(zFactors, saveActorZ, NumActors);
+}
+
void setactorson(void) {
bActorsOn = true;
}
@@ -880,18 +1412,307 @@ void setactorson(void) {
void ActorsLife(int ano, bool bAlive) {
assert((ano > 0 && ano <= NumActors) || ano == -1); // illegal actor number
- actorInfo[ano-1].alive = bAlive;
+ actorInfo[ano-1].bAlive = bAlive;
}
void syncAllActorsAlive(Serializer &s) {
for (int i = 0; i < MAX_SAVED_ALIVES; i++) {
- s.syncAsByte(actorInfo[i].alive);
+ s.syncAsByte(actorInfo[i].bAlive);
s.syncAsByte(actorInfo[i].tagged);
s.syncAsByte(actorInfo[i].tType);
s.syncAsUint32LE(actorInfo[i].hTag);
}
}
+/**
+ * Called from EndActor()
+ */
+void dwEndActor(int ano) {
+ int i;
+
+ RANGE_CHECK(ano);
+
+ // Make play.c think it's been replaced
+// The following line may have been indirectly making text go away!
+// actorInfo[ano - 1].presFilm = NULL;
+// but things were returning after a cut scene.
+// so re-instate it and de-register the object
+ actorInfo[ano - 1].presFilm = 0;
+ actorInfo[ano-1].filmNum++;
+
+ for (i = 0; i < MAX_REELS; i++) {
+ // It may take a frame to remove this, so make it invisible
+ if (actorInfo[ano-1].presObjs[i] != NULL) {
+ MultiHideObject(actorInfo[ano-1].presObjs[i]);
+ actorInfo[ano-1].presObjs[i] = NULL;
+ }
+ }
+}
+
+
+/**
+ * Returns a tagged actor's tag portion.
+ */
+void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right) {
+ // Convert actor number to index
+ ano = TaggedActorIndex(ano);
+
+ *top = taggedActors[ano].tagPortionV >> 16;
+ *bottom = taggedActors[ano].tagPortionV & 0xffff;
+ *left = taggedActors[ano].tagPortionH >> 16;
+ *right = taggedActors[ano].tagPortionH & 0xffff;
+
+ // ensure validity
+ assert(*top >= 1 && *top <= 8);
+ assert(*bottom >= *top && *bottom <= 8);
+ assert(*left >= 1 && *left <= 8);
+ assert(*right >= *left && *right <= 8);
+}
+
+/**
+ * Returns handle to tagged actor's tag text.
+ */
+SCNHANDLE GetActorTagHandle(int ano) {
+ // Convert actor number to index
+ ano = TaggedActorIndex(ano);
+
+ return taggedActors[ano].hOverrideTag ?
+ taggedActors[ano].hOverrideTag : taggedActors[ano].hTagText;
+}
+
+void SetActorPointedTo(int actor, bool bPointedTo) {
+ // Convert actor number to index
+ actor = TaggedActorIndex(actor);
+
+ if (bPointedTo)
+ taggedActors[actor].tagFlags |= POINTING;
+ else
+ taggedActors[actor].tagFlags &= ~POINTING;
+}
+
+bool ActorIsPointedTo(int actor) {
+ // Convert actor number to index
+ actor = TaggedActorIndex(actor);
+
+ return (taggedActors[actor].tagFlags & POINTING);
+}
+
+void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag) {
+ // Convert actor number to index
+ actor = TaggedActorIndex(actor);
+
+ if (bTagWanted) {
+ taggedActors[actor].tagFlags |= TAGWANTED;
+ taggedActors[actor].hOverrideTag = hOverrideTag;
+ } else {
+ taggedActors[actor].tagFlags &= ~TAGWANTED;
+ taggedActors[actor].hOverrideTag = 0;
+ }
+
+ if (bCursor)
+ taggedActors[actor].tagFlags |= FOLLOWCURSOR;
+ else
+ taggedActors[actor].tagFlags &= ~FOLLOWCURSOR;
+}
+
+bool ActorTagIsWanted(int actor) {
+ // Convert actor number to index
+ actor = TaggedActorIndex(actor);
+
+ return (taggedActors[actor].tagFlags & TAGWANTED);
+}
+
+/**
+ * Given cursor position and an actor number, ascertains
+ * whether the cursor is within the actor's tag area.
+ * Returns True for a positive result, False for negative.
+ */
+bool InHotSpot(int ano, int curX, int curY) {
+ int aTop, aBot; // Top and bottom limits }
+ int aHeight; // Height } of active area
+ int aLeft, aRight; // Left and right }
+ int aWidth; // Width }
+ unsigned topEighth, botEighth, leftEighth, rightEighth;
+
+ // First check if within broad range
+ if (curX < (aLeft = GetActorLeft(ano)) // too far left
+ || curX > (aRight = GetActorRight(ano)) // too far right
+ || curY < (aTop = GetActorTop(ano)) // too high
+ || curY > (aBot = GetActorBottom(ano)) ) // too low
+ return false;
+
+ GetActorTagPortion(ano, &topEighth, &botEighth, &leftEighth, &rightEighth);
+
+ aWidth = aRight - aLeft;
+ aLeft += ((leftEighth - 1)*aWidth)/8;
+ aRight -= ((8 - rightEighth)*aWidth)/8;
+
+ // check if within x-range
+ if (curX < aLeft || curX > aRight)
+ return false;
+
+ aHeight = aBot - aTop;
+ aTop += ((topEighth - 1)*aHeight)/8;
+ aBot -= ((8 - botEighth)*aHeight)/8;
+
+ // check if within y-range
+ if (curY < aTop || curY > aBot)
+ return false;
+
+ return true;
+}
+
+/**
+ * Front Tagged Actor
+ */
+int FrontTaggedActor(void) {
+ int i;
+
+ for (i = 0; i < numTaggedActors; i++) {
+ if (taggedActors[i].tagFlags & POINTING)
+ return taggedActors[i].id;
+ }
+ return 0;
+}
+
+/**
+ * GetActorTagPos
+ */
+void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute) {
+ unsigned topEighth, botEighth;
+ int aTop; // Top and bottom limits }
+ int aHeight; // Height } of active area
+ int Loffset, Toffset;
+
+ GetActorTagPortion(actor, &topEighth, &botEighth, (unsigned *)&Loffset, (unsigned *)&Toffset);
+
+ aTop = GetActorTop(actor);
+ aHeight = GetActorBottom(actor) - aTop;
+ aTop += ((topEighth - 1) * aHeight) / 8;
+
+ *pTagX = ((GetActorLeft(actor) + GetActorRight(actor)) / 2);
+ *pTagY = aTop;
+
+ if (!bAbsolute) {
+ PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+ *pTagX -= Loffset;
+ *pTagY -= Toffset;
+ }
+}
+
+/**
+ * Is Tagged Actor
+ */
+bool IsTaggedActor(int actor) {
+ int i;
+
+ for (i = 0; i < numTaggedActors; i++) {
+ if (taggedActors[i].id == actor)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * StoreActorPresFilm
+ */
+void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y) {
+ int i;
+
+ RANGE_CHECK(ano);
+
+ actorInfo[ano-1].presFilm = hFilm;
+ actorInfo[ano-1].presPlayX = x;
+ actorInfo[ano-1].presPlayY = y;
+ actorInfo[ano-1].filmNum++;
+
+ for (i = 0; i < MAX_REELS; i++) {
+ // It may take a frame to remove this, so make it invisible
+ if (actorInfo[ano - 1].presObjs[i] != NULL)
+ MultiHideObject(actorInfo[ano - 1].presObjs[i]);
+
+ actorInfo[ano - 1].presColumns[i] = -1;
+ actorInfo[ano - 1].presObjs[i] = NULL;
+ }
+}
+
+/**
+ * GetActorPresFilm
+ */
+SCNHANDLE GetActorPresFilm(int ano) {
+ RANGE_CHECK(ano);
+
+ return actorInfo[ano - 1].presFilm;
+}
+
+
+/**
+ * GetActorFilmNumber
+ */
+int GetActorFilmNumber(int ano) {
+ RANGE_CHECK(ano);
+
+ return actorInfo[ano - 1].filmNum;
+}
+
+/**
+ * More properly should be called:
+ * 'StoreActorReelAndObject()'
+ */
+void StoreActorReel(int actor, int column, OBJECT *pObj) {
+ RANGE_CHECK(actor);
+ int i;
+
+ for (i = 0; i < MAX_REELS; i++) {
+ if (actorInfo[actor-1].presColumns[i] == -1) {
+ // Store reel and object
+ actorInfo[actor - 1].presColumns[i] = column;
+ actorInfo[actor - 1].presObjs[i] = pObj;
+ break;
+ }
+ }
+
+ assert(i < MAX_REELS);
+}
+
+/**
+ * NotPlayingReel
+ */
+void NotPlayingReel(int actor, int filmNumber, int column) {
+ int i;
+
+ RANGE_CHECK(actor);
+
+ if (actorInfo[actor-1].filmNum != filmNumber)
+ return;
+
+ // De-register this reel
+ for (i = 0; i < MAX_REELS; i++) {
+ if (actorInfo[actor-1].presColumns[i] == column) {
+ actorInfo[actor-1].presObjs[i] = NULL;
+ actorInfo[actor-1].presColumns[i] = -1;
+ break;
+ }
+ }
+
+ // De-register the film if this was the last reel
+ for (i = 0; i < MAX_REELS; i++) {
+ if (actorInfo[actor-1].presColumns[i] != -1)
+ break;
+ }
+ if (i == MAX_REELS)
+ actorInfo[actor-1].presFilm = 0;
+}
+
+bool ActorReelPlaying(int actor, int column) {
+ RANGE_CHECK(actor);
+
+ for (int i = 0; i < MAX_REELS; i++) {
+ if (actorInfo[actor - 1].presColumns[i] == column)
+ return true;
+ }
+ return false;
+}
} // end of namespace Tinsel
diff --git a/engines/tinsel/actors.h b/engines/tinsel/actors.h
index 91f54519d5..03fd0151b4 100644
--- a/engines/tinsel/actors.h
+++ b/engines/tinsel/actors.h
@@ -29,24 +29,30 @@
#include "tinsel/dw.h" // for SCNHANDLE
-#include "tinsel/events.h" // for USER_EVENT
+#include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/palette.h" // for COLORREF
namespace Tinsel {
struct FREEL;
struct INT_CONTEXT;
-struct MACTOR;
+struct MOVER;
struct OBJECT;
+#define ACTORTAG_KEY 0x1000000
+
+#define OTH_RELATEDACTOR 0x00000fff
+#define OTH_RELATIVE 0x00001000
+#define OTH_ABSOLUTE 0x00002000
/*----------------------------------------------------------------------*/
void RegisterActors(int num);
void FreeActors(void);
-void setleadid(int rid);
-int LeadId(void);
-void StartActors(SCNHANDLE ah, int numActors, bool bRunScript);
+void SetLeadId(int rid);
+int GetLeadId(void);
+bool ActorIsGhost(int actor);
+void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript);
void DropActors(void); // No actor reels running
void DisableActor(int actor);
void EnableActor(int actor);
@@ -63,60 +69,110 @@ int GetActorLeft(int ano);
int GetActorRight(int ano);
int GetActorTop(int ano);
int GetActorBottom(int ano);
-void HideActor(int ano);
+void ShowActor(CORO_PARAM, int ano);
+void HideActor(CORO_PARAM, int ano);
+bool ActorHidden(int ano);
bool HideMovingActor(int id, int sf);
void unHideMovingActor(int id);
void restoreMovement(int id);
-void storeActorReel(int ano, const FREEL *reel, SCNHANDLE film, OBJECT *pobj, int reelnum, int x, int y);
+void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y);
const FREEL *actorReel(int ano);
SCNHANDLE actorFilm(int ano);
-void setActorPlayFilm(int ano, SCNHANDLE film);
-SCNHANDLE getActorPlayFilm(int ano);
-void setActorTalkFilm(int ano, SCNHANDLE film);
-SCNHANDLE getActorTalkFilm(int ano);
-void setActorTalking(int ano, bool tf);
-bool isActorTalking(int ano);
-void setActorLatestFilm(int ano, SCNHANDLE film);
-SCNHANDLE getActorLatestFilm(int ano);
-
-void updateActorEsc(int ano, bool escOn, int escEv);
-bool actorEsc(int ano);
-int actorEev(int ano);
-void storeActorPos(int ano, int x, int y);
-void storeActorSteps(int ano, int steps);
-int getActorSteps(int ano);
-void storeActorZpos(int ano, int z);
+void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorPlayFilm(int ano);
+void SetActorTalkFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorTalkFilm(int ano);
+void SetActorTalking(int ano, bool tf);
+bool ActorIsTalking(int ano);
+void SetActorLatestFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorLatestFilm(int ano);
+
+void UpdateActorEsc(int ano, bool escOn, int escEvent);
+void UpdateActorEsc(int ano, int escEvent);
+bool ActorEsc(int ano);
+int ActorEev(int ano);
+void StoreActorPos(int ano, int x, int y);
+void StoreActorSteps(int ano, int steps);
+int GetActorSteps(int ano);
+void StoreActorZpos(int ano, int z, int column = -1);
+int GetActorZpos(int ano, int column);
+void IncLoopCount(int ano);
+int GetLoopCount(int ano);
SCNHANDLE GetActorTag(int ano);
void FirstTaggedActor(void);
int NextTaggedActor(void);
+int NextTaggedActor(int previous);
int AsetZPos(OBJECT *pObj, int y, int32 zFactor);
-void MAsetZPos(MACTOR *pActor, int y, int32 zFactor);
-void actorEvent(int ano, USER_EVENT event, BUTEVENT be);
+void SetMoverZ(MOVER *pMover, int y, int32 zFactor);
+void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be);
void storeActorAttr(int ano, int r1, int g1, int b1);
-COLORREF getActorTcol(int ano);
+COLORREF GetActorRGB(int ano);
+void SetActorRGB(int ano, COLORREF colour);
+void SetActorZfactor(int ano, uint32 zFactor);
+uint32 GetActorZfactor(int ano);
void setactorson(void);
void ActorsLife(int id, bool bAlive);
+void dwEndActor(int ano);
+
+void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
+
+void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right);
+SCNHANDLE GetActorTagHandle(int ano);
+void SetActorPointedTo(int actor, bool bPointedTo);
+bool ActorIsPointedTo(int actor);
+void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
+bool ActorTagIsWanted(int actor);
+bool InHotSpot(int ano, int curX, int curY);
+int FrontTaggedActor(void);
+void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute);
+bool IsTaggedActor(int actor);
+void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y);
+SCNHANDLE GetActorPresFilm(int ano);
+int GetActorFilmNumber(int ano);
+void StoreActorReel(int actor, int column, OBJECT *pObj);
+void NotPlayingReel(int actor, int filmNumber, int column);
+bool ActorReelPlaying(int actor, int column);
+void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorPlayFilm(int ano);
+
/*----------------------------------------------------------------------*/
struct SAVED_ACTOR {
short actorID;
- short z;
+ short zFactor;
bool bAlive;
+ bool bHidden;
SCNHANDLE presFilm; //!< the film that reel belongs to
short presRnum; //!< the present reel number
- short presX, presY;
+ short presPlayX, presPlayY;
+};
+typedef SAVED_ACTOR *PSAVED_ACTOR;
+
+#define NUM_ZPOSITIONS 200 // Reasonable-sounding number
+
+struct Z_POSITIONS {
+ short actor;
+ short column;
+ int z;
};
int SaveActors(SAVED_ACTOR *sActorInfo);
-
void RestoreActorProcess(int id, INT_CONTEXT *pic);
+int SaveActors(PSAVED_ACTOR sActorInfo);
+void RestoreActors(int numActors, PSAVED_ACTOR sActorInfo);
+
+void SaveZpositions(void *zpp);
+void RestoreZpositions(void *zpp);
+
+void SaveActorZ(byte *saveActorZ);
+void RestoreActorZ(byte *saveActorZ);
/*----------------------------------------------------------------------*/
diff --git a/engines/tinsel/anim.cpp b/engines/tinsel/anim.cpp
index 95d834d88a..055e0bba94 100644
--- a/engines/tinsel/anim.cpp
+++ b/engines/tinsel/anim.cpp
@@ -29,32 +29,12 @@
#include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/sched.h"
+#include "tinsel/tinsel.h"
#include "common/util.h"
namespace Tinsel {
-/** Animation script commands */
-enum {
- ANI_END = 0, //!< end of animation script
- ANI_JUMP = 1, //!< animation script jump
- ANI_HFLIP = 2, //!< flip animated object horizontally
- ANI_VFLIP = 3, //!< flip animated object vertically
- ANI_HVFLIP = 4, //!< flip animated object in both directions
- ANI_ADJUSTX = 5, //!< adjust animated object x animation point
- ANI_ADJUSTY = 6, //!< adjust animated object y animation point
- ANI_ADJUSTXY = 7, //!< adjust animated object x & y animation points
- ANI_NOSLEEP = 8, //!< do not sleep for this frame
- ANI_CALL = 9, //!< call routine
- ANI_HIDE = 10 //!< hide animated object
-};
-
-/** animation script command possibilities */
-union ANI_SCRIPT {
- int32 op; //!< treat as an opcode or operand
- uint32 hFrame; //!< treat as a animation frame handle
-};
-
/**
* Advance to next frame routine.
* @param pAnim Animation data structure
@@ -64,6 +44,9 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
while (1) { // repeat until a real image
+ debugC(DEBUG_DETAILED, kTinselDebugAnimations,
+ "DoNextFrame %ph index=%d, op=%xh", (byte *)pAnim, pAnim->scriptIndex,
+ FROM_LE_32(pAni[pAnim->scriptIndex].op));
switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
case ANI_END: // end of animation script
@@ -104,7 +87,6 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
// go fetch a real image
break;
-
case ANI_HVFLIP: // flip animated object in both directions
// next opcode
@@ -230,6 +212,12 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
OBJECT *pObj; // multi-object list iterator
+ debugC(DEBUG_DETAILED, kTinselDebugAnimations,
+ "InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph",
+ !pAniObj ? 0 : fracToInt(pAniObj->xPos),
+ !pAniObj ? 0 : fracToInt(pAniObj->yPos),
+ !pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (byte *)pAnim);
+
pAnim->aniDelta = 1; // will animate on next call to NextAnimRate
pAnim->pObject = pAniObj; // set object to animate
pAnim->hScript = hNewScript; // set animation script
@@ -254,9 +242,13 @@ SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
// re-init animation delta counter
pAnim->aniDelta = pAnim->aniRate;
- // move to next frame
- while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
- ;
+ if (TinselV2)
+ state = DoNextFrame(pAnim);
+ else {
+ // move to next frame
+ while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
+ ;
+ }
return state;
}
@@ -274,7 +266,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
// get a pointer to the script
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
- if (numFrames <= 0)
+ if (!TinselV2 && (numFrames <= 0))
// do nothing
return;
@@ -282,9 +274,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
case ANI_END: // end of animation script
- // going off the end is probably a error
- error("SkipFrames(): formally 'assert(0)!'");
- break;
+ // going off the end is probably a error, but only in Tinsel 1
+ if (!TinselV2)
+ error("SkipFrames(): formally 'assert(0)!'");
+ return;
case ANI_JUMP: // do animation jump
@@ -293,6 +286,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
// jump to new frame position
pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op);
+
+ if (TinselV2)
+ // Done if skip to jump
+ return;
break;
case ANI_HFLIP: // flip animated object horizontally
@@ -383,7 +380,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
default: // must be an actual animation frame handle
// one less frame
- if (numFrames-- > 0) {
+ if (numFrames == 0)
+ return;
+
+ if (numFrames == -1 || numFrames-- > 0) {
// next opcode
pAnim->scriptIndex++;
} else {
@@ -401,4 +401,48 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
}
}
+/**
+ * About to jump or end
+ * @param pAnim Animation data structure
+ */
+bool AboutToJumpOrEnd(PANIM pAnim) {
+ if (pAnim->aniDelta == 1) {
+ // get a pointer to the script
+ ANI_SCRIPT *pAni = (ANI_SCRIPT *)LockMem(pAnim->hScript);
+ int zzz = pAnim->scriptIndex;
+
+ for (;;) {
+ // repeat until a real image
+ switch (FROM_LE_32(pAni[zzz].op)) {
+ case ANI_END: // end of animation script
+ case ANI_JUMP: // do animation jump
+ return true;
+
+ case ANI_HFLIP: // flip animated object horizontally
+ case ANI_VFLIP: // flip animated object vertically
+ case ANI_HVFLIP: // flip animated object in both directions
+ zzz++;
+ break;
+
+ case ANI_ADJUSTX: // adjust animated object x animation point
+ case ANI_ADJUSTY: // adjust animated object y animation point
+ zzz += 2;
+ break;
+
+ case ANI_ADJUSTXY: // adjust animated object x & y animation points
+ zzz += 3;
+ break;
+
+ case ANI_HIDE: // hide animated object
+ default: // must be an actual animation frame handle
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/anim.h b/engines/tinsel/anim.h
index 5b25292a6b..a3a7b4447e 100644
--- a/engines/tinsel/anim.h
+++ b/engines/tinsel/anim.h
@@ -41,6 +41,32 @@ struct ANIM {
uint32 hScript; //!< animation script handle
int scriptIndex; //!< current position in animation script
};
+typedef ANIM *PANIM;
+
+typedef void (*PANI_ADDR)(struct ANIM *);
+
+/** Animation script commands */
+enum {
+ ANI_END = 0, //!< end of animation script
+ ANI_JUMP = 1, //!< animation script jump
+ ANI_HFLIP = 2, //!< flip animated object horizontally
+ ANI_VFLIP = 3, //!< flip animated object vertically
+ ANI_HVFLIP = 4, //!< flip animated object in both directions
+ ANI_ADJUSTX = 5, //!< adjust animated object x animation point
+ ANI_ADJUSTY = 6, //!< adjust animated object y animation point
+ ANI_ADJUSTXY = 7, //!< adjust animated object x & y animation points
+ ANI_NOSLEEP = 8, //!< do not sleep for this frame
+ ANI_CALL = 9, //!< call routine
+ ANI_HIDE = 10, //!< hide animated object
+ ANI_STOP = 11 //!< stop sound
+};
+
+/** animation script command possibilities */
+union ANI_SCRIPT {
+ int32 op; //!< treat as an opcode or operand
+ uint32 hFrame; //!< treat as a animation frame handle
+// PANI_ADDR pFunc; //!< treat as a animation function call
+};
/*----------------------------------------------------------------------*\
@@ -66,6 +92,8 @@ void SkipFrames( // Skip the specified number of frames
ANIM *pAnim, // animation data structure
int numFrames); // number of frames to skip
+bool AboutToJumpOrEnd(PANIM pAnim);
+
} // end of namespace Tinsel
#endif // TINSEL_ANIM_H
diff --git a/engines/tinsel/background.cpp b/engines/tinsel/background.cpp
index 91d21b4e0b..94525e33dd 100644
--- a/engines/tinsel/background.cpp
+++ b/engines/tinsel/background.cpp
@@ -37,6 +37,9 @@ namespace Tinsel {
// current background
BACKGND *pCurBgnd = NULL;
+// FIXME: Not yet used
+static bool bEntireRedraw;
+
/**
* Called to initialise a background.
* @param pBgnd Pointer to data struct for current background
@@ -125,6 +128,27 @@ void PlayfieldGetPos(int which, int *pXpos, int *pYpos) {
}
/**
+ * Returns the x position of the centre of the specified playfield
+ * @param which Which playfield
+ */
+
+int PlayfieldGetCentreX(int which) {
+ PLAYFIELD *pPlayfield; // pointer to relavent playfield
+
+ // make sure there is a background
+ assert(pCurBgnd != NULL);
+
+ // make sure the playfield number is in range
+ assert(which >= 0 && which < pCurBgnd->numPlayfields);
+
+ // get playfield pointer
+ pPlayfield = pCurBgnd->fieldArray + which;
+
+ // get current integer position
+ return fracToInt(pPlayfield->fieldX) + SCREEN_WIDTH/2;
+}
+
+/**
* Returns the display list for the specified playfield.
* @param which Which playfield
*/
@@ -229,4 +253,9 @@ void DrawBackgnd(void) {
ResetClipRect();
}
+void ForceEntireRedraw(void) {
+ bEntireRedraw = true;
+}
+
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/background.h b/engines/tinsel/background.h
index 7b8d099446..245507841c 100644
--- a/engines/tinsel/background.h
+++ b/engines/tinsel/background.h
@@ -27,10 +27,11 @@
#ifndef TINSEL_BACKGND_H // prevent multiple includes
#define TINSEL_BACKGND_H
-#include "tinsel/dw.h" // for SCNHANDLE
-#include "tinsel/palette.h" // palette definitions
#include "common/frac.h"
#include "common/rect.h"
+#include "tinsel/coroutine.h"
+#include "tinsel/dw.h" // for SCNHANDLE
+#include "tinsel/palette.h" // palette definitions
namespace Tinsel {
@@ -78,6 +79,8 @@ struct BACKGND {
void InitBackground( // called to initialise a background
BACKGND *pBgnd); // pointer to data struct for current background
+void StartupBackground(CORO_PARAM, SCNHANDLE hFilm);
+
void StopBgndScrolling(void); // Stops all background playfields from scrolling
void PlayfieldSetPos( // Sets the xy position of the specified playfield in the current background
@@ -90,6 +93,9 @@ void PlayfieldGetPos( // Returns the xy position of the specified playfield in
int *pXpos, // returns current x position
int *pYpos); // returns current y position
+int PlayfieldGetCentreX( // Returns the xy position of the specified playfield in the current background
+ int which); // which playfield
+
OBJECT *GetPlayfieldList( // Returns the display list for the specified playfield
int which); // which playfield
@@ -100,7 +106,15 @@ void DrawBackgnd(void); // Draws all playfields for the current background
void RedrawBackgnd(void); // Completely redraws all the playfield object lists for the current background
-SCNHANDLE BackPal(void);
+OBJECT *GetBgObject();
+
+SCNHANDLE BgPal(void);
+
+void ForceEntireRedraw(void);
+
+int BgWidth(void);
+
+int BgHeight(void);
} // end of namespace Tinsel
diff --git a/engines/tinsel/bg.cpp b/engines/tinsel/bg.cpp
index 9c1e5f1540..96b03292cb 100644
--- a/engines/tinsel/bg.cpp
+++ b/engines/tinsel/bg.cpp
@@ -37,7 +37,8 @@
#include "tinsel/pid.h"
#include "tinsel/sched.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
-#include "tinsel/tinlib.h" // For control()
+#include "tinsel/tinlib.h" // For Control()
+#include "tinsel/tinsel.h"
#include "common/util.h"
@@ -45,49 +46,61 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
-static SCNHANDLE BackPalette = 0; // Background's palette
-static OBJECT *pBG = 0; // The main picture's object.
+#define MAX_BG 10
+
+static SCNHANDLE hBgPal = 0; // Background's palette
+static POBJECT pBG[MAX_BG];
+static ANIM thisAnim[MAX_BG]; // used by BGmainProcess()
static int BGspeed = 0;
-static SCNHANDLE BgroundHandle = 0; // Current scene handle - stored in case of Save_Scene()
-static bool DoFadeIn = false;
-static ANIM thisAnim; // used by BGmainProcess()
+static SCNHANDLE hBackground = 0; // Current scene handle - stored in case of Save_Scene()
+static bool bDoFadeIn = false;
+static int bgReels;
+
+/**
+ * GetBgObject
+ */
+OBJECT *GetBgObject() {
+ return pBG[0];
+}
/**
* BackPal
*/
-SCNHANDLE BackPal(void) {
- return BackPalette;
+SCNHANDLE BgPal(void) {
+ return hBgPal;
}
/**
* SetDoFadeIn
*/
void SetDoFadeIn(bool tf) {
- DoFadeIn = tf;
+ bDoFadeIn = tf;
}
/**
* Called before scene change.
*/
void DropBackground(void) {
- pBG = NULL; // No background
- BackPalette = 0; // No background palette
+ pBG[0] = NULL; // No background
+
+ if (!TinselV2)
+ hBgPal = 0; // No background palette
}
/**
* Return the width of the current background.
*/
-int BackgroundWidth(void) {
- assert(pBG);
- return MultiRightmost(pBG) + 1;
+int BgWidth(void) {
+ assert(pBG[0]);
+ return MultiRightmost(pBG[0]) + 1;
}
/**
* Return the height of the current background.
*/
-int BackgroundHeight(void) {
- assert(pBG);
- return MultiLowest(pBG) + 1;
+int BgHeight(void) {
+ assert(pBG[0]);
+ return MultiLowest(pBG[0]) + 1;
}
/**
@@ -100,90 +113,137 @@ static void BGmainProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
- const FREEL *pfr;
+ const FILM *pFilm;
+ const FREEL *pReel;
const MULTI_INIT *pmi;
// get the stuff copied to process when it was created
- pfr = (const FREEL *)param;
-
- if (pBG == NULL) {
+ if (pBG[0] == NULL) {
/*** At start of scene ***/
- // Get the MULTI_INIT structure
- pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfr->mobj));
-
- // Initialise and insert the object, and initialise its script.
- pBG = MultiInitObject(pmi);
- MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG);
- InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed);
+ 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 = pFilm->numreels;
+
+ int i;
+ for (i = 0; i < bgReels; i++) {
+ // Get the MULTI_INIT structure
+ pmi = (PMULTI_INIT) LockMem(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], pFilm->reels[i].script, BGspeed);
+
+ if (i > 0)
+ pBG[i-1]->pSlave = pBG[i];
+ }
+ }
- if (DoFadeIn) {
+ if (bDoFadeIn) {
FadeInFast(NULL);
- DoFadeIn = false;
- }
+ bDoFadeIn = false;
+ } else if (TinselV2)
+ PokeInTagColour();
- while (StepAnimScript(&thisAnim) != ScriptFinished)
- CORO_SLEEP(1);
+ for (;;) {
+ for (int i = 0; i < bgReels; i++) {
+ if (StepAnimScript(&thisAnim[i]) == ScriptFinished)
+ error("Background animation has finished!");
+ }
- error("Background animation has finished!");
+ CORO_SLEEP(1);
+ }
} else {
// New background during scene
-
- // Just re-initialise the script.
- InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed);
- StepAnimScript(&thisAnim);
+ 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 == 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;
}
/**
- * setBackPal()
+ * AetBgPal()
*/
-void setBackPal(SCNHANDLE hPal) {
- BackPalette = hPal;
+void SetBackPal(SCNHANDLE hPal) {
+ hBgPal = hPal;
- fettleFontPal(BackPalette);
- CreateTranslucentPalette(BackPalette);
+ FettleFontPal(hBgPal);
+ CreateTranslucentPalette(hBgPal);
}
void ChangePalette(SCNHANDLE hPal) {
- SwapPalette(FindPalette(BackPalette), hPal);
+ SwapPalette(FindPalette(hBgPal), hPal);
- setBackPal(hPal);
+ SetBackPal(hPal);
}
/**
* Given the scene background film, extracts the palette handle for
* everything else's use, then starts a display process for each reel
* in the film.
- * @param bfilm Scene background film
+ * @param hFilm Scene background film
*/
-void startupBackground(SCNHANDLE bfilm) {
+void StartupBackground(CORO_PARAM, SCNHANDLE hFilm) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
const FILM *pfilm;
IMAGE *pim;
- BgroundHandle = bfilm; // Save handle in case of Save_Scene()
+ hBackground = hFilm; // Save handle in case of Save_Scene()
- pim = GetImageFromFilm(bfilm, 0, NULL, NULL, &pfilm);
- setBackPal(FROM_LE_32(pim->hImgPal));
+ pim = GetImageFromFilm(hFilm, 0, NULL, NULL, &pfilm);
+ SetBackPal(FROM_LE_32(pim->hImgPal));
// Extract the film speed
BGspeed = ONE_SECOND / FROM_LE_32(pfilm->frate);
- if (pBG == NULL)
- control(CONTROL_STARTOFF); // New feature - start scene with control off
-
// Start display process for each reel in the film
- assert(FROM_LE_32(pfilm->numreels) == 1); // Multi-reeled backgrounds withdrawn
g_scheduler->createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL));
+
+ if (pBG[0] == NULL)
+ ControlStartOff();
+
+ if (TinselV2 && (coroParam != nullContext))
+ CORO_GIVE_WAY;
+
+ CORO_END_CODE;
}
/**
* Return the current scene handle.
*/
SCNHANDLE GetBgroundHandle(void) {
- return BgroundHandle;
+ return hBackground;
}
} // end of namespace Tinsel
diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp
new file mode 100644
index 0000000000..613d8c9686
--- /dev/null
+++ b/engines/tinsel/bmv.cpp
@@ -0,0 +1,1272 @@
+/* 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$
+ *
+ * The movie player.
+ */
+
+#include "common/file.h"
+#include "sound/mixer.h"
+#include "sound/audiostream.h"
+#include "tinsel/tinsel.h"
+#include "tinsel/background.h"
+#include "tinsel/cliprect.h"
+#include "tinsel/coroutine.h"
+#include "tinsel/config.h"
+#include "tinsel/dw.h"
+#include "tinsel/events.h"
+#include "tinsel/font.h"
+#include "tinsel/graphics.h"
+#include "tinsel/handle.h"
+#include "tinsel/heapmem.h"
+#include "tinsel/multiobj.h"
+#include "tinsel/object.h"
+#include "tinsel/palette.h"
+#include "tinsel/sched.h"
+#include "tinsel/strres.h"
+#include "tinsel/text.h"
+#include "tinsel/timers.h"
+#include "tinsel/tinlib.h"
+#include "tinsel/tinsel.h"
+
+namespace Tinsel {
+
+//----------------- GLOBAL GLOBAL DATA ------------------------
+
+bool bOldAudio;
+
+//----------------- LOCAL GLOBAL DATA ------------------------
+
+//static READREQ rr;
+
+//----------------- LOCAL DEFINES ----------------------------
+
+#define SZ_C_BLOB 65
+#define SZ_U_BLOB 128
+
+#define BLANK_SOUND 0x0 // for 16 bit silence
+
+#define PT_A 20 // Number of times PT_B may be reached
+#define PT_B 6
+
+
+#define SLOT_SIZE (25*1024)
+//#define NUM_SLOTS 168
+#define NUM_SLOTS 122 // -> ~ 3MB
+
+
+#define PREFETCH (NUM_SLOTS/2) // For initial test
+
+#ifndef _Windows
+//#define ADVANCE_SOUND 12 // 1 second
+#define ADVANCE_SOUND 18 // 1 1/2 second
+//#define MAX_ADVANCE_SOUND 36 // 3 seconds
+#else
+#define ADVANCE_SOUND 18 // 1 1/2 seconds
+#endif
+#define SUBSEQUENT_SOUND 6 // 1/2 second
+
+
+
+// PACKET TYPE IDs & FLAGS
+
+#define CD_SLOT_NOP 0x00 // Skip to next slot
+#define CD_LE_FIN 0x01 // End of movie
+#define CD_PDELTA 0x02 // Image compressed to previous one
+#define CD_SDELTA 0x03 // Image self-compressed
+
+#define BIT0 0x01
+
+#define CD_XSCR 0x04 // Screen has a scroll offset
+#define CD_CMAP 0x08 // Colour map is included
+#define CD_CMND 0x10 // Command is included
+#define CD_AUDIO 0x20 // Audio data is included
+#define CD_EXTEND 0x40 // Extended modes "A"-"z"
+#define CD_PRINT 0x80 // goes in conjunction with CD_CMD
+
+// Data field sizes
+#define sz_XSCR_pkt 2
+#define sz_CMAP_pkt 0x300
+#define sz_CMD_TALK_pkt 10
+#define sz_CMD_PRINT_pkt 8
+#define sz_AUDIO_pkt 3675
+
+
+typedef struct {
+
+ short x;
+ short y;
+ short stringId;
+ unsigned char duration;
+ char r; // may be b!
+ char g;
+ char b; // may be r!
+
+} TALK_CMD, *PTALK_CMD;
+
+typedef struct {
+
+ short x;
+ short y;
+ short stringId;
+ unsigned char duration;
+ unsigned char fontId;
+
+} PRINT_CMD, *PPRINT_CMD;
+
+
+//----------------- LOCAL GLOBAL DATA ------------------------
+
+// Set when a movie is on
+static bool bMovieOn;
+
+// Set to kill one off
+static bool bAbort;
+
+// For escaping out of movies
+static int bmvEscape;
+
+// Movie file pointer
+static Common::File stream;
+
+// Movie file name
+static char szMovieFile[14];
+
+// Pointers to buffers
+static byte *bigBuffer; //, *screenBuffer;
+
+// Next data to use to extract a frame
+static int nextUseOffset;
+
+// Next data to use to extract sound data
+static int nextSoundOffset;
+
+// When above offset gets to what this is set at, rewind
+static int wrapUseOffset;
+
+// The offset of the most future packet
+static int mostFutureOffset;
+
+// The current frame
+static int currentFrame;
+static int currentSoundFrame;
+
+// Number of packets currently in RAM
+static int numAdvancePackets;
+
+// Next slot that will be read from disc
+static int nextReadSlot;
+
+// Set when the whole file has been read
+static bool bFileEnd;
+
+// Palette
+static COLORREF moviePal[256];
+
+static int blobsInBuffer;
+
+static struct {
+
+ POBJECT pText;
+ int dieFrame;
+
+} texts[2];
+
+static COLORREF talkColour;
+
+static int bigProblemCount;
+
+static bool bIsText;
+
+static int movieTick;
+static int startTick;
+static uint32 nextMovieTime = 0;
+
+static int nowTick;
+
+static uint16 Au_Prev1 = 0;
+static uint16 Au_Prev2 = 0;
+static byte *ScreenBeg;
+static byte *screenBuffer;
+
+static bool audioStarted;
+
+static Audio::AppendableAudioStream *audioStream = 0;
+static Audio::SoundHandle audioHandle;
+
+const uint16 Au_DecTable[16] = {16512, 8256, 4128, 2064, 1032, 516, 258, 192,
+ 129, 88, 64, 56, 48, 40, 36, 32};
+
+//---------------- DECOMPRESSOR FUNCTIONS --------------------
+
+#define SCREEN_WIDE 640
+#define SCREEN_HIGH 429
+#define SAM_P_BLOB (32 * 2)
+
+#define ROR(x,v) x = ((x >> (v%32)) | (x << (32 - (v%32))));
+#define ROL(x,v) x = ((x << (v%32)) | (x >> (32 - (v%32))));
+#define NEXT_BYTE(v) v = forwardDirection ? v + 1 : v - 1;
+
+static void PrepBMV(const byte *sourceData, int length, short deltaFetchDisp) {
+ uint8 NibbleHi = 0;
+ const byte *saved_esi;
+ uint32 eax = 0;
+ uint32 edx = length;
+ int32 ebx = deltaFetchDisp;
+ uint32 ecx = 0;
+ const byte *esi;
+ byte *edi, *ebp;
+
+ bool forwardDirection = (deltaFetchDisp <= -SCREEN_WIDE) || (deltaFetchDisp >= 0);
+ if (forwardDirection) {
+ // Forward decompression
+ esi = sourceData;
+ edi = ScreenBeg;
+ ebp = ScreenBeg + SCREEN_WIDE * SCREEN_HIGH;
+ } else {
+ esi = sourceData + length - 1;
+ edi = ScreenBeg + SCREEN_WIDE * SCREEN_HIGH - 1;
+ ebp = ScreenBeg - 1;
+ }
+
+ bool firstLoop, flag;
+
+ int loopCtr = 0;
+ for (;;) {
+ flag = false;
+
+ if ((loopCtr == 0) || (edx == 4)) {
+ // Get the next hi,lo nibble
+ eax = (eax & 0xffffff00) | *esi;
+ firstLoop = true;
+ } else {
+ // Get the high nibble
+ eax = eax & 0xffffff00 | (NibbleHi >> 4);
+ firstLoop = false;
+ }
+
+ // Is lo nibble '00xx'?
+ if ((eax & 0xC) == 0) {
+ for (;;) {
+//@_rDN_Lp_1:
+ // Only execute this bit first the first time into the loop
+ if (!firstLoop) {
+ ROR(eax, 2);
+ ecx += 2;
+ eax = (eax & 0xffffff00) | *esi;
+
+ if ((eax & 0xC) != 0)
+ break;
+ }
+ firstLoop = false;
+
+//@_rD2nd_1:
+ ROR(eax, 2); // Save bi-bit into hi 2 bits
+ ecx += 2; // and increase bit-shifter
+ // Shift another 2 bits to get hi nibble
+ eax = (eax & 0xffffff00) | ((eax & 0xff) >> 2);
+ NEXT_BYTE(esi);
+
+ if ((eax & 0xC) != 0) {
+ flag = true;
+ ROL(eax, ecx);
+ break;
+ }
+ }
+ } else if (loopCtr != 0) {
+ flag = edx != 4;
+ }
+
+ if (flag) {
+//@_rdNum__1:
+ edx = 4; // offset rDNum_Lo ; Next nibble is a 'lo'
+ } else {
+// @_rDNum_1
+ NibbleHi = (uint8)eax;
+ edx = 0; // offset rDNum_Hi ; Next nibble is a 'hi' (reserved)
+ eax &= 0xffffff0f;
+ NEXT_BYTE(esi);
+ ROL(eax, ecx);
+ }
+//rDN_1:
+//@_rD_or_R:
+ bool actionFlag = (eax & 1) != 0;
+ eax >>= 1;
+ ecx = eax - 1;
+
+ // Move to next loop index
+ if (++loopCtr == 4) loopCtr = 1;
+
+ if (actionFlag) {
+ // Adjust loopCtr to fall into the correct processing case
+ loopCtr = loopCtr % 3 + 1;
+ }
+
+ switch (loopCtr) {
+ case 1:
+ // @_rDelta:
+ saved_esi = esi; // Save the source pointer
+ esi = edi + ebx; // Point it to existing data
+
+ while (ecx > 0) {
+ *edi = *esi;
+ NEXT_BYTE(esi);
+ NEXT_BYTE(edi);
+ --ecx;
+ }
+
+ esi = saved_esi;
+ break;
+
+ case 2:
+ // @_rRaw
+ // Copy data from source to dest
+ while (ecx > 0) {
+ *edi = *esi;
+ NEXT_BYTE(esi);
+ NEXT_BYTE(edi);
+ --ecx;
+ }
+ break;
+
+ case 3:
+ // @_rRun
+ // Repeating run of data
+ eax = forwardDirection ? *(edi - 1) : *(edi + 1);
+
+ while (ecx > 0) {
+ *edi = (uint8)eax;
+ NEXT_BYTE(edi);
+ --ecx;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (edi == ebp)
+ break; // Exit if complete
+
+ eax = 0;
+ }
+}
+
+static void InitBMV(byte *memoryBuffer) {
+ // Clear the two extra 'off-screen' rows
+ memset(memoryBuffer, 0, SCREEN_WIDE);
+ memset(memoryBuffer + SCREEN_WIDE * (SCREEN_HIGH + 1), 0, SCREEN_WIDE);
+
+ if (audioStream) {
+ _vm->_mixer->stopHandle(audioHandle);
+
+ delete audioStream;
+ audioStream = 0;
+ }
+
+ // Set the screen beginning to the second line (ie. past the off-screen line)
+ ScreenBeg = memoryBuffer + SCREEN_WIDTH;
+ Au_Prev1 = Au_Prev2 = 0;
+}
+
+void PrepAudio(const byte *sourceData, int blobCount, byte *destPtr) {
+ uint16 dx1 = Au_Prev1;
+ uint16 dx2 = Au_Prev2;
+
+ uint16 *destP = (uint16 *) destPtr;
+ int8 *srcP = (int8 *) sourceData;
+
+ // Blob Loop
+ while (blobCount-- > 0) {
+ uint32 ebx = (uint8) *srcP++;
+ uint32 ebp = ebx & 0x1E;
+
+ int blobSize = SAM_P_BLOB / 2;
+
+ ebx = (((ebx & 0x0F) << 4) | ((ebx & 0xF0) >> 4)) & 0x1E;
+
+ ebp = Au_DecTable[ebp >> 1];
+ ebx = Au_DecTable[ebx >> 1];
+
+ // Inner loop
+ while (blobSize-- > 0) {
+ uint32 s1 = (((int32) *srcP++) * ((int32) ebp)) >> 5;
+ uint32 s2 = (((int32) *srcP++) * ((int32) ebx)) >> 5;
+
+ dx1 += s1 & 0xFFFF;
+ dx2 += s2 & 0xFFFF;
+
+ *destP++ = TO_BE_16(dx1);
+ *destP++ = TO_BE_16(dx2);
+ }
+ }
+
+ Au_Prev1 = dx1;
+ Au_Prev2 = dx2;
+}
+
+//----------------- BMV FUNCTIONS ----------------------------
+
+static bool MaintainBuffer(void);
+
+
+/**
+ * Called when a packet contains a palette field.
+ * Build a COLORREF array and queue it to the DAC.
+ */
+static void MoviePalette(int paletteOffset) {
+ int i;
+ byte *r;
+
+ r = bigBuffer + paletteOffset;
+
+ for (i = 0; i < 256; i++, r += 3) {
+ moviePal[i] = RGB(*r, *(r + 1), *(r + 2));
+ }
+
+ UpdateDACqueue(1, 255, &moviePal[1]);
+
+ // Don't clobber talk
+ if (talkColour != 0)
+ SetTextPal(talkColour);
+}
+
+static void InitialiseMovieSound() {
+ audioStream =
+ Audio::makeAppendableAudioStream(22050,
+ Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_STEREO);
+ audioStarted = false;
+}
+
+static void StartMovieSound() {
+}
+
+static void FinishMovieSound() {
+ if (audioStream) {
+ _vm->_mixer->stopHandle(audioHandle);
+
+ delete audioStream;
+ audioStream = 0;
+ }
+}
+
+/**
+ * Called when a packet contains an audio field.
+ */
+static void MovieAudio(int audioOffset, int blobs) {
+ if (audioOffset == 0 && blobs == 0)
+ blobs = 57;
+
+ byte *data = new byte[blobs * 128];
+
+ if (audioOffset != 0)
+ PrepAudio(bigBuffer+audioOffset, blobs, data);
+ else
+ memset(data, 0, blobs * 128);
+
+ audioStream->queueBuffer(data, blobs * 128);
+
+ if (currentSoundFrame == ADVANCE_SOUND) {
+ if (!audioStarted) {
+ _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType,
+ &audioHandle, audioStream, -1, Audio::Mixer::kMaxChannelVolume, 0, false);
+ audioStarted = true;
+ }
+ }
+}
+
+/*-----------------------------------------------------*\
+|-------------------------------------------------------|
+\*-----------------------------------------------------*/
+
+void FettleMovieText(void) {
+ int i;
+
+ bIsText = false;
+
+ for (i = 0; i < 2; i++) {
+ if (texts[i].pText) {
+ if (currentFrame > texts[i].dieFrame) {
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), texts[i].pText);
+ texts[i].pText = NULL;
+ } else {
+ MultiForceRedraw(texts[i].pText);
+ bIsText = true;
+ }
+ }
+ }
+}
+
+/*-----------------------------------------------------*\
+|-------------------------------------------------------|
+\*-----------------------------------------------------*/
+
+void BmvDrawText(bool bDraw) {
+ int w, h, x, y;
+
+ for (int i = 0; i < 2; i++) {
+ if (texts[i].pText) {
+ x = MultiLeftmost(texts[i].pText);
+ y = MultiHighest(texts[i].pText);
+ w = MIN(MultiRightmost(texts[i].pText) + 1, (int)SCREEN_WIDTH) - x;
+ h = MIN(MultiLowest(texts[i].pText) + 1, SCREEN_HIGH) - y;
+
+ const byte *src = ScreenBeg + (y * SCREEN_WIDTH) + x;
+ byte *dest = (byte *)_vm->screen().getBasePtr(x, y);
+
+ for (int j = 0; j < h; j++, dest += SCREEN_WIDTH, src += SCREEN_WIDTH) {
+ memcpy(dest, src, w);
+ }
+
+ if (bDraw) {
+ Common::Point ptWin;
+ Common::Rect rcPlayClip;
+
+ ptWin.x = ptWin.y = 0;
+ rcPlayClip.left = x;
+ rcPlayClip.top = y;
+ rcPlayClip.right = x+w;
+ rcPlayClip.bottom = y+h;
+ UpdateClipRect(GetPlayfieldList(FIELD_STATUS), &ptWin, &rcPlayClip);
+ }
+ }
+ }
+}
+
+/*-----------------------------------------------------*\
+|-------------------------------------------------------|
+\*-----------------------------------------------------*/
+
+void MovieText(CORO_PARAM, int stringId, int x, int y, int fontId, COLORREF *pTalkColour, int duration) {
+ SCNHANDLE hFont;
+ int index;
+
+ if (fontId == 1) {
+ // It's a 'print'
+
+ hFont = GetTagFontHandle();
+ index = 0;
+ } else {
+ // It's a 'talk'
+
+ if (pTalkColour != NULL)
+ SetTextPal(*pTalkColour);
+ hFont = GetTalkFontHandle();
+ index = 1;
+ }
+
+ if (texts[index].pText)
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), texts[index].pText);
+
+ LoadSubString(stringId, 0, TextBufferAddr(), TBUFSZ);
+
+ texts[index].dieFrame = currentFrame + duration;
+ texts[index].pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(),
+ 0,
+ x, y,
+ hFont,
+ TXT_CENTRE, 0);
+ KeepOnScreen(texts[index].pText, &x, &y);
+}
+
+/**
+ * Called when a packet contains a command field.
+ */
+static int MovieCommand(char cmd, int commandOffset) {
+ if (cmd & CD_PRINT) {
+ PPRINT_CMD pCmd;
+
+ pCmd = (PPRINT_CMD)(bigBuffer + commandOffset);
+
+ MovieText(nullContext, pCmd->stringId,
+ pCmd->x,
+ pCmd->y,
+ pCmd->fontId,
+ NULL,
+ pCmd->duration);
+
+ return sz_CMD_PRINT_pkt;
+ } else {
+ if (bSubtitles) {
+ PTALK_CMD pCmd;
+
+ pCmd = (PTALK_CMD)(bigBuffer + commandOffset);
+ talkColour = RGB(pCmd->r, pCmd->g, pCmd->b);
+
+ MovieText(nullContext, pCmd->stringId,
+ pCmd->x,
+ pCmd->y,
+ 0,
+ &talkColour,
+ pCmd->duration);
+ }
+ return sz_CMD_TALK_pkt;
+ }
+}
+
+/**
+ * Called from PlayMovie() in tinlib.cpp
+ * Kicks off the playback of a movie, and waits around
+ * until it's finished.
+ */
+void PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ assert(!bMovieOn);
+
+ strcpy(szMovieFile, (char *)LockMem(hFileStem));
+ strcat(szMovieFile, BMOVIE_EXTENSION);
+
+ assert(strlen(szMovieFile) <= 12);
+
+ bMovieOn = true;
+ bAbort = false;
+ bmvEscape = myEscape;
+
+ do {
+ CORO_SLEEP(1);
+ } while (bMovieOn);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Given a packet offset, calculates the offset of the
+ * next packet. The packet may not yet exist, and the
+ *return value may be off the end of bigBuffer.
+ */
+static int FollowingPacket(int thisPacket, bool bReallyImportant) {
+ unsigned char *data;
+ int nextSlot, length;
+
+ // Set pointer to thisPacket's data
+ data = bigBuffer + thisPacket;
+
+ switch (*data) {
+ case CD_SLOT_NOP:
+ nextSlot = thisPacket/SLOT_SIZE;
+ if (thisPacket%SLOT_SIZE)
+ nextSlot++;
+
+ return nextSlot * SLOT_SIZE;
+
+ case CD_LE_FIN:
+ return -1;
+
+ default:
+ // Following 3 bytes are the length
+ if (bReallyImportant) {
+ // wrapped round or at least 3 bytes
+ assert(((nextReadSlot * SLOT_SIZE) < thisPacket) ||
+ ((thisPacket + 3) < (nextReadSlot * SLOT_SIZE)));
+
+ if ((nextReadSlot * SLOT_SIZE >= thisPacket) &&
+ ((thisPacket + 3) >= nextReadSlot*SLOT_SIZE)) {
+ // MaintainBuffer calls this back, but with false
+ MaintainBuffer();
+ }
+ } else {
+ // not wrapped and not 3 bytes
+ if (nextReadSlot*SLOT_SIZE >= thisPacket && thisPacket+3 >= nextReadSlot*SLOT_SIZE)
+ return thisPacket + 3;
+ }
+ length = *(int32 *)(bigBuffer + thisPacket + 1);
+ length &= 0x00ffffff;
+ return thisPacket + length + 4;
+ }
+}
+
+/**
+ * Called from the foreground when starting playback of a movie.
+ */
+static void LoadSlots(int number) {
+ int nextOffset;
+
+ assert(number + nextReadSlot < NUM_SLOTS);
+
+ if (stream.read(bigBuffer + nextReadSlot*SLOT_SIZE, number * SLOT_SIZE) !=
+ (uint32)(number * SLOT_SIZE)) {
+ int possibleSlots;
+
+ // May be a short file
+ possibleSlots = stream.size() / SLOT_SIZE;
+ if ((number + nextReadSlot) > possibleSlots) {
+ bFileEnd = true;
+ nextReadSlot = possibleSlots;
+ } else
+ error(FILE_IS_CORRUPT, szMovieFile);
+ }
+
+ nextReadSlot += number;
+
+ nextOffset = FollowingPacket(nextUseOffset, true);
+ while (nextOffset < nextReadSlot*SLOT_SIZE
+ && nextOffset != -1) {
+ numAdvancePackets++;
+ mostFutureOffset = nextOffset;
+ nextOffset = FollowingPacket(mostFutureOffset, false);
+ }
+}
+
+/**
+ * Called from the foreground when starting playback of a movie.
+ */
+static void InitialiseBMV(void) {
+ if (!stream.open(szMovieFile))
+ error(CANNOT_FIND_FILE, szMovieFile);
+
+ // Grab the data buffer
+ bigBuffer = (byte *)malloc(NUM_SLOTS * SLOT_SIZE);
+ if (bigBuffer == NULL)
+ error(NO_MEM, "FMV data buffer\n");
+
+ // Screen buffer (2 lines more than screen
+ screenBuffer = (byte *)malloc(SCREEN_WIDTH * (SCREEN_HIGH + 2));
+ if (screenBuffer == NULL)
+ error(NO_MEM, "FMV screen buffer\n");
+
+ // Pass the sceen buffer to the decompresser
+ InitBMV(screenBuffer);
+
+ // Initialise some stuff
+ nextUseOffset = 0;
+ nextSoundOffset = 0;
+ wrapUseOffset = -1;
+ mostFutureOffset = 0;
+ currentFrame = 0;
+ currentSoundFrame = 0;
+ numAdvancePackets = 0;
+ nextReadSlot = 0;
+ bFileEnd = false;
+ blobsInBuffer = 0;
+ memset(texts, 0, sizeof(texts));
+ talkColour = 0;
+ bigProblemCount = 0;
+
+ movieTick = 0;
+
+ bIsText = false;
+
+// memset(&rr, 0, sizeof(rr));
+
+ // Prefetch data
+ LoadSlots(PREFETCH);
+
+ while (numAdvancePackets < ADVANCE_SOUND)
+ LoadSlots(1);
+
+ // Initialise the sound channel
+ InitialiseMovieSound();
+}
+
+/**
+ * Called from the foreground when ending playback of a movie.
+ */
+void FinishBMV(void) {
+ int i;
+
+ // Notify the sound channel
+ FinishMovieSound();
+
+ // Close the file stream
+ if (stream.isOpen())
+ stream.close();
+
+ // Release the data buffer
+ if (bigBuffer != NULL) {
+ free(bigBuffer);
+ bigBuffer = NULL;
+ }
+
+ // Release the screen buffer
+ if (screenBuffer != NULL) {
+ free(screenBuffer);
+ screenBuffer = NULL;
+ }
+
+ // Ditch any text objects
+ for (i = 0; i < 2; i++) {
+ if (texts[i].pText) {
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), texts[i].pText);
+ texts[i].pText = NULL;
+ }
+ }
+ bMovieOn = false;
+
+ nextMovieTime = 0;
+
+ // Test for 'twixt-movie glitch
+ ClearScreen();
+}
+
+/**
+ * MaintainBuffer()
+ */
+static bool MaintainBuffer(void) {
+ int nextOffset;
+
+ // No action if the file is all read
+ if (bFileEnd == true)
+ return false;
+
+ // See if next complete packet exists
+ // and if so, if it will fit in the top of the buffer
+ nextOffset = FollowingPacket(mostFutureOffset, false);
+ if (nextOffset == -1) {
+ // No following packets
+ return false;
+ } else if (nextOffset > NUM_SLOTS * SLOT_SIZE) {
+ // The current unfinished packet will not fit
+ // Copy this slot to slot 0
+
+ // Not if we're still using it!!!
+ // Or, indeed, if the player is still lagging
+ if (nextUseOffset < SLOT_SIZE || nextUseOffset > mostFutureOffset) {
+ // Slot 0 is still in use, buffer is full!
+ return false;
+ }
+
+ // Tell data player where to make the jump
+ wrapUseOffset = mostFutureOffset;
+
+ // mostFuture Offset is now in slot 0
+ mostFutureOffset %= SLOT_SIZE;
+
+ // Copy the data we already have for unfinished packet
+ memcpy(bigBuffer + mostFutureOffset,
+ bigBuffer + wrapUseOffset,
+ SLOT_SIZE - mostFutureOffset);
+
+ // Next read is into slot 1
+ nextReadSlot = 1;
+ }
+
+ if (nextReadSlot == NUM_SLOTS) {
+ // Want to go to slot zero, wait if still in use
+ if (nextUseOffset < SLOT_SIZE) {
+ // Slot 0 is still in use, buffer is full!
+ return false;
+ }
+
+ // nextOffset must be the buffer size
+ assert(nextOffset == NUM_SLOTS*SLOT_SIZE);
+
+ // wrapUseOffset must not be set
+ assert(wrapUseOffset == -1);
+ wrapUseOffset = nextOffset;
+
+ nextReadSlot = 0;
+ mostFutureOffset = 0;
+ }
+
+ // Don't overwrite unused data
+ if (nextUseOffset / SLOT_SIZE == nextReadSlot) {
+ // Buffer is full!
+ return false;
+ }
+
+ if (stream.read(bigBuffer + nextReadSlot * SLOT_SIZE, SLOT_SIZE) != SLOT_SIZE) {
+ bFileEnd = true;
+ }
+
+ // Read next slot next time
+ nextReadSlot++;
+
+ // Find new mostFutureOffset
+ nextOffset = FollowingPacket(mostFutureOffset, false);
+ while (nextOffset < nextReadSlot*SLOT_SIZE
+ && nextOffset != -1) {
+ numAdvancePackets++;
+ mostFutureOffset = nextOffset;
+ nextOffset = FollowingPacket(mostFutureOffset, false);
+ }
+
+ // New test feature for e.g. short files
+ if (bFileEnd && *(bigBuffer+mostFutureOffset) != CD_LE_FIN)
+ bAbort = true;
+
+ return true;
+}
+
+/**
+ * DoBMVFrame
+ */
+static bool DoBMVFrame(void) {
+ unsigned char *data;
+ int graphOffset, length;
+ signed short xscr;
+
+ if (nextUseOffset == wrapUseOffset) {
+ nextUseOffset %= SLOT_SIZE;
+ }
+
+ while (nextUseOffset == mostFutureOffset) {
+ data = bigBuffer + nextUseOffset;
+ if (*data != CD_LE_FIN) {
+ // Don't get stuck in an infinite loop
+ if (!MaintainBuffer()) {
+ FinishBMV();
+ return false;
+ }
+
+ if (nextUseOffset == wrapUseOffset) {
+ nextUseOffset %= SLOT_SIZE;
+ }
+ } else
+ break;
+ }
+
+ // Set pointer to data
+ data = bigBuffer + nextUseOffset;
+
+ // If still at most Future, it must be last
+ if (nextUseOffset == mostFutureOffset) {
+ assert(*data == CD_LE_FIN);
+ }
+
+ switch (*data) {
+ case CD_SLOT_NOP:
+ nextUseOffset = FollowingPacket(nextUseOffset, true);
+ if (nextUseOffset == wrapUseOffset) {
+ nextUseOffset %= SLOT_SIZE;
+ wrapUseOffset = -1;
+ }
+ numAdvancePackets--;
+ return false;
+
+ case CD_LE_FIN:
+ FinishBMV();
+ numAdvancePackets--;
+ return true;
+
+ default:
+ length = *(int *)(data + 1);
+ length &= 0x00ffffff;
+
+ graphOffset = nextUseOffset + 4; // Skip command byte and length
+
+ if (*data & CD_AUDIO) {
+ if (bOldAudio) {
+ graphOffset += sz_AUDIO_pkt; // Skip audio data
+ length -= sz_AUDIO_pkt;
+ } else {
+ int blobs;
+
+ blobs = *(bigBuffer + graphOffset);
+ blobs *= SZ_C_BLOB;
+ graphOffset += (blobs + 1);
+ length -= (blobs + 1);
+ }
+ }
+
+ if (*data & CD_CMND) {
+ int cmdLen;
+
+ // Process command and skip data
+ cmdLen = MovieCommand(*data, graphOffset);
+
+ graphOffset += cmdLen;
+ length -= cmdLen;
+ }
+
+ if (*data & CD_CMAP) {
+ MoviePalette(graphOffset);
+ graphOffset += sz_CMAP_pkt; // Skip palette data
+ length -= sz_CMAP_pkt;
+ }
+
+ if (*data & CD_XSCR) {
+ xscr = *(signed short *)(bigBuffer + graphOffset);
+ graphOffset += sz_XSCR_pkt; // Skip scroll offset
+ length -= sz_XSCR_pkt;
+ } else if (*data & BIT0)
+ xscr = -640;
+ else
+ xscr = 0;
+
+ PrepBMV(bigBuffer + graphOffset, length, xscr);
+
+ currentFrame++;
+ numAdvancePackets--;
+
+ nextUseOffset = FollowingPacket(nextUseOffset, true);
+ if (nextUseOffset == wrapUseOffset) {
+ nextUseOffset %= SLOT_SIZE;
+ wrapUseOffset = -1;
+ }
+ return true;
+ }
+}
+
+/**
+ * DoSoundFrame
+ */
+static bool DoSoundFrame(void) {
+ unsigned char *data;
+ int graphOffset;
+
+ if (nextSoundOffset == wrapUseOffset) {
+ nextSoundOffset %= SLOT_SIZE;
+ }
+
+ // Make sure the full slot is here
+ while (nextSoundOffset == mostFutureOffset) {
+ data = bigBuffer + nextSoundOffset;
+ if (*data != CD_LE_FIN) {
+ // Don't get stuck in an infinite loop
+ if (!MaintainBuffer()) {
+ if (!bOldAudio)
+ MovieAudio(0, 0);
+ currentSoundFrame++;
+ return false;
+ }
+
+ if (nextSoundOffset == wrapUseOffset) {
+ nextSoundOffset %= SLOT_SIZE;
+ }
+ } else
+ break;
+ }
+
+ // Set pointer to data
+ data = bigBuffer + nextSoundOffset;
+
+ // If still at most Future, it must be last
+ if (nextSoundOffset == mostFutureOffset) {
+ assert(*data == CD_LE_FIN);
+ }
+
+ switch (*data) {
+ case CD_SLOT_NOP:
+ nextSoundOffset = FollowingPacket(nextSoundOffset, true);
+ if (nextSoundOffset == wrapUseOffset) {
+ nextSoundOffset %= SLOT_SIZE;
+ }
+ return false;
+
+ case CD_LE_FIN:
+ if (!bOldAudio)
+ MovieAudio(0, 0);
+ currentSoundFrame++;
+ return true;
+
+ default:
+ if (*data & CD_AUDIO) {
+ graphOffset = nextSoundOffset + 4; // Skip command byte and length
+
+ if (!bOldAudio) {
+ int blobs = *(bigBuffer + graphOffset);
+ MovieAudio(graphOffset+1, blobs);
+ }
+ } else {
+ if (!bOldAudio)
+ MovieAudio(0, 0);
+ }
+
+ nextSoundOffset = FollowingPacket(nextSoundOffset, false);
+ if (nextSoundOffset == wrapUseOffset) {
+ nextSoundOffset %= SLOT_SIZE;
+ }
+ currentSoundFrame++;
+ return true;
+ }
+
+ return true;
+}
+
+/**
+ * CopyMovieToScreen
+ */
+void CopyMovieToScreen(void) {
+ // Not if not up and running yet!
+ if (!screenBuffer || (currentFrame == 0)) {
+ ForceEntireRedraw();
+ DrawBackgnd();
+ return;
+ }
+
+ // The movie surface is slightly less high than the output screen (429 rows versus 432).
+ // Because of this, there's some extra line clearing above and below the displayed area
+ int yStart = (SCREEN_HEIGHT - SCREEN_HIGH) / 2;
+ memset(_vm->screen().getBasePtr(0, 0), 0, yStart * SCREEN_WIDTH);
+ memcpy(_vm->screen().getBasePtr(0, yStart), ScreenBeg, SCREEN_WIDTH * SCREEN_HIGH);
+ memset(_vm->screen().getBasePtr(0, yStart + SCREEN_HIGH), 0,
+ (SCREEN_HEIGHT - SCREEN_HIGH - yStart) * SCREEN_WIDTH);
+
+ BmvDrawText(true);
+ PalettesToVideoDAC(); // Keep palette up-to-date
+ UpdateScreenRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+ BmvDrawText(false);
+}
+
+/**
+ * LookAtBuffers
+ */
+static void LookAtBuffers(void) {
+ static int junk;
+ int i;
+
+ if (bigBuffer) {
+ for (i = 0; i < NUM_SLOTS; i++)
+ junk += bigBuffer[i*SLOT_SIZE];
+ }
+}
+
+/**
+ * Handles playback of any active movie. Called from the foreground 24 times a second.
+ */
+void FettleBMV(void) {
+ static int nextMaintain = 0;
+
+ int refFrame;
+ // Tick counter needs to be incrementing at a 24Hz rate
+ int tick = movieTick++;
+
+ if (!bMovieOn)
+ return;
+
+ // Escape the rest if appropriate
+ if (bAbort || (bmvEscape && bmvEscape != GetEscEvents())) {
+ FinishBMV();
+ return;
+ }
+
+ LookAtBuffers();
+
+ if (!stream.isOpen()) {
+ int i;
+
+ // First time in with this movie
+
+ InitialiseBMV();
+
+ for (i = 0; i < ADVANCE_SOUND;) {
+ if (DoSoundFrame())
+ i++;
+ }
+ startTick = -ONE_SECOND / 4; // 1/4 second
+ return;
+ }
+
+ if (startTick < 0) {
+ startTick++;
+ return;
+ }
+ if (startTick == 0) {
+ startTick = tick;
+ nextMaintain = startTick + 1;
+ StartMovieSound();
+ }
+
+ nextMovieTime = g_system->getMillis() + 41;
+
+ FettleMovieText();
+
+ if (bigProblemCount < PT_A) {
+ refFrame = currentSoundFrame;
+
+ while (currentSoundFrame < ((tick+1-startTick)/2 + ADVANCE_SOUND) && bMovieOn) {
+ if (currentSoundFrame == refFrame+PT_B)
+ break;
+
+ DoSoundFrame();
+ }
+ }
+
+ // Time to process a frame (or maybe more)
+ if (bigProblemCount < PT_A) {
+ refFrame = currentFrame;
+
+ while ((currentFrame < (tick-startTick)/2) && bMovieOn) {
+ DoBMVFrame();
+
+ if (currentFrame == refFrame+PT_B) {
+ bigProblemCount++;
+
+ if (bigProblemCount == PT_A) {
+ startTick = tick-(2*currentFrame);
+ bigProblemCount = 0;
+ }
+ break;
+ }
+ }
+ if (currentFrame == refFrame || currentFrame <= refFrame+3) {
+ bigProblemCount = 0;
+ }
+ } else {
+ while (currentFrame < (tick-startTick)/2 && bMovieOn) {
+ DoBMVFrame();
+ }
+ }
+
+ if (tick >= nextMaintain || numAdvancePackets < SUBSEQUENT_SOUND) {
+ MaintainBuffer();
+ nextMaintain = tick + 2;
+ }
+}
+
+/**
+ * Returns true if a movie is playing.
+ */
+bool MoviePlaying(void) {
+ return bMovieOn;
+}
+
+/**
+ * Returns the audio lag in ms
+ */
+int32 MovieAudioLag(void) {
+ if (!bMovieOn || !audioStream)
+ return 0;
+
+ // Calculate lag
+ int32 playLength = (movieTick - startTick - 1) * ((((uint32) 1000) << 10) / 24);
+ return (playLength - (audioStream->getTotalPlayTime() << 10)) >> 10;
+}
+
+uint32 NextMovieTime(void) {
+ return nextMovieTime;
+}
+
+void AbortMovie(void) {
+ bAbort = true;
+}
+
+void SlowMovieDown(void) {
+ bigProblemCount = 0;
+
+ if (currentFrame < (nowTick-startTick)/2 && bMovieOn) {
+ startTick = nowTick - 2*currentFrame;
+ } else
+ startTick += 2;
+}
+
+void SpeedMovieUp(void) {
+ if (!bigProblemCount) {
+ startTick -= 2;
+ }
+}
+
+} // end of namespace Tinsel
diff --git a/engines/tinsel/config.cpp b/engines/tinsel/config.cpp
index 803d2231e4..c6d774de7a 100644
--- a/engines/tinsel/config.cpp
+++ b/engines/tinsel/config.cpp
@@ -24,6 +24,8 @@
* This file contains configuration functionality
*/
+//#define USE_3FLAGS 1
+
#include "tinsel/config.h"
#include "tinsel/dw.h"
#include "tinsel/sound.h"
@@ -39,7 +41,7 @@ namespace Tinsel {
//----------------- GLOBAL GLOBAL DATA --------------------
int dclickSpeed = DOUBLE_CLICK_TIME;
-int volMidi = Audio::Mixer::kMaxChannelVolume;
+int volMusic = Audio::Mixer::kMaxChannelVolume;
int volSound = Audio::Mixer::kMaxChannelVolume;
int volVoice = Audio::Mixer::kMaxChannelVolume;
int speedText = DEFTEXTSPEED;
@@ -49,21 +51,18 @@ LANGUAGE g_language = TXT_ENGLISH;
int bAmerica = 0;
-// Shouldn't really be here, but time is short...
-bool bNoBlocking;
/**
* Write settings to config manager and flush the config file to disk.
*/
void WriteConfig(void) {
ConfMan.setInt("dclick_speed", dclickSpeed);
- ConfMan.setInt("music_volume", volMidi);
+ ConfMan.setInt("music_volume", volMusic);
ConfMan.setInt("sfx_volume", volSound);
ConfMan.setInt("speech_volume", volVoice);
ConfMan.setInt("talkspeed", (speedText * 255) / 100);
ConfMan.setBool("subtitles", bSubtitles);
//ConfMan.setBool("swap_buttons", bSwapButtons ? 1 : 0);
- //ConfigData.bAmerica = bAmerica; // EN_USA / EN_GRB
// Store language for multilingual versions
if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
@@ -92,16 +91,14 @@ void WriteConfig(void) {
ConfMan.flushToDisk();
}
-/*---------------------------------------------------------------------*\
-| ReadConfig() |
-|-----------------------------------------------------------------------|
-|
-\*---------------------------------------------------------------------*/
+/**
+ * Read configuration settings from the config file into memory
+ */
void ReadConfig(void) {
if (ConfMan.hasKey("dclick_speed"))
dclickSpeed = ConfMan.getInt("dclick_speed");
- volMidi = ConfMan.getInt("music_volume");
+ volMusic = ConfMan.getInt("music_volume");
volSound = ConfMan.getInt("sfx_volume");
volVoice = ConfMan.getInt("speech_volume");
diff --git a/engines/tinsel/config.h b/engines/tinsel/config.h
index fc85f0abe0..a02b763afb 100644
--- a/engines/tinsel/config.h
+++ b/engines/tinsel/config.h
@@ -37,7 +37,7 @@ enum {
};
extern int dclickSpeed;
-extern int volMidi;
+extern int volMusic;
extern int volSound;
extern int volVoice;
extern int speedText;
@@ -51,10 +51,6 @@ void ReadConfig(void);
extern bool isJapanMode();
-
-// Shouldn't really be here, but time is short...
-extern bool bNoBlocking;
-
} // end of namespace Tinsel
#endif
diff --git a/engines/tinsel/coroutine.h b/engines/tinsel/coroutine.h
index e0292735bb..8cdbd44b19 100644
--- a/engines/tinsel/coroutine.h
+++ b/engines/tinsel/coroutine.h
@@ -70,6 +70,7 @@ struct CoroBaseContext {
typedef CoroBaseContext *CoroContext;
+extern CoroContext nullContext;
/**
* Wrapper class which holds a pointer to a pointer to a CoroBaseContext.
@@ -101,6 +102,7 @@ public:
#define CORO_END_CONTEXT(x) } *x = (CoroContextTag *)coroParam
#define CORO_BEGIN_CODE(x) \
+ if (&coroParam == &nullContext) assert(!nullContext);\
if (!x) {coroParam = x = new CoroContextTag();}\
assert(coroParam);\
assert(coroParam->_sleep >= 0);\
@@ -109,17 +111,22 @@ public:
switch(coroParam->_line) { case 0:;
#define CORO_END_CODE \
+ if (&coroParam == &nullContext) nullContext = NULL; \
}
-#define CORO_SLEEP(delay) \
- do {\
+#define CORO_SLEEP(delay) do {\
coroParam->_line = __LINE__;\
coroParam->_sleep = delay;\
+ assert(&coroParam != &nullContext);\
return; case __LINE__:;\
} while (0)
+#define CORO_GIVE_WAY do { g_scheduler->giveWay(); CORO_SLEEP(1); } while (0)
+#define CORO_RESCHEDULE do { g_scheduler->reschedule(); CORO_SLEEP(1); } while (0)
+
/** Stop the currently running coroutine */
-#define CORO_KILL_SELF() do { coroParam->_sleep = -1; return; } while(0)
+#define CORO_KILL_SELF() \
+ do { if (&coroParam != &nullContext) { coroParam->_sleep = -1; } return; } while(0)
/** Invoke another coroutine */
#define CORO_INVOKE_ARGS(subCoro, ARGS) \
@@ -130,9 +137,22 @@ public:
subCoro ARGS;\
if (!coroParam->_subctx) break;\
coroParam->_sleep = coroParam->_subctx->_sleep;\
+ assert(&coroParam != &nullContext);\
return; case __LINE__:;\
} while(1);\
} while (0)
+#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS) \
+ do {\
+ coroParam->_line = __LINE__;\
+ coroParam->_subctx = 0;\
+ do {\
+ subCoro ARGS;\
+ if (!coroParam->_subctx) break;\
+ coroParam->_sleep = coroParam->_subctx->_sleep;\
+ assert(&coroParam != &nullContext);\
+ return RESULT; case __LINE__:;\
+ } while(1);\
+ } while (0)
#define CORO_INVOKE_0(subCoroutine) \
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX))
@@ -140,6 +160,8 @@ public:
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0))
#define CORO_INVOKE_2(subCoroutine, a0,a1) \
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1))
+#define CORO_INVOKE_3(subCoroutine, a0,a1,a2) \
+ CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1,a2))
} // end of namespace Tinsel
diff --git a/engines/tinsel/cursor.cpp b/engines/tinsel/cursor.cpp
index f933b2dd79..719b55d1bb 100644
--- a/engines/tinsel/cursor.cpp
+++ b/engines/tinsel/cursor.cpp
@@ -34,11 +34,14 @@
#include "tinsel/film.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
-#include "tinsel/inventory.h"
+#include "tinsel/dialogs.h"
#include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/pid.h"
+#include "tinsel/play.h"
#include "tinsel/sched.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h" // resetidletime()
#include "tinsel/tinsel.h" // For engine access
@@ -54,20 +57,21 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
-static OBJECT *McurObj = 0; // Main cursor object
-static OBJECT *AcurObj = 0; // Auxiliary cursor object
+static OBJECT *McurObj = NULL; // Main cursor object
+static OBJECT *AcurObj = NULL; // Auxiliary cursor object
static ANIM McurAnim = {0,0,0,0,0}; // Main cursor animation structure
static ANIM AcurAnim = {0,0,0,0,0}; // Auxiliary cursor animation structure
-static bool bHiddenCursor = false; // Set when cursor is hidden
+static bool bHiddenCursor = false; // Set when cursor is hidden
static bool bTempNoTrailers = false; // Set when cursor trails are hidden
+static bool bTempHide = false; // Set when cursor is hidden
static bool bFrozenCursor = false; // Set when cursor position is frozen
static frac_t IterationSize = 0;
-static SCNHANDLE CursorHandle = 0; // Handle to cursor reel data
+static SCNHANDLE hCursorFilm = 0; // Handle to cursor reel data
static int numTrails = 0;
static int nextTrail = 0;
@@ -76,8 +80,11 @@ static bool bWhoa = false; // Set by DropCursor() at the end of a scene
// - causes cursor processes to do nothing
// Reset when main cursor has re-initialised
-static bool restart = false; // When main cursor has been bWhoa-ed, it waits
- // for this to be set to true.
+static uint16 restart = 0; // When main cursor has been bWhoa-ed, it waits
+ // for this to be set to 0x8000.
+ // Main cursor sets all the bits after a re-start
+ // - each cursor trail examines it's own bit
+ // to trigger a trail restart.
static short ACoX = 0, ACoY = 0; // Auxillary cursor image's animation offsets
@@ -97,7 +104,7 @@ static int lastCursorX = 0, lastCursorY = 0;
//----------------- FORWARD REFERENCES --------------------
-static void MoveCursor(void);
+static void DoCursorMove(void);
/**
* Initialise and insert a cursor trail object, set its Z-pos, and hide
@@ -117,9 +124,9 @@ static void InitCurTrailObj(int i, int x, int y) {
if (ntrailData[i].trailObj != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), ntrailData[i].trailObj);
- pim = GetImageFromFilm(CursorHandle, i+1, &pfr, &pmi, &pfilm);// Get pointer to image
- assert(BackPal()); // No background palette
- pim->hImgPal = TO_LE_32(BackPal());
+ pim = GetImageFromFilm(hCursorFilm, i+1, &pfr, &pmi, &pfilm);// Get pointer to image
+ assert(BgPal()); // No background palette
+ pim->hImgPal = TO_LE_32(BgPal());
// Initialise and insert the object, set its Z-pos, and hide it
ntrailData[i].trailObj = MultiInitObject(pmi);
@@ -140,8 +147,8 @@ static bool GetDriverPosition(int *x, int *y) {
*x = ptMouse.x;
*y = ptMouse.y;
- return(*x >= 0 && *x <= SCREEN_WIDTH-1 &&
- *y >= 0 && *y <= SCREEN_HEIGHT-1);
+ return(*x >= 0 && *x <= SCREEN_WIDTH - 1 &&
+ *y >= 0 && *y <= SCREEN_HEIGHT - 1);
}
/**
@@ -154,7 +161,7 @@ void AdjustCursorXY(int deltaX, int deltaY) {
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(x + deltaX, y + deltaY));
}
- MoveCursor();
+ DoCursorMove();
}
/**
@@ -170,7 +177,7 @@ void SetCursorXY(int newx, int newy) {
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy));
- MoveCursor();
+ DoCursorMove();
}
/**
@@ -181,7 +188,7 @@ void SetCursorScreenXY(int newx, int newy) {
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy));
- MoveCursor();
+ DoCursorMove();
}
/**
@@ -229,7 +236,7 @@ void RestoreMainCursor(void) {
const FILM *pfilm;
if (McurObj != NULL) {
- pfilm = (const FILM *)LockMem(CursorHandle);
+ pfilm = (const FILM *)LockMem(hCursorFilm);
InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfilm->reels->script), ONE_SECOND / FROM_LE_32(pfilm->frate));
StepAnimScript(&McurAnim);
@@ -282,6 +289,13 @@ void FreezeCursor(void) {
}
/**
+ * Freeze the cursor, or not.
+ */
+void DoFreezeCursor(bool bFreeze) {
+ bFrozenCursor = bFreeze;
+}
+
+/**
* HideCursorTrails
*/
void HideCursorTrails(void) {
@@ -365,11 +379,12 @@ void SetAuxCursor(SCNHANDLE hFilm) {
GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear
pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image
- assert(BackPal()); // no background palette
- pim->hImgPal = TO_LE_32(BackPal()); // Poke in the background palette
+ assert(BgPal()); // no background palette
+ pim->hImgPal = TO_LE_32(BgPal()); // Poke in the background palette
ACoX = (short)(FROM_LE_16(pim->imgWidth)/2 - ((int16) FROM_LE_16(pim->anioffX)));
- ACoY = (short)(FROM_LE_16(pim->imgHeight)/2 - ((int16) FROM_LE_16(pim->anioffY)));
+ ACoY = (short)((FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK)/2 -
+ ((int16) FROM_LE_16(pim->anioffY)));
// Initialise and insert the auxillary cursor object
AcurObj = MultiInitObject(pmi);
@@ -387,7 +402,7 @@ void SetAuxCursor(SCNHANDLE hFilm) {
/**
* MoveCursor
*/
-static void MoveCursor(void) {
+static void DoCursorMove(void) {
int startX, startY;
Common::Point ptMouse;
frac_t newX, newY;
@@ -459,22 +474,30 @@ static void MoveCursor(void) {
* Initialise cursor object.
*/
static void InitCurObj(void) {
- const FILM *pfilm;
+ const FILM *pFilm;
const FREEL *pfr;
const MULTI_INIT *pmi;
IMAGE *pim;
- pim = GetImageFromFilm(CursorHandle, 0, &pfr, &pmi, &pfilm);// Get pointer to image
- assert(BackPal()); // no background palette
- pim->hImgPal = TO_LE_32(BackPal());
-//---
+ if (TinselV2) {
+ pFilm = (const FILM *)LockMem(hCursorFilm);
+ pfr = (const FREEL *)&pFilm->reels[0];
+ pmi = (MULTI_INIT *)LockMem(pfr->mobj);
- AcurObj = NULL; // No auxillary cursor
+ PokeInPalette(pmi);
+ } else {
+ assert(BgPal()); // no background palette
+
+ pim = GetImageFromFilm(hCursorFilm, 0, &pfr, &pmi, &pFilm);// Get pointer to image
+ pim->hImgPal = TO_LE_32(BgPal());
+
+ AcurObj = NULL; // No auxillary cursor
+ }
McurObj = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), McurObj);
- InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pfilm->frate));
+ InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pFilm->frate));
}
/**
@@ -486,7 +509,7 @@ static void InitCurPos(void) {
lastCursorY = ptMouse.y;
MultiSetZPosition(McurObj, Z_CURSOR);
- MoveCursor();
+ DoCursorMove();
MultiHideObject(McurObj);
IterationSize = ITERATION_BASE;
@@ -505,16 +528,16 @@ static void CursorStoppedCheck(CORO_PARAM) {
// If scene is closing down
if (bWhoa) {
// ...wait for next scene start-up
- while (!restart)
+ while (restart != 0x8000)
CORO_SLEEP(1);
// Re-initialise
InitCurObj();
InitCurPos();
- InventoryIconCursor(); // May be holding something
+ InventoryIconCursor(false); // May be holding something
// Re-start the cursor trails
- restart = false; // set all bits
+ restart = (uint16)-1; // set all bits
bWhoa = false;
}
CORO_END_CODE;
@@ -530,15 +553,15 @@ void CursorProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CODE(_ctx);
- while (!CursorHandle || !BackPal())
+ while (!hCursorFilm || !BgPal())
CORO_SLEEP(1);
InitCurObj();
InitCurPos();
- InventoryIconCursor(); // May be holding something
+ InventoryIconCursor(false); // May be holding something
bWhoa = false;
- restart = false;
+ restart = 0;
while (1) {
// allow rescheduling
@@ -562,10 +585,10 @@ void CursorProcess(CORO_PARAM, const void *) {
// Move the cursor as appropriate
if (!bFrozenCursor)
- MoveCursor();
+ DoCursorMove();
// If the cursor should be hidden...
- if (bHiddenCursor) {
+ if (bHiddenCursor || bTempHide) {
// ...hide the cursor object(s)
MultiHideObject(McurObj);
if (AcurObj)
@@ -595,9 +618,9 @@ void CursorProcess(CORO_PARAM, const void *) {
void DwInitCursor(SCNHANDLE bfilm) {
const FILM *pfilm;
- CursorHandle = bfilm;
+ hCursorFilm = bfilm;
- pfilm = (const FILM *)LockMem(CursorHandle);
+ pfilm = (const FILM *)LockMem(hCursorFilm);
numTrails = FROM_LE_32(pfilm->numreels) - 1;
assert(numTrails <= MAX_TRAILERS);
@@ -607,6 +630,15 @@ void DwInitCursor(SCNHANDLE bfilm) {
* DropCursor is called when a scene is closing down.
*/
void DropCursor(void) {
+ if (TinselV2) {
+ if (AcurObj)
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), AcurObj);
+ if (McurObj)
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), McurObj);
+
+ restart = 0;
+ }
+
AcurObj = NULL; // No auxillary cursor
McurObj = NULL; // No cursor object (imminently deleted elsewhere)
bHiddenCursor = false; // Not hidden in next scene
@@ -625,7 +657,7 @@ void DropCursor(void) {
* RestartCursor is called when a new scene is starting up.
*/
void RestartCursor(void) {
- restart = true; // Get the main cursor to re-initialise
+ restart = 0x8000; // Get the main cursor to re-initialise
}
/**
@@ -639,10 +671,22 @@ void RebootCursor(void) {
bHiddenCursor = bTempNoTrailers = bFrozenCursor = false;
- CursorHandle = 0;
+ hCursorFilm = 0;
bWhoa = false;
- restart = false;
+ restart = 0;
+}
+
+void StartCursorFollowed(void) {
+ DelAuxCursor();
+
+ if (!SysVar(SV_ENABLEPRINTCURSOR))
+ bTempHide = true;
+}
+
+void EndCursorFollowed(void) {
+ InventoryIconCursor(false); // May be holding something
+ bTempHide = false;
}
} // end of namespace Tinsel
diff --git a/engines/tinsel/cursor.h b/engines/tinsel/cursor.h
index 15349dda26..bf1f0d064b 100644
--- a/engines/tinsel/cursor.h
+++ b/engines/tinsel/cursor.h
@@ -42,6 +42,7 @@ void SetTempCursor(SCNHANDLE pScript);
void DwHideCursor(void);
void UnHideCursor(void);
void FreezeCursor(void);
+void DoFreezeCursor(bool bFreeze);
void HideCursorTrails(void);
void UnHideCursorTrails(void);
void DelAuxCursor(void);
@@ -50,6 +51,8 @@ void DwInitCursor(SCNHANDLE bfilm);
void DropCursor(void);
void RestartCursor(void);
void RebootCursor(void);
+void StartCursorFollowed(void);
+void EndCursorFollowed(void);
} // end of namespace Tinsel
diff --git a/engines/tinsel/debugger.cpp b/engines/tinsel/debugger.cpp
index dc37e6a9a1..3ade360445 100644
--- a/engines/tinsel/debugger.cpp
+++ b/engines/tinsel/debugger.cpp
@@ -25,7 +25,7 @@
#include "tinsel/tinsel.h"
#include "tinsel/debugger.h"
-#include "tinsel/inventory.h"
+#include "tinsel/dialogs.h"
#include "tinsel/pcode.h"
#include "tinsel/scene.h"
#include "tinsel/sound.h"
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 45cd903552..e56198c76c 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -31,7 +31,6 @@
#include "tinsel/tinsel.h"
#include "tinsel/savescn.h" // needed by TinselMetaEngine::listSaves
-
namespace Tinsel {
struct TinselGameDescription {
@@ -68,7 +67,7 @@ uint16 TinselEngine::getVersion() const {
static const PlainGameDescriptor tinselGames[] = {
{"tinsel", "Tinsel engine game"},
{"dw", "Discworld"},
- {"dw2", "Discworld 2: Mortality Bytes!"},
+ {"dw2", "Discworld 2: Missing Presumed ...!?"},
{0, 0}
};
@@ -77,25 +76,15 @@ namespace Tinsel {
static const TinselGameDescription gameDescriptions[] = {
- // The DW1 demo was based on an older revision of the Tinsel engine
- // than the one used in the released game. We call it Tinsel v0 as
- // opposed to v1 which was used in the full retail version of DW.
-
- { // Demo from http://www.adventure-treff.de/specials/dl_demos.php
- {
- "dw",
- "Demo",
- AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
- //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
- Common::EN_ANY,
- Common::kPlatformPC,
- Common::ADGF_DEMO
- },
- GID_DW1,
- 0,
- GF_DEMO,
- TINSEL_V0,
- },
+ // Note: The following is the (hopefully) definitive list of version details:
+ // TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
+ // of the Tinsel engine and graphics compression, and isn't currently supported
+ // TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
+ // files, and the second used .SCN files. The second also provided some fixes to
+ // various script bugs and coding errors, but is still considered TINSEL_V1,
+ // as both game versions work equally well with the newer code.
+ // TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
+ // and as far as we know there aren't any variations of this engine.
{
{ // This version has *.gra files
@@ -112,49 +101,6 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
- { // Multilingual floppy with *.gra files.
- // Note: It contains no english subtitles.
- // Reported on our forums.
- {
- "dw",
- "Floppy",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- Common::ADGF_DROPLANGUAGE
- },
- GID_DW1,
- 0,
- GF_FLOPPY | GF_USE_4FLAGS,
- TINSEL_V1,
- },
-
- { // English CD. This version has *.gra files
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- Common::ADGF_NO_FLAGS
- },
- GID_DW1,
- 0,
- GF_CD,
- TINSEL_V1,
- },
-
{ // Multilingual CD with english speech and *.gra files.
// Note: It contains no english subtitles.
{
@@ -245,7 +191,7 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
- { // English CD with SCN files
+ { // English CD v2
{
"dw",
"CD",
@@ -265,7 +211,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
#if 0
- { // English Saturn CD. Not (yet?) supported
+ { // English Saturn CD
{
"dw",
"CD",
@@ -285,6 +231,25 @@ static const TinselGameDescription gameDescriptions[] = {
},
#endif
+// Currently disabled since it isn't really supported
+#if 0
+ { // Demo from http://www.adventure-treff.de/specials/dl_demos.php
+ {
+ "dw",
+ "Demo",
+ AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
+ //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_DEMO
+ },
+ GID_DW1,
+ 0,
+ GF_DEMO,
+ TINSEL_V0,
+ },
+#endif
+
{ // German CD re-release "Neon Edition"
// Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
{
@@ -301,6 +266,44 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
+ { // Europen/Australian Discworld 2 release
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // German Discworld 2 re-release "Neon Edition"
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"german1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
{ AD_TABLE_END_MARKER, 0, 0, 0, 0 }
};
@@ -334,25 +337,23 @@ public:
}
virtual const char *getCopyright() const {
- // FIXME: Bad copyright string.
- // Should be something like "Tinsel (C) Psygnosis" or so... ???
- return "Tinsel Engine";
+ return "Tinsel (C) Psygnosis";
}
virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const;
virtual SaveStateList listSaves(const char *target) const;
- virtual int getMaximumSaveSlot() const;
};
bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
- return
- (f == kSupportsListSaves);
+ return (f == kSupportsListSaves);
}
namespace Tinsel {
+
extern int getList(Common::SaveFileManager *saveFileMan, const Common::String &target);
+
}
SaveStateList TinselMetaEngine::listSaves(const char *target) const {
@@ -368,8 +369,6 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
return saveList;
}
-int TinselMetaEngine::getMaximumSaveSlot() const { return 999; }
-
bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const {
const Tinsel::TinselGameDescription *gd = (const Tinsel::TinselGameDescription *)desc;
if (gd) {
diff --git a/engines/tinsel/inventory.cpp b/engines/tinsel/dialogs.cpp
index d20ada51ac..cc844278fb 100644
--- a/engines/tinsel/inventory.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -29,31 +29,38 @@
* And there's still a bit of tidying and commenting to do yet.
*/
+//#define USE_3FLAGS 1
+
#include "tinsel/actors.h"
#include "tinsel/anim.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
+#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/film.h"
#include "tinsel/font.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
-#include "tinsel/inventory.h"
+#include "tinsel/heapmem.h"
+#include "tinsel/dialogs.h"
#include "tinsel/multiobj.h"
#include "tinsel/music.h"
+#include "tinsel/palette.h"
+#include "tinsel/pcode.h"
+#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/savescn.h"
#include "tinsel/sched.h"
#include "tinsel/serializer.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
+#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
+#include "tinsel/tinlib.h"
#include "tinsel/tinsel.h" // For engine access
#include "tinsel/token.h"
-#include "tinsel/pcode.h"
-#include "tinsel/pid.h"
namespace Tinsel {
@@ -73,17 +80,14 @@ extern int volMaster;
// Tag functions in PDISPLAY.C
extern void EnableTags(void);
extern void DisableTags(void);
-
+extern void DisablePointing(CORO_PARAM);
+extern void EnablePointing(void);
//----------------- LOCAL DEFINES --------------------
-//#define ALL_CURSORS
-
-#define INV_PICKUP BE_SLEFT // Local names
-#define INV_LOOK BE_SRIGHT // for button events
-#define INV_ACTION BE_DLEFT //
-
-
+#define INV_PICKUP PLR_SLEFT // Local names
+#define INV_LOOK PLR_SRIGHT // for button events
+#define INV_ACTION PLR_DLEFT //
// For SlideSlider() and similar
enum SSFN {
S_START, S_SLIDE, S_END, S_TIMEUP, S_TIMEDN
@@ -106,56 +110,102 @@ enum {
};
//-----------------------
-// Indices into winPartsf's reels
-#define IX_SLIDE 0 // Slider
-#define IX_V26 1
-#define IX_V52 2
-#define IX_V78 3
-#define IX_V104 4
-#define IX_V130 5
-#define IX_H26 6
-#define IX_H52 7
-#define IX_H78 8
-#define IX_H104 9
-#define IX_H130 10
-#define IX_H156 11
-#define IX_H182 12
-#define IX_H208 13
-#define IX_H234 14
-#define IX_TL 15 // Top left corner
-#define IX_TR 16 // Top right corner
-#define IX_BL 17 // Bottom left corner
-#define IX_BR 18 // Bottom right corner
-#define IX_H25 19
-#define IX_V11 20
-#define IX_RTL 21 // Re-sizing top left corner
-#define IX_RTR 22 // Re-sizing top right corner
-#define IX_RBR 23 // Re-sizing bottom right corner
-#define IX_CURLR 24 // }
-#define IX_CURUD 25 // }
-#define IX_CURDU 26 // } Custom cursors
-#define IX_CURDD 27 // }
-#define IX_CURUP 28 // }
-#define IX_CURDOWN 29 // }
-#define IX_MDGROOVE 30 // 'Mixing desk' slider background
-#define IX_MDSLIDER 34 // 'Mixing desk' slider
-
-#define IX_BLANK1 35 //
-#define IX_BLANK2 36 //
-#define IX_BLANK3 37 //
-#define IX_CIRCLE1 38 //
-#define IX_CIRCLE2 39 //
-#define IX_CROSS1 40 //
-#define IX_CROSS2 41 //
-#define IX_CROSS3 42 //
-#define IX_QUIT1 43 //
-#define IX_QUIT2 44 //
-#define IX_QUIT3 45 //
-#define IX_TICK1 46 //
-#define IX_TICK2 47 //
-#define IX_TICK3 48 //
-#define IX_NTR 49 // New top right corner
-#define HOPEDFORREELS 50
+// Indices into hWinParts's reels
+
+enum PARTS_INDEX {
+ IX_SLIDE = 0, // Slider
+ IX_V26 = 1,
+ IX_V52 = 2,
+ IX_V78 = 3,
+ IX_V104 = 4,
+ IX_V130 = 5,
+ IX_H26 = 6,
+ IX_H52 = 7,
+ IX_H78 = 8,
+ IX_H104 = 9,
+ IX_H130 = 10,
+ IX_H156 = 11,
+ IX_H182 = 12,
+ IX_H208 = 13,
+ IX_H234 = 14,
+ IX_TL = 15, // Top left corner
+ IX_TR = 16, // Top right corner
+ IX_BL = 17, // Bottom left corner
+ IX_BR = 18, // Bottom right corner
+
+ IX1_H25 = 19,
+ IX1_V11 = 20,
+ IX1_RTL = 21, // Re-sizing top left corner
+ IX1_RTR = 22, // Re-sizing top right corner
+ IX1_RBR = 23, // Re-sizing bottom right corner
+ IX1_CURLR = 24, // }
+ IX1_CURUD = 25, // }
+ IX1_CURDU = 26, // } Custom cursors
+ IX1_CURDD = 27, // }
+ IX1_CURUP = 28, // }
+ IX1_CURDOWN = 29, // }
+ IX1_MDGROOVE = 30, // 'Mixing desk' slider background
+ IX1_MDSLIDER= 34, // 'Mixing desk' slider
+ IX1_BLANK1 = 35, //
+ IX1_BLANK2 = 36, //
+ IX1_BLANK3 = 37, //
+ IX1_CIRCLE1 = 38, //
+ IX1_CIRCLE2 = 39, //
+ IX1_CROSS1 = 40, //
+ IX1_CROSS2 = 41, //
+ IX1_CROSS3 = 42, //
+ IX1_QUIT1 = 43, //
+ IX1_QUIT2 = 44, //
+ IX1_QUIT3 = 45, //
+ IX1_TICK1 = 46, //
+ IX1_TICK2 = 47, //
+ IX1_TICK3 = 48, //
+ IX1_NTR = 49, // New top right corner
+
+ IX2_RTL = 19, // Re-sizing top left corner
+ IX2_RTR = 20, // Re-sizing top right corner
+ IX2_RBR = 21, // Re-sizing bottom right corner
+ IX2_CURLR = 22, // }
+ IX2_CURUD = 23, // }
+ IX2_CURDU = 24, // } Custom cursors
+ IX2_CURDD = 25, // }
+ IX2_MDGROOVE = 26, // 'Mixing desk' slider background
+ IX2_MDSLIDER = 27, // 'Mixing desk' slider
+ IX2_CIRCLE1 = 28, //
+ IX2_CIRCLE2 = 29, //
+ IX2_CROSS1 = 30, //
+ IX2_CROSS2 = 31, //
+ IX2_CROSS3 = 32, //
+ IX2_TICK1 = 33, //
+ IX2_TICK2 = 34, //
+ IX2_TICK3 = 35, //
+ IX2_NTR = 36, // New top right corner
+ IX2_TR4 = 37,
+ IX2_LEFT1 = 38,
+ IX2_LEFT2 = 39,
+ IX2_RIGHT1 = 40,
+ IX2_RIGHT2 = 41,
+
+ T1_HOPEDFORREELS = 50,
+ T2_HOPEDFORREELS = 42
+};
+
+// The following defines select the correct constant depending on Tinsel version
+#define IX_CROSS1 (TinselV2 ? IX2_CROSS1 : IX1_CROSS1)
+#define IX_CURDD (TinselV2 ? IX2_CURDD : IX1_CURDD)
+#define IX_CURDU (TinselV2 ? IX2_CURDU : IX1_CURDU)
+#define IX_CURLR (TinselV2 ? IX2_CURLR : IX1_CURLR)
+#define IX_CURUD (TinselV2 ? IX2_CURUD : IX1_CURUD)
+#define IX_CURUL (TinselV2 ? IX2_CURUL : IX1_CURUL)
+#define IX_MDGROOVE (TinselV2 ? IX2_MDGROOVE : IX1_MDGROOVE)
+#define IX_MDSLIDER (TinselV2 ? IX2_MDSLIDER : IX1_MDSLIDER)
+#define IX_NTR (TinselV2 ? IX2_NTR : IX1_NTR)
+#define IX_RBR (TinselV2 ? IX2_RBR : IX1_RBR)
+#define IX_RTL (TinselV2 ? IX2_RTL : IX1_RTL)
+#define IX_RTR (TinselV2 ? IX2_RTR : IX1_RTR)
+#define IX_TICK1 (TinselV2 ? IX2_TICK1 : IX1_TICK1)
+
+
#define NORMGRAPH 0
#define DOWNGRAPH 1
@@ -170,14 +220,52 @@ enum {
#define HOPEDFORFREELS 6 // Expected flag reels
//-----------------------
-#define MAX_ININV 150 // Max in an inventory
-#define MAX_CONVBASIC 10 // Max permanent conversation icons
+#define MAX_ININV (TinselV2 ? 160 : 150) // Max in an inventory
+#define MAX_ININV_TOT 160
+#define MAX_PERMICONS 10 // Max permanent conversation icons
#define MAXHICONS 10 // Max dimensions of
#define MAXVICONS 6 // an inventory window
-#define ITEM_WIDTH 25 // Dimensions of an icon
-#define ITEM_HEIGHT 25 //
+#define ITEM_WIDTH (TinselV2 ? 50 : 25) // Dimensions of an icon
+#define ITEM_HEIGHT (TinselV2 ? 50 : 25) //
+#define I_SEPARATION (TinselV2 ? 2 : 1) // Item separation
+
+#define NM_TOFF 11 // Title text Y offset from top
+#define NM_TBT (TinselV2 ? 4 : 0) // Y, title box top
+#define NM_TBB 33
+#define NM_LSX (TinselV2 ? 4 : 0) // X, left side
+#define NM_BSY (TinselV2 ? -9 : - M_TH + 1)
+#define NM_RSX (TinselV2 ? -9 : - M_SW + 1)
+#define NM_SBL (-27)
+#define NM_SLH (TinselV2 ? 11 : 5) // Slider height
+#define NM_SLX (-11) // Slider X offset (from right)
+
+#define NM_BG_POS_X (TinselV2 ? 9 : 1) // }
+#define NM_BG_POS_Y (TinselV2 ? 9 : 1) // } Offset of translucent rectangle
+#define NM_BG_SIZ_X (TinselV2 ? -18 : -3) // }
+#define NM_BG_SIZ_Y (TinselV2 ? -18 : -3) // } How much larger it is than edges
+
+#define NM_RS_T_INSET 3
+#define NM_RS_B_INSET 4
+#define NM_RS_L_INSET 3
+#define NM_RS_R_INSET 4
+#define NM_RS_THICKNESS 5
+#define NM_MOVE_AREA_B_Y 30
+#define NM_SLIDE_INSET (TinselV2 ? 18 : 15) // X offset (from right) of left of scroll region
+#define NM_SLIDE_THICKNESS (TinselV2 ? 13 : 4) // thickness of scroll region
+#define NM_UP_ARROW_TOP 34 // Y offset of top of up arrow
+#define NM_UP_ARROW_BOTTOM 49 // Y offset of bottom of up arrow
+#define NM_DN_ARROW_TOP 22 // Y offset (from bottom) of top of down arrow
+#define NM_DN_ARROW_BOTTOM 5 // Y offset (from bottom) of bottom of down arrow
+
+#define MD_YBUTTOP (TinselV2 ? 2 : 9)
+#define MD_YBUTBOT (TinselV2 ? 16 : 0)
+#define MD_XLBUTL (TinselV2 ? 4 : 1)
+#define MD_XLBUTR (TinselV2 ? 26 : 10)
+#define MD_XRBUTL (TinselV2 ? 173 : 105)
+#define MD_XRBUTR (TinselV2 ? 195 : 114)
+#define ROTX1 60 // Rotate button's offsets from the centre
// Number of objects that makes up an empty window
#define MAX_WCOMP 21 // 4 corners + (3+3) sides + (2+2) extra sides
@@ -186,6 +274,19 @@ enum {
#define MAX_ICONS MAXHICONS*MAXVICONS
+#define MAX_NAME_RIGHT (TinselV2 ? 417 : 213)
+
+#define SLIDE_RANGE (TinselV2 ? 120 : 81)
+#define SLIDE_MINX (TinselV2 ? 25 : 8)
+#define SLIDE_MAXX (TinselV2 ? 25 + 120 : 8 + 81)
+
+#define MDTEXT_YOFF (TinselV2 ? -1 : 6)
+#define MDTEXT_XOFF -4
+#define TOG2_YOFF -22
+#define ROT_YOFF 48
+#define TYOFF (TinselV2 ? 4 : 0)
+#define FLAGX (-5)
+#define FLAGY 4
//----------------- LOCAL GLOBAL DATA --------------------
@@ -218,13 +319,15 @@ static const int vFillers[MAXVICONS] = {
//----- Permanent data (set once) -----
-static SCNHANDLE winPartsf = 0; // Window members and cursors' graphic data
+static SCNHANDLE hWinParts = 0; // Window members and cursors' graphic data
static SCNHANDLE flagFilm = 0; // Window members and cursors' graphic data
static SCNHANDLE configStrings[20];
-static INV_OBJECT *pio = 0; // Inventory objects' data
-static int numObjects = 0; // Number of inventory objects
-
+static INV_OBJECT *invObjects = NULL; // Inventory objects' data
+static int numObjects = 0; // Number of inventory objects
+static SCNHANDLE *invFilms = NULL;
+static bool bNoLanguage = false;
+static DIRECTION initialDirection;
//----- Permanent data (updated, valid while inventory closed) -----
@@ -232,6 +335,8 @@ static enum {NO_INV, IDLE_INV, ACTIVE_INV, BOGUS_INV} InventoryState;
static int HeldItem = INV_NOICON; // Current held item
+static SCNHANDLE heldFilm;
+
struct INV_DEF {
int MinHicons; // }
@@ -242,7 +347,7 @@ struct INV_DEF {
int NoofHicons; // }
int NoofVicons; // } Current dimentsions
- int ItemOrder[MAX_ININV]; // Contained items
+ int contents[160]; // Contained items
int NoofItems; // Current number of held items
int FirstDisp; // Index to first item currently displayed
@@ -257,7 +362,7 @@ struct INV_DEF {
SCNHANDLE hInvTitle; // Window heading
bool resizable; // Re-sizable window?
- bool moveable; // Moveable window?
+ bool bMoveable; // Moveable window?
int sNoofHicons; // }
int sNoofVicons; // } Current dimensions
@@ -270,10 +375,9 @@ static INV_DEF InvD[NUM_INV]; // Conversation + 2 inventories + ...
// Permanent contents of conversation inventory
-static int Inv0Order[MAX_CONVBASIC]; // Basic items i.e. permanent contents
-static int Num0Order = 0; // - copy to conv. inventory at pop-up time
-
-
+static int permIcons[MAX_PERMICONS]; // Basic items i.e. permanent contents
+static int numPermIcons = 0; // - copy to conv. inventory at pop-up time
+static int numEndIcons = 0;
//----- Data pertinant to current active inventory -----
@@ -297,14 +401,14 @@ static int Xcompensate = 0; //
static bool ItemsChanged = 0; // When set, causes items to be re-drawn
-static bool bOpenConf = 0;
+static bool bReOpenMenu = 0;
static int TL = 0, TR = 0, BL = 0, BR = 0; // Used during window construction
static int TLwidth = 0, TLheight = 0; //
static int TRwidth = 0; //
static int BLheight = 0; //
-
+static LANGUAGE displayedLanguage;
static OBJECT *objArray[MAX_WCOMP]; // Current display objects (window)
static OBJECT *iconArray[MAX_ICONS]; // Current display objects (icons)
@@ -314,11 +418,13 @@ static OBJECT *DobjArray[MAX_WCOMP]; // Current display objects (re-sizing windo
static OBJECT *RectObject = 0, *SlideObject = 0; // Current display objects, for reference
// objects are in objArray.
-static int slideY = 0; // For positioning the slider
-static int slideYmax = 0, slideYmin = 0; //
+static int sliderYpos = 0; // For positioning the slider
+static int sliderYmax = 0, sliderYmin = 0; //
+
+#define sliderRange (sliderYmax - sliderYmin)
// Also to do with the slider
-static struct { int n; int y; } slideStuff[MAX_ININV+1];
+static struct { int n; int y; } slideStuff[MAX_ININV_TOT+1];
#define MAXSLIDES 4
struct MDSLIDES {
@@ -331,13 +437,22 @@ static int numMdSlides = 0;
static int GlitterIndex = 0;
+// Icon clicked on to cause an event
+// - Passed to conversation polygon or actor code via Topic()
+// - (sometimes) Passed to inventory icon code via OtherObject()
+static int thisIcon = 0;
+
+static CONV_PARAM thisConvFn; // Top, 'Middle' or Bottom
static HPOLYGON thisConvPoly = 0; // Conversation code is in a polygon code block
-static int thisConvIcon = 0; // Passed to polygon code via convIcon()
+static int thisConvActor; // ...or an actor's code block.
static int pointedIcon = INV_NOICON; // used by InvLabels - icon pointed to on last call
-static volatile int PointedWaitCount = 0; // used by InvTinselProcess - fix the 'repeated pressing bug'
+static volatile int PointedWaitCount = 0; // used by ObjectProcess - fix the 'repeated pressing bug'
static int sX = 0; // used by SlideMSlider() - current x-coordinate
static int lX = 0; // used by SlideMSlider() - last x-coordinate
+static bool bMoveOnUnHide; // Set before start of conversation
+ // - causes conversation to be started in a sensible place
+
//----- Data pertinant to configure (incl. load/save game) -----
#define COL_MAINBOX TBLUE1 // Base blue colour
@@ -353,52 +468,102 @@ static int lX = 0; // used by SlideMSlider() - last x-coordinate
#endif
#define EDIT_BOX2_WIDTH 166
-// RGROUP Radio button group - 1 is selectable at a time. Action on double click
-// ARSBUT Action if a radio button is selected
-// AABUT Action always
-// AATBUT Action always, text box
-// AAGBUT Action always, graphic button
-// SLIDER Not a button at all
+#define T2_EDIT_BOX1_WIDTH 290
+#define T2_EDIT_BOX2_WIDTH 322
+#define T2_BOX_HEIGHT 26
+
+//----- Data pertinant to scene hoppers ------------------------
+
+#include "common/pack-start.h" // START STRUCT PACKING
+
+struct HOPPER {
+ uint32 hScene;
+ SCNHANDLE hSceneDesc;
+ uint32 numEntries;
+ uint32 entryIndex;
+} PACKED_STRUCT;
+typedef HOPPER *PHOPPER;
+
+struct HOPENTRY {
+ uint32 eNumber; // entrance number
+ SCNHANDLE hDesc; // handle to entrance description
+ uint32 flags;
+} PACKED_STRUCT;
+typedef HOPENTRY *PHOPENTRY;
+
+#include "common/pack-end.h" // END STRUCT PACKING
+
+static PHOPPER pHopper;
+static PHOPENTRY pEntries;
+static int numScenes;
+
+static int numEntries;
+
+static PHOPPER pChosenScene = NULL;
+
+static int lastChosenScene;
+static bool bRemember;
+
+//--------------------------------------------------------------
+
+
+
enum BTYPE {
- RGROUP, ARSBUT, AABUT, AATBUT, ARSGBUT, AAGBUT, SLIDER,
- TOGGLE, DCTEST, FLIP, FRGROUP, NOTHING
+ RGROUP, //!< Radio button group - 1 is selectable at a time. Action on double click
+ ARSBUT, //!< Action if a radio button is selected
+ AABUT, //!< Action always
+ AATBUT, //!< Action always, text box
+ ARSGBUT,
+ AAGBUT, //!< Action always, graphic button
+ SLIDER, //!< Not a button at all
+ TOGGLE, //!< Discworld 1 toggle
+ TOGGLE1, //!< Discworld 2 toggle type 1
+ TOGGLE2, //!< Discworld 2 toggle type 2
+ DCTEST,
+ FLIP,
+ FRGROUP,
+ ROTATE,
+ NOTHING
};
enum BFUNC {
- NOFUNC, SAVEGAME, LOADGAME, IQUITGAME, CLOSEWIN,
- OPENLOAD, OPENSAVE, OPENREST,
- OPENSOUND, OPENCONT,
+ NOFUNC,
+ SAVEGAME,
+ LOADGAME,
+ IQUITGAME,
+ CLOSEWIN,
+ OPENLOAD,
+ OPENSAVE,
+ OPENREST,
+ OPENSOUND,
+ OPENCONT,
+#ifndef JAPAN
OPENSUBT,
+#endif
OPENQUIT,
- INITGAME, MIDIVOL,
- CLANG, RLANG
+ INITGAME,
+ MUSICVOL,
+
+ HOPPER2, // Call up Scene Hopper 2
+ BF_CHANGESCENE,
+
+ CLANG,
+ RLANG
#ifdef MAC_OPTIONS
, MASTERVOL, SAMPVOL
#endif
};
-struct CONFBOX {
- BTYPE boxType;
- BFUNC boxFunc;
- char *boxText;
- int ixText;
- int xpos;
- int ypos;
- int w; // Doubles as max value for SLIDERs
- int h; // Doubles as iteration size for SLIDERs
- int *ival;
- int bi; // Base index for AAGBUTs
-};
-
-
-#define NO_HEADING (-1)
+#define NO_HEADING ((SCNHANDLE)-1)
#define USE_POINTER (-1)
#define SIX_LOAD_OPTION 0
#define SIX_SAVE_OPTION 1
#define SIX_RESTART_OPTION 2
#define SIX_SOUND_OPTION 3
#define SIX_CONTROL_OPTION 4
+#ifndef JAPAN
#define SIX_SUBTITLES_OPTION 5
+#endif
#define SIX_QUIT_OPTION 6
#define SIX_RESUME_OPTION 7
#define SIX_LOAD_HEADING 8
@@ -414,6 +579,36 @@ struct CONFBOX {
#define SIX_STITLE_TOGGLE 18
#define SIX_QUIT_HEADING 19
+enum TM {TM_POINTER, TM_INDEX, TM_STRINGNUM, TM_NONE};
+
+struct CONFBOX {
+ BTYPE boxType;
+ BFUNC boxFunc;
+ TM textMethod;
+
+ char *boxText;
+ int ixText;
+ int xpos;
+ int ypos;
+ int w; // Doubles as max value for SLIDERs
+ int h; // Doubles as iteration size for SLIDERs
+ int *ival;
+ int bi; // Base index for AAGBUTs
+};
+
+struct CONFINIT {
+ int h;
+ int v;
+ int x;
+ int y;
+ bool bExtraWin;
+ CONFBOX *Box;
+ int NumBoxes;
+ uint32 ixHeading;
+};
+
+#define BW 44 // Width of crosses and ticks etc. buttons
+#define BH 41 // Height of crosses and ticks etc. buttons
/*-------------------------------------------------------------*\
| This is the main menu (that comes up when you hit F1 on a PC) |
@@ -427,110 +622,196 @@ struct CONFBOX {
#define FBX 15 // x-offset of first button
#endif
-CONFBOX optionBox[] = {
+#define OPTX 33
+#define OPTY 30
+#define BOX_V_SEP 7
+
+#define BOXX 56 // X-position of text boxes
+#define BOXY 50 // Y-position of text boxes
+#define T2_OPTX 33
+#define T2_OPTY 36
+#define T2_BOX_V_SEP 12
+#define T2_BOX_V2_SEP 6
+
+CONFBOX t1OptionBox[] = {
- { AATBUT, OPENLOAD, NULL, SIX_LOAD_OPTION, FBX, FBY, EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
- { AATBUT, OPENSAVE, NULL, SIX_SAVE_OPTION, FBX, FBY + (BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
- { AATBUT, OPENREST, NULL, SIX_RESTART_OPTION, FBX, FBY + 2*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
- { AATBUT, OPENSOUND, NULL, SIX_SOUND_OPTION, FBX, FBY + 3*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
- { AATBUT, OPENCONT, NULL, SIX_CONTROL_OPTION, FBX, FBY + 4*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENLOAD, TM_NONE, NULL, SIX_LOAD_OPTION, FBX, FBY, EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENSAVE, TM_NONE, NULL, SIX_SAVE_OPTION, FBX, FBY + (BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENREST, TM_NONE, NULL, SIX_RESTART_OPTION, FBX, FBY + 2*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENSOUND, TM_NONE, NULL, SIX_SOUND_OPTION, FBX, FBY + 3*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENCONT, TM_NONE, NULL, SIX_CONTROL_OPTION, FBX, FBY + 4*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
#ifdef JAPAN
// TODO: If in JAPAN mode, simply disable the subtitles button?
{ AATBUT, OPENQUIT, NULL, SIX_QUIT_OPTION, FBX, FBY + 5*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
{ AATBUT, CLOSEWIN, NULL, SIX_RESUME_OPTION, FBX, FBY + 6*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 }
#else
- { AATBUT, OPENSUBT, NULL, SIX_SUBTITLES_OPTION,FBX, FBY + 5*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
- { AATBUT, OPENQUIT, NULL, SIX_QUIT_OPTION, FBX, FBY + 6*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
- { AATBUT, CLOSEWIN, NULL, SIX_RESUME_OPTION, FBX, FBY + 7*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 }
+ { AATBUT, OPENSUBT, TM_NONE, NULL, SIX_SUBTITLES_OPTION,FBX, FBY + 5*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENQUIT, TM_NONE, NULL, SIX_QUIT_OPTION, FBX, FBY + 6*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { AATBUT, CLOSEWIN, TM_NONE, NULL, SIX_RESUME_OPTION, FBX, FBY + 7*(BOX_HEIGHT + 2), EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 }
#endif
};
+CONFINIT t1ciOption = { 6, 5, 72, 23, false, t1OptionBox, ARRAYSIZE(t1OptionBox), NO_HEADING };
+
+CONFBOX t2OptionBox[] = {
+
+ { AATBUT, OPENLOAD, TM_INDEX, NULL, SS_LOAD_OPTION, T2_OPTX, T2_OPTY, T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENSAVE, TM_INDEX, NULL, SS_SAVE_OPTION, T2_OPTX, T2_OPTY + (T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENREST, TM_INDEX, NULL, SS_RESTART_OPTION, T2_OPTX, T2_OPTY + 2*(T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENSOUND, TM_INDEX, NULL, SS_SOUND_OPTION, T2_OPTX, T2_OPTY + 3*(T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { AATBUT, OPENQUIT, TM_INDEX, NULL, SS_QUIT_OPTION, T2_OPTX, T2_OPTY + 4*(T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 }
+
+};
+
+CONFINIT t2ciOption = { 6, 4, 144, 60, false, t2OptionBox, sizeof(t2OptionBox)/sizeof(CONFBOX), NO_HEADING };
+
+#define ciOption (TinselV2 ? t2ciOption : t1ciOption)
+#define optionBox (TinselV2 ? t2OptionBox : t1OptionBox)
+
/*-------------------------------------------------------------*\
| These are the load and save game menus. |
\*-------------------------------------------------------------*/
+#define NUM_RGROUP_BOXES 9
+
#ifdef JAPAN
-#define NUM_SL_RGROUP 7 // number of visible slots
+#define NUM_RGROUP_BOXES 7 // number of visible slots
#define SY 32 // y-position of first slot
#else
-#define NUM_SL_RGROUP 9 // number of visible slots
+#define NUM_RGROUP_BOXES 9 // number of visible slots
#define SY 31 // y-position of first slot
#endif
-CONFBOX loadBox[NUM_SL_RGROUP+2] = {
-
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY, EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + (BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 2*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 3*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 4*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 5*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 6*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+CONFBOX t1LoadBox[NUM_RGROUP_BOXES+2] = {
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY, EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + (BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 2*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 3*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 4*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 5*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 6*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
#ifndef JAPAN
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 7*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, LOADGAME, NULL, USE_POINTER, 28, SY + 8*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 7*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 8*(BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
#endif
- { ARSGBUT, LOADGAME, NULL, USE_POINTER, 230, 44, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, CLOSEWIN, NULL, USE_POINTER, 230, 44+47, 23, 19, NULL, IX_CROSS1 }
+ { ARSGBUT, LOADGAME, TM_NONE, NULL, USE_POINTER, 230, 44, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 230, 44+47, 23, 19, NULL, IX1_CROSS1 }
+};
+CONFBOX t2LoadBox[] = {
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY, T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+
+ { ARSGBUT, LOADGAME, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100+100, BW, BH, NULL, IX2_CROSS1 }
};
-CONFBOX saveBox[NUM_SL_RGROUP+2] = {
+CONFINIT t1ciLoad = { 10, 6, 20, 16, true, t1LoadBox, ARRAYSIZE(t1LoadBox), SIX_LOAD_HEADING };
+CONFINIT t2ciLoad = { 10, 6, 40, 16, true, t2LoadBox, sizeof(t2LoadBox)/sizeof(CONFBOX), SS_LOAD_HEADING };
+
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY, EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + (BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 2*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 3*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 4*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 5*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 6*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+CONFBOX t1SaveBox[NUM_RGROUP_BOXES+2] = {
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY, EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + (BOX_HEIGHT + 2), EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 2*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 3*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 4*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 5*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 6*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
#ifndef JAPAN
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 7*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
- { RGROUP, SAVEGAME, NULL, USE_POINTER, 28, SY + 8*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 7*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 8*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
#endif
- { ARSGBUT, SAVEGAME, NULL,USE_POINTER, 230, 44, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, CLOSEWIN, NULL, USE_POINTER, 230, 44+47, 23, 19, NULL, IX_CROSS1 }
+ { ARSGBUT, SAVEGAME, TM_NONE, NULL,USE_POINTER, 230, 44, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 230, 44+47, 23, 19, NULL, IX1_CROSS1 }
+};
+CONFBOX t2SaveBox[] = {
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY, T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+
+ { ARSGBUT, SAVEGAME, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100+100, BW, BH, NULL, IX2_CROSS1 }
};
+CONFINIT t1ciSave = { 10, 6, 20, 16, true, t1SaveBox, ARRAYSIZE(t1SaveBox), SIX_SAVE_HEADING };
+CONFINIT t2ciSave = { 10, 6, 40, 16, true, t2SaveBox, sizeof(t2SaveBox)/sizeof(CONFBOX), SS_SAVE_HEADING };
+
+#define ciLoad (TinselV2 ? t2ciLoad : t1ciLoad)
+#define loadBox (TinselV2 ? t2LoadBox : t1LoadBox)
+#define ciSave (TinselV2 ? t2ciSave : t1ciSave)
+#define saveBox (TinselV2 ? t2SaveBox : t1SaveBox)
/*-------------------------------------------------------------*\
| This is the restart confirmation 'menu'. |
\*-------------------------------------------------------------*/
-CONFBOX restartBox[] = {
-
+CONFBOX t1RestartBox[] = {
#ifdef JAPAN
- { AAGBUT, INITGAME, NULL, USE_POINTER, 96, 44, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, CLOSEWIN, NULL, USE_POINTER, 56, 44, 23, 19, NULL, IX_CROSS1 }
+ { AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 96, 44, 23, 19, NULL, IX_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 56, 44, 23, 19, NULL, IX_CROSS1 }
#else
- { AAGBUT, INITGAME, NULL, USE_POINTER, 70, 28, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, CLOSEWIN, NULL, USE_POINTER, 30, 28, 23, 19, NULL, IX_CROSS1 }
+ { AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 70, 28, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 30, 28, 23, 19, NULL, IX1_CROSS1 }
#endif
+};
+CONFBOX t2RestartBox[] = {
+ { AAGBUT, INITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1 }
};
+#ifdef JAPAN
+CONFINIT t1ciRestart = { 6, 2, 72, 53, false, t1RestartBox, ARRAYSIZE(t1RestartBox), SIX_RESTART_HEADING };
+#else
+CONFINIT t1ciRestart = { 4, 2, 98, 53, false, t1RestartBox, ARRAYSIZE(t1RestartBox), SIX_RESTART_HEADING };
+#endif
+CONFINIT t2ciRestart = { 4, 2, 196, 53, false, t2RestartBox, sizeof(t2RestartBox)/sizeof(CONFBOX), SS_RESTART_HEADING };
+
+#define ciRestart (TinselV2 ? t2ciRestart : t1ciRestart)
+#define restartBox (TinselV2 ? t2RestartBox : t1RestartBox)
/*-------------------------------------------------------------*\
-| This is the sound control 'menu'. |
+| This is the sound control 'menu'. In Discworld 2, it also |
+| contains the subtitles and language selection. |
\*-------------------------------------------------------------*/
-#ifdef MAC_OPTIONS
- CONFBOX soundBox[] = {
- { SLIDER, MASTERVOL, NULL, SIX_MVOL_SLIDER, 142, 20, 100, 2, &volMaster, 0 },
- { SLIDER, MIDIVOL, NULL, SIX_MVOL_SLIDER, 142, 20+40, 100, 2, &volMidi, 0 },
- { SLIDER, SAMPVOL, NULL, SIX_SVOL_SLIDER, 142, 20+2*40, 100, 2, &volSound, 0 },
- { SLIDER, SAMPVOL, NULL, SIX_VVOL_SLIDER, 142, 20+3*40, 100, 2, &volVoice, 0 }
- };
-#else
-CONFBOX soundBox[] = {
- { SLIDER, MIDIVOL, NULL, SIX_MVOL_SLIDER, 142, 25, Audio::Mixer::kMaxChannelVolume, 2, &volMidi, 0 },
- { SLIDER, NOFUNC, NULL, SIX_SVOL_SLIDER, 142, 25+40, Audio::Mixer::kMaxChannelVolume, 2, &volSound, 0 },
- { SLIDER, NOFUNC, NULL, SIX_VVOL_SLIDER, 142, 25+2*40, Audio::Mixer::kMaxChannelVolume, 2, &volVoice, 0 }
+CONFBOX t1SoundBox[] = {
+ { SLIDER, MUSICVOL, TM_NONE, NULL, SIX_MVOL_SLIDER, 142, 25, Audio::Mixer::kMaxChannelVolume, 2, &volMusic, 0 },
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_SVOL_SLIDER, 142, 25+40, Audio::Mixer::kMaxChannelVolume, 2, &volSound, 0 },
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_VVOL_SLIDER, 142, 25+2*40, Audio::Mixer::kMaxChannelVolume, 2, &volVoice, 0 }
};
-#endif
+CONFBOX t2SoundBox[] = {
+ { SLIDER, MUSICVOL, TM_INDEX, NULL, SS_MVOL_SLIDER, 280, 50, Audio::Mixer::kMaxChannelVolume, 2, &volMusic, 0 },
+ { SLIDER, NOFUNC, TM_INDEX, NULL, SS_SVOL_SLIDER, 280, 50+30, Audio::Mixer::kMaxChannelVolume, 2, &volSound, 0 },
+ { SLIDER, NOFUNC, TM_INDEX, NULL, SS_VVOL_SLIDER, 280, 50+2*30, Audio::Mixer::kMaxChannelVolume, 2, &volVoice, 0 },
+
+ { SLIDER, NOFUNC, TM_INDEX, NULL, SS_TSPEED_SLIDER, 280, 160, 100, 2, &speedText, 0 },
+ { TOGGLE2, NOFUNC, TM_INDEX, NULL, SS_STITLE_TOGGLE, 100, 220, BW, BH, &bSubtitles, 0 },
+ { ROTATE, NOFUNC, TM_INDEX, NULL, SS_LANGUAGE_SELECT, 320,220, BW, BH, NULL, 0 }
+};
+
+CONFINIT t1ciSound = { 10, 5, 20, 16, false, t1SoundBox, ARRAYSIZE(t1SoundBox), NO_HEADING };
+CONFINIT t2ciSound = { 10, 5, 40, 16, false, t2SoundBox, sizeof(t2SoundBox)/sizeof(CONFBOX), SS_SOUND_HEADING };
+
+#define ciSound (TinselV2 ? t2ciSound : t1ciSound)
+#define soundBox (TinselV2 ? t2SoundBox : t1SoundBox)
/*-------------------------------------------------------------*\
| This is the (mouse) control 'menu'. |
@@ -538,83 +819,64 @@ CONFBOX soundBox[] = {
int bFlipped; // looks like this is just so the code has something to alter!
-
-#ifdef MAC_OPTIONS
CONFBOX controlBox[] = {
-
- { SLIDER, NOFUNC, NULL, SIX_DCLICK_SLIDER, 142, 25, 3*DOUBLE_CLICK_TIME, 1, &dclickSpeed, 0 },
- { FLIP, NOFUNC, NULL, SIX_DCLICK_TEST, 142, 25+30, 23, 19, &bFlipped, IX_CIRCLE1 }
-
-};
-#else
-CONFBOX controlBox[] = {
-
- { SLIDER, NOFUNC, NULL, SIX_DCLICK_SLIDER, 142, 25, 3*DOUBLE_CLICK_TIME, 1, &dclickSpeed, 0 },
- { FLIP, NOFUNC, NULL, SIX_DCLICK_TEST, 142, 25+30, 23, 19, &bFlipped, IX_CIRCLE1 },
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_DCLICK_SLIDER, 142, 25, 3*DOUBLE_CLICK_TIME, 1, &dclickSpeed, 0 },
+ { FLIP, NOFUNC, TM_NONE, NULL, SIX_DCLICK_TEST, 142, 25+30, 23, 19, &bFlipped, IX1_CIRCLE1 },
#ifdef JAPAN
- { TOGGLE, NOFUNC, NULL, SIX_SWAP_TOGGLE, 205, 25+70, 23, 19, &bSwapButtons, 0 }
+ { TOGGLE, NOFUNC, TM_NONE, NULL, SIX_SWAP_TOGGLE, 205, 25+70, 23, 19, &bSwapButtons, 0 }
#else
- { TOGGLE, NOFUNC, NULL, SIX_SWAP_TOGGLE, 155, 25+70, 23, 19, &bSwapButtons, 0 }
+ { TOGGLE, NOFUNC, TM_NONE, NULL, SIX_SWAP_TOGGLE, 155, 25+70, 23, 19, &bSwapButtons, 0 }
#endif
-
};
-#endif
+CONFINIT ciControl = { 10, 5, 20, 16, false, controlBox, ARRAYSIZE(controlBox), NO_HEADING };
/*-------------------------------------------------------------*\
-| This is the subtitles 'menu'. |
+| This is the subtitles 'menu'. |
\*-------------------------------------------------------------*/
CONFBOX subtitlesBox[] = {
-
- { SLIDER, NOFUNC, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
- { TOGGLE, NOFUNC, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
-
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
+ { TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
};
CONFBOX subtitlesBox3Flags[] = {
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 15, 118, 56, 32, NULL, FIX_FR },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 85, 118, 56, 32, NULL, FIX_GR },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 155, 118, 56, 32, NULL, FIX_SP },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 15, 118, 56, 32, NULL, FIX_FR },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 85, 118, 56, 32, NULL, FIX_GR },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 155, 118, 56, 32, NULL, FIX_SP },
-
- { SLIDER, NOFUNC, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
- { TOGGLE, NOFUNC, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
-
- { ARSGBUT, CLANG, NULL, USE_POINTER, 230, 110, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, RLANG, NULL, USE_POINTER, 230, 140, 23, 19, NULL, IX_CROSS1 }
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
+ { TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
+ { ARSGBUT, CLANG, TM_NONE, NULL, USE_POINTER, 230, 110, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, RLANG, TM_NONE, NULL, USE_POINTER, 230, 140, 23, 19, NULL, IX1_CROSS1 }
};
CONFBOX subtitlesBox4Flags[] = {
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 20, 100, 56, 32, NULL, FIX_FR },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 108, 100, 56, 32, NULL, FIX_GR },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 64, 137, 56, 32, NULL, FIX_IT },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 152, 137, 56, 32, NULL, FIX_SP },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 20, 100, 56, 32, NULL, FIX_FR },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 108, 100, 56, 32, NULL, FIX_GR },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 64, 137, 56, 32, NULL, FIX_IT },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 152, 137, 56, 32, NULL, FIX_SP },
-
- { SLIDER, NOFUNC, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
- { TOGGLE, NOFUNC, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
-
- { ARSGBUT, CLANG, NULL, USE_POINTER, 230, 110, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, RLANG, NULL, USE_POINTER, 230, 140, 23, 19, NULL, IX_CROSS1 }
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
+ { TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
+ { ARSGBUT, CLANG, TM_NONE, NULL, USE_POINTER, 230, 110, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, RLANG, TM_NONE, NULL, USE_POINTER, 230, 140, 23, 19, NULL, IX1_CROSS1 }
};
-CONFBOX subtitlesBox5Flags[] = {
+CONFBOX subtitlesBox5Flags[] = {
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 15, 100, 56, 32, NULL, FIX_UK },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 85, 100, 56, 32, NULL, FIX_FR },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 155, 100, 56, 32, NULL, FIX_GR },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 50, 137, 56, 32, NULL, FIX_IT },
+ { FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER, 120, 137, 56, 32, NULL, FIX_SP },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 15, 100, 56, 32, NULL, FIX_UK },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 85, 100, 56, 32, NULL, FIX_FR },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 155, 100, 56, 32, NULL, FIX_GR },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 50, 137, 56, 32, NULL, FIX_IT },
- { FRGROUP, NOFUNC, NULL, USE_POINTER, 120, 137, 56, 32, NULL, FIX_SP },
-
- { SLIDER, NOFUNC, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
- { TOGGLE, NOFUNC, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
-
- { ARSGBUT, CLANG, NULL, USE_POINTER, 230, 110, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, RLANG, NULL, USE_POINTER, 230, 140, 23, 19, NULL, IX_CROSS1 }
+ { SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER, 142, 20, 100, 2, &speedText, 0 },
+ { TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE, 142, 20+40, 23, 19, &bSubtitles, 0 },
+ { ARSGBUT, CLANG, TM_NONE, NULL, USE_POINTER, 230, 110, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, RLANG, TM_NONE, NULL, USE_POINTER, 230, 140, 23, 19, NULL, IX1_CROSS1 }
};
@@ -622,70 +884,92 @@ CONFBOX subtitlesBox5Flags[] = {
| This is the quit confirmation 'menu'. |
\*-------------------------------------------------------------*/
-CONFBOX quitBox[] = {
-#ifdef g
- { AAGBUT, IQUITGAME, NULL, USE_POINTER,70, 44, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, CLOSEWIN, NULL, USE_POINTER, 30, 44, 23, 19, NULL, IX_CROSS1 }
+CONFBOX t1QuitBox[] = {
+#ifdef JAPAN
+ { AAGBUT, IQUITGAME, TM_NONE, NULL, USE_POINTER,70, 44, 23, 19, NULL, IX_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 30, 44, 23, 19, NULL, IX_CROSS1 }
#else
- { AAGBUT, IQUITGAME, NULL, USE_POINTER,70, 28, 23, 19, NULL, IX_TICK1 },
- { AAGBUT, CLOSEWIN, NULL, USE_POINTER, 30, 28, 23, 19, NULL, IX_CROSS1 }
+ { AAGBUT, IQUITGAME, TM_NONE, NULL, USE_POINTER,70, 28, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 30, 28, 23, 19, NULL, IX1_CROSS1 }
#endif
};
+CONFBOX t2QuitBox[] = {
+ { AAGBUT, IQUITGAME, TM_NONE, NULL, 0,140, 78, BW, BH, NULL, IX2_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1 }
+};
-CONFBOX topwinBox[] = {
- { NOTHING, NOFUNC, NULL, USE_POINTER, 0, 0, 0, 0, NULL, 0 }
+CONFINIT t1ciQuit = { 4, 2, 98, 53, false, t1QuitBox, ARRAYSIZE(t1QuitBox), SIX_QUIT_HEADING };
+CONFINIT t2ciQuit = { 4, 2, 196, 53, false, t2QuitBox, sizeof(t2QuitBox)/sizeof(CONFBOX), SS_QUIT_HEADING };
+
+#define quitBox (TinselV2 ? t2QuitBox : t1QuitBox)
+#define ciQuit (TinselV2 ? t2ciQuit : t1ciQuit)
+
+/***************************************************************************\
+|************************ Startup and shutdown ***********************|
+\***************************************************************************/
+
+CONFBOX hopperBox1[] = {
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY, T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+
+ { ARSGBUT, HOPPER2, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100 + 100, BW, BH, NULL, IX2_CROSS1 }
};
+CONFINIT ciHopper1 = { 10, 6, 40, 16, true, hopperBox1, sizeof(hopperBox1) / sizeof(CONFBOX), SS_HOPPER1 };
+
+CONFBOX hopperBox2[] = {
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY, T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+ { RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
+
+ { ARSGBUT, BF_CHANGESCENE, TM_NONE, NULL, 0, 460, 50, BW, BH, NULL, IX2_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 200, BW, BH, NULL, IX2_CROSS1 }
+};
+CONFINIT ciHopper2 = { 10, 6, 40, 16, true, hopperBox2, sizeof(hopperBox2)/sizeof(CONFBOX), NO_HEADING };
-struct CONFINIT {
- int h;
- int v;
- int x;
- int y;
- bool bExtraWin;
- CONFBOX *Box;
- int NumBoxes;
- int ixHeading;
+
+/***************************************************************************\
+|**************************** Top Window *****************************|
+\***************************************************************************/
+CONFBOX topwinBox[] = {
+ { NOTHING, NOFUNC, TM_NONE, NULL, USE_POINTER, 0, 0, 0, 0, NULL, 0 }
};
-CONFINIT ciOption = { 6, 5, 72, 23, false, optionBox, ARRAYSIZE(optionBox), NO_HEADING };
-
-CONFINIT ciLoad = { 10, 6, 20, 16, true, loadBox, ARRAYSIZE(loadBox), SIX_LOAD_HEADING };
-CONFINIT ciSave = { 10, 6, 20, 16, true, saveBox, ARRAYSIZE(saveBox), SIX_SAVE_HEADING };
-#ifdef JAPAN
-CONFINIT ciRestart = { 6, 2, 72, 53, false, restartBox, ARRAYSIZE(restartBox), SIX_RESTART_HEADING };
-#else
-CONFINIT ciRestart = { 4, 2, 98, 53, false, restartBox, ARRAYSIZE(restartBox), SIX_RESTART_HEADING };
-#endif
-CONFINIT ciSound = { 10, 5, 20, 16, false, soundBox, ARRAYSIZE(soundBox), NO_HEADING };
-#ifdef MAC_OPTIONS
- CONFINIT ciControl = { 10, 3, 20, 40, false, controlBox, ARRAYSIZE(controlBox), NO_HEADING };
-#else
- CONFINIT ciControl = { 10, 5, 20, 16, false, controlBox, ARRAYSIZE(controlBox), NO_HEADING };
-#endif
CONFINIT ciSubtitles = { 10, 3, 20, 16, false, subtitlesBox, ARRAYSIZE(subtitlesBox), NO_HEADING };
-CONFINIT ciQuit = { 4, 2, 98, 53, false, quitBox, ARRAYSIZE(quitBox), SIX_QUIT_HEADING };
-
CONFINIT ciTopWin = { 6, 5, 72, 23, false, topwinBox, 0, NO_HEADING };
#define NOBOX (-1)
// Conf window globals
static struct {
- CONFBOX *Box;
+ CONFBOX *box;
int NumBoxes;
bool bExtraWin;
- int ixHeading;
+ uint32 ixHeading;
bool editableRgroup;
int selBox;
int pointBox; // Box pointed to on last call
- int saveModifier;
- int fileBase;
+ int modifier;
+ int extraBase;
int numSaved;
} cd = {
NULL, 0, false, 0, false,
@@ -714,7 +998,7 @@ static struct {
enum {
IB_NONE = -1, //
IB_UP = -2, // negative numbers returned
- IB_DOWN = -3, // by WhichInvBox()
+ IB_DOWN = -3, // by WhichMenuBox()
IB_SLIDE = -4, //
IB_SLIDE_UP = -5, //
IB_SLIDE_DOWN = -6 //
@@ -728,7 +1012,7 @@ enum {
IS_MASK = (IS_LEFT | IS_SLIDER | IS_RIGHT)
};
-static int WhichInvBox(int curX, int curY, bool bSlides);
+static int WhichMenuBox(int curX, int curY, bool bSlides);
static void SlideMSlider(int x, SSFN fn);
static OBJECT *AddObject(const FREEL *pfreel, int num);
static void AddBoxes(bool posnSlide);
@@ -761,18 +1045,15 @@ static void ConfActionSpecial(int i);
#endif
#define M_IDT 10 // Y offset (from bottom) of top of down arrow
#define M_IDB 3 // Y offset (from bottom) of bottom of down arrow
-#define M_IAL 12 // X offset (from right) of left of scroll arrows
-#define M_IAR 3 // X offset (from right) of right of scroll arrows
-#define START_ICONX (M_SW+1) // } Relative offset of first icon
-#define START_ICONY (M_TBB+M_TH+1) // } within the inventory window
+#define START_ICONX (TinselV2 ? 12 : (M_SW+1)) // } Relative offset of first icon
+#define START_ICONY (TinselV2 ? 40 : (M_TBB+M_TH+1)) // } within the inventory window
/*-------------------------------------------------------------------------*/
-
bool LanguageChange(void) {
- LANGUAGE nLang = TXT_ENGLISH;
+ LANGUAGE nLang = g_language;
if (_vm->getFeatures() & GF_USE_3FLAGS) {
// VERY quick dodgy bodge
@@ -798,13 +1079,152 @@ bool LanguageChange(void) {
}
/**************************************************************************/
+/***************************** Scene Hopper ******************************/
+/**************************************************************************/
+
+/**
+ * Read in the scene hopper data file and set the
+ * pointers to the data and scene count.
+ */
+static void PrimeSceneHopper(void) {
+ Common::File f;
+ char *pBuffer;
+ uint32 vSize;
+
+ // Open the file (it's on the CD)
+ CdCD(nullContext);
+ if (!f.open(HOPPER_FILENAME))
+ error(CANNOT_FIND_FILE, HOPPER_FILENAME);
+
+ // Read in header
+ if (f.readUint32LE() != CHUNK_SCENE_HOPPER)
+ error(FILE_IS_CORRUPT, HOPPER_FILENAME);
+ vSize = f.readUint32LE();
+
+ // allocate a buffer for it all
+ assert(pHopper == NULL);
+ uint32 size = f.size() - 8;
+
+ // make sure memory allocated
+ pBuffer = (char *)malloc(size);
+ if (pBuffer == NULL)
+ // cannot alloc buffer for index
+ error(NO_MEM, "Scene hopper data\n");
+
+ // load data
+ if (f.read(pBuffer, size) != size)
+ // file must be corrupt if we get to here
+ error(FILE_IS_CORRUPT, HOPPER_FILENAME);
+
+ // Set data pointers
+ pHopper = (PHOPPER)pBuffer;
+ pEntries = (PHOPENTRY)(pBuffer + vSize);
+ numScenes = vSize / sizeof(HOPPER);
+
+ // close the file
+ f.close();
+}
+
+/**
+ * Free the scene hopper data file
+ */
+static void FreeSceneHopper() {
+ free(pHopper);
+ pHopper = NULL;
+}
+
+static void FirstScene(int first) {
+ int i;
+
+ assert(numScenes && pHopper);
+
+ if (bRemember) {
+ assert(first == 0);
+ first = lastChosenScene;
+ bRemember = false;
+ }
+
+ // Force it to a sensible value
+ if (first > numScenes - NUM_RGROUP_BOXES)
+ first = numScenes - NUM_RGROUP_BOXES;
+ if (first < 0)
+ first = 0;
+
+ // Fill in the rest
+ for (i = 0; i < NUM_RGROUP_BOXES && i + first < numScenes; i++) {
+ cd.box[i].textMethod = TM_STRINGNUM;
+ cd.box[i].ixText = FROM_LE_32(pHopper[i + first].hSceneDesc);
+ }
+ // Blank out the spare ones (if any)
+ while (i < NUM_RGROUP_BOXES) {
+ cd.box[i].textMethod = TM_NONE;
+ cd.box[i++].ixText = 0;
+ }
+
+ cd.extraBase = first;
+}
+
+static void RememberChosenScene(void) {
+ bRemember = true;
+}
+
+static void SetChosenScene(void) {
+ lastChosenScene = cd.selBox + cd.extraBase;
+ pChosenScene = &pHopper[cd.selBox + cd.extraBase];
+}
+
+static void FirstEntry(int first) {
+ int i;
+
+ InvD[INV_MENU].hInvTitle = FROM_LE_32(pChosenScene->hSceneDesc);
+
+ // get number of entrances
+ numEntries = FROM_LE_32(pChosenScene->numEntries);
+
+ // Force first to a sensible value
+ if (first > numEntries-NUM_RGROUP_BOXES)
+ first = numEntries-NUM_RGROUP_BOXES;
+ if (first < 0)
+ first = 0;
+
+ for (i = 0; i < NUM_RGROUP_BOXES && i < numEntries; i++) {
+ cd.box[i].textMethod = TM_STRINGNUM;
+ cd.box[i].ixText = FROM_LE_32(pEntries[FROM_LE_32(pChosenScene->entryIndex) + i + first].hDesc);
+ }
+ // Blank out the spare ones (if any)
+ while (i < NUM_RGROUP_BOXES) {
+ cd.box[i].textMethod = TM_NONE;
+ cd.box[i++].ixText = 0;
+ }
+
+ cd.extraBase = first;
+}
+
+void HopAction(void) {
+ PHOPENTRY pEntry = pEntries + FROM_LE_32(pChosenScene->entryIndex) + cd.selBox + cd.extraBase;
+
+ uint32 hScene = FROM_LE_32(pChosenScene->hScene);
+ uint32 eNumber = FROM_LE_32(pEntry->eNumber);
+ debugC(DEBUG_BASIC, kTinselDebugAnimations, "Scene hopper chose scene %xh,%d\n", hScene, eNumber);
+
+ if (FROM_LE_32(pEntry->flags) & fCall) {
+ SaveScene(nullContext);
+ NewScene(nullContext, pChosenScene->hScene, pEntry->eNumber, TRANS_FADE);
+ }
+ else if (FROM_LE_32(pEntry->flags) & fHook)
+ HookScene(hScene, eNumber, TRANS_FADE);
+ else
+ NewScene(nullContext, hScene, eNumber, TRANS_CUT);
+}
+
+/**************************************************************************/
/******************** Some miscellaneous functions ************************/
/**************************************************************************/
/**
* Delete all the objects in iconArray[]
*/
-static void DumpIconArray(void){
+static void DumpIconArray(void) {
for (int i = 0; i < MAX_ICONS; i++) {
if (iconArray[i] != NULL) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[i]);
@@ -841,8 +1261,8 @@ static void DumpObjArray(void) {
* Convert item ID number to pointer to item's compiled data
* i.e. Image data and Glitter code.
*/
-INV_OBJECT *findInvObject(int num) {
- INV_OBJECT *retval = pio;
+INV_OBJECT *GetInvObject(int num) {
+ INV_OBJECT *retval = invObjects;
for (int i = 0; i < numObjects; i++, retval++) {
if (retval->id == num)
@@ -853,19 +1273,34 @@ INV_OBJECT *findInvObject(int num) {
}
/**
+ * Convert item ID number to index.
+ */
+int GetObjectIndex(int id) {
+ int i; // Loop counter
+ INV_OBJECT *pObject;
+
+ for (i = 0, pObject = invObjects; i < numObjects; i++, pObject++) {
+ if (pObject->id == id)
+ break;
+ }
+
+ assert(i < numObjects);
+ return i;
+}
+
+/**
* Returns position of an item in one of the inventories.
* The actual position is not important for the uses that this is put to.
*/
-
int InventoryPos(int num) {
int i;
for (i = 0; i < InvD[INV_1].NoofItems; i++) // First inventory
- if (InvD[INV_1].ItemOrder[i] == num)
+ if (InvD[INV_1].contents[i] == num)
return i;
for (i = 0; i < InvD[INV_2].NoofItems; i++) // Second inventory
- if (InvD[INV_2].ItemOrder[i] == num)
+ if (InvD[INV_2].contents[i] == num)
return i;
if (HeldItem == num)
@@ -878,7 +1313,7 @@ bool IsInInventory(int object, int invnum) {
assert(invnum == INV_1 || invnum == INV_2);
for (int i = 0; i < InvD[invnum].NoofItems; i++) // First inventory
- if (InvD[invnum].ItemOrder[i] == object)
+ if (InvD[invnum].contents[i] == object)
return true;
return false;
@@ -895,17 +1330,24 @@ int WhichItemHeld(void) {
* Called from the cursor module when it re-initialises (at the start of
* a new scene). For if we are holding something at scene-change time.
*/
-void InventoryIconCursor(void) {
- INV_OBJECT *invObj;
+void InventoryIconCursor(bool bNewItem) {
if (HeldItem != INV_NOICON) {
- invObj = findInvObject(HeldItem);
- SetAuxCursor(invObj->hFilm);
+ if (TinselV2) {
+ if (bNewItem) {
+ int objIndex = GetObjectIndex(HeldItem);
+ heldFilm = invFilms[objIndex];
+ }
+ SetAuxCursor(heldFilm);
+ } else {
+ INV_OBJECT *invObj = GetInvObject(HeldItem);
+ SetAuxCursor(invObj->hIconFilm);
+ }
}
}
/**
- * Returns TRUE if the inventory is active.
+ * Returns true if the inventory is active.
*/
bool InventoryActive(void) {
return (InventoryState == ACTIVE_INV);
@@ -923,16 +1365,17 @@ int WhichInventoryOpen(void) {
/************** Running inventory item's Glitter code *********************/
/**************************************************************************/
-struct ITP_INIT {
+struct OP_INIT {
INV_OBJECT *pinvo;
- USER_EVENT event;
- BUTEVENT bev;
+ TINSEL_EVENT event;
+ PLR_EVENT bev;
+ int myEscape;
};
/**
* Run inventory item's Glitter code
*/
-static void InvTinselProcess(CORO_PARAM, const void *param) {
+static void ObjectProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
@@ -940,13 +1383,15 @@ static void InvTinselProcess(CORO_PARAM, const void *param) {
CORO_END_CONTEXT(_ctx);
// get the stuff copied to process when it was created
- ITP_INIT *to = (ITP_INIT *)param;
+ const OP_INIT *to = (const OP_INIT *)param;
CORO_BEGIN_CODE(_ctx);
- CORO_INVOKE_1(AllowDclick, to->bev);
+ if (!TinselV2)
+ CORO_INVOKE_1(AllowDclick, to->bev);
- _ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->hScript, to->event, NOPOLY, 0, to->pinvo);
+ _ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->hScript, to->event, NOPOLY, 0, to->pinvo,
+ to->myEscape);
CORO_INVOKE_1(Interpret, _ctx->pic);
if (to->event == POINTED) {
@@ -973,14 +1418,47 @@ static void InvTinselProcess(CORO_PARAM, const void *param) {
/**
* Run inventory item's Glitter code
*/
-void RunInvTinselCode(INV_OBJECT *pinvo, USER_EVENT event, BUTEVENT be, int index) {
- ITP_INIT to = { pinvo, event, be };
+void InvTinselEvent(INV_OBJECT *pinvo, TINSEL_EVENT event, PLR_EVENT be, int index) {
+ OP_INIT to = { pinvo, event, be, 0 };
- if (InventoryHidden)
+ if (InventoryHidden || (TinselV2 && !pinvo->hScript))
return;
GlitterIndex = index;
- g_scheduler->createProcess(PID_TCODE, InvTinselProcess, &to, sizeof(to));
+ g_scheduler->createProcess(PID_TCODE, ObjectProcess, &to, sizeof(to));
+}
+
+void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, int myEscape, bool *result) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ PROCESS *pProc;
+ INV_OBJECT *pInvo;
+ OP_INIT op;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (result) *result = false;
+ _ctx->pInvo = GetInvObject(objId);
+ if (!_ctx->pInvo->hScript)
+ return;
+
+ _ctx->op.pinvo = _ctx->pInvo;
+ _ctx->op.event = event;
+ _ctx->op.myEscape = myEscape;
+
+ g_scheduler->createProcess(PID_TCODE, ObjectProcess, &_ctx->op, sizeof(_ctx->op));
+
+ if (bWait)
+ CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
+ else
+ // FIXME: Essential to ensure created ObjectProcess coroutine has a chance to start
+ // up, for the scene restore when returning from looking at the AshkEnte instructions
+ // to work. Really need to trace operation of original to see at what point the scene
+ // save is done, and when control was first yielded to the ObjectProcess coroutine
+ CORO_SLEEP(1);
+
+ CORO_END_CODE;
}
/**************************************************************************/
@@ -991,8 +1469,7 @@ void RunInvTinselCode(INV_OBJECT *pinvo, USER_EVENT event, BUTEVENT be, int inde
* Set first load/save file entry displayed.
* Point Box[] text pointers to appropriate file descriptions.
*/
-
-void firstFile(int first) {
+void FirstFile(int first) {
int i, j;
i = getList();
@@ -1001,22 +1478,22 @@ void firstFile(int first) {
if (first < 0)
first = 0;
- else if (first > MAX_SFILES-NUM_SL_RGROUP)
- first = MAX_SFILES-NUM_SL_RGROUP;
+ else if (first > MAX_SAVED_FILES - NUM_RGROUP_BOXES)
+ first = MAX_SAVED_FILES - NUM_RGROUP_BOXES;
- if (first == 0 && i < MAX_SFILES && cd.Box == saveBox) {
+ if (first == 0 && i < MAX_SAVED_FILES && cd.box == saveBox) {
// Blank first entry for new save
- cd.Box[0].boxText = NULL;
- cd.saveModifier = j = 1;
+ cd.box[0].boxText = NULL;
+ cd.modifier = j = 1;
} else {
- cd.saveModifier = j = 0;
+ cd.modifier = j = 0;
}
- for (i = first; j < NUM_SL_RGROUP; j++, i++) {
- cd.Box[j].boxText = ListEntry(i, LE_DESC);
+ for (i = first; j < NUM_RGROUP_BOXES; j++, i++) {
+ cd.box[j].boxText = ListEntry(i, LE_DESC);
}
- cd.fileBase = first;
+ cd.extraBase = first;
}
/**
@@ -1028,7 +1505,7 @@ void InvSaveGame(void) {
#ifndef JAPAN
sedit[strlen(sedit)-1] = 0; // Don't include the cursor!
#endif
- SaveGame(ListEntry(cd.selBox-cd.saveModifier+cd.fileBase, LE_NAME), sedit);
+ SaveGame(ListEntry(cd.selBox-cd.modifier+cd.extraBase, LE_NAME), sedit);
}
}
@@ -1038,7 +1515,7 @@ void InvSaveGame(void) {
void InvLoadGame(void) {
int rGame;
- if (cd.selBox != NOBOX && (cd.selBox+cd.fileBase < cd.numSaved)) {
+ if (cd.selBox != NOBOX && (cd.selBox+cd.extraBase < cd.numSaved)) {
rGame = cd.selBox;
cd.selBox = NOBOX;
if (iconArray[HL3] != NULL) {
@@ -1053,13 +1530,13 @@ void InvLoadGame(void) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
iconArray[HL1] = NULL;
}
- RestoreGame(rGame+cd.fileBase);
+ RestoreGame(rGame+cd.extraBase);
}
}
/**
* Edit the string in sedit[]
- * Returns TRUE if the string was altered.
+ * Returns true if the string was altered.
*/
#ifndef JAPAN
bool UpdateString(const Common::KeyState &kbd) {
@@ -1070,6 +1547,9 @@ bool UpdateString(const Common::KeyState &kbd) {
cpos = strlen(sedit)-1;
+ if (kbd.ascii == 0)
+ return false;
+
if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
if (!cpos)
return false;
@@ -1078,7 +1558,7 @@ bool UpdateString(const Common::KeyState &kbd) {
sedit[cpos] = CURSOR_CHAR;
return true;
// } else if (isalnum(c) || c == ',' || c == '.' || c == '\'' || (c == ' ' && cpos != 0)) {
- } else if (IsCharImage(hTagFontHandle(), kbd.ascii) || (kbd.ascii == ' ' && cpos != 0)) {
+ } else if (IsCharImage(GetTagFontHandle(), kbd.ascii) || (kbd.ascii == ' ' && cpos != 0)) {
if (cpos == SG_DESC_LEN)
return false;
sedit[cpos] = kbd.ascii;
@@ -1118,17 +1598,19 @@ bool InvKeyIn(const Common::KeyState &kbd) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL3]);
iconArray[HL3] = NULL;
}
- iconArray[HL3] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), sedit, 0,
- InvD[ino].inventoryX + cd.Box[cd.selBox].xpos + 2,
- InvD[ino].inventoryY + cd.Box[cd.selBox].ypos,
- hTagFontHandle(), 0);
- if (MultiRightmost(iconArray[HL3]) > 213) {
+ iconArray[HL3] = ObjectTextOut(nullContext,
+ GetPlayfieldList(FIELD_STATUS), sedit, 0,
+ InvD[ino].inventoryX + cd.box[cd.selBox].xpos + 2,
+ InvD[ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
+ GetTagFontHandle(), 0);
+ if (MultiRightmost(iconArray[HL3]) > MAX_NAME_RIGHT) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL3]);
UpdateString(Common::KeyState(Common::KEYCODE_BACKSPACE));
- iconArray[HL3] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), sedit, 0,
- InvD[ino].inventoryX + cd.Box[cd.selBox].xpos + 2,
- InvD[ino].inventoryY + cd.Box[cd.selBox].ypos,
- hTagFontHandle(), 0);
+ iconArray[HL3] = ObjectTextOut(nullContext,
+ GetPlayfieldList(FIELD_STATUS), sedit, 0,
+ InvD[ino].inventoryX + cd.box[cd.selBox].xpos + 2,
+ InvD[ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
+ GetTagFontHandle(), 0);
}
MultiSetZPosition(iconArray[HL3], Z_INV_ITEXT + 2);
}
@@ -1137,13 +1619,10 @@ bool InvKeyIn(const Common::KeyState &kbd) {
return false;
}
-/*---------------------------------------------------------------------*\
-| Select() |
-|-----------------------------------------------------------------------|
-| Highlights selected box. |
-| If it's editable (save game), copy existing description and add a |
-| cursor. |
-\*---------------------------------------------------------------------*/
+/**
+ * Highlights selected box.
+ * If it's editable (save game), copy existing description and add a cursor.
+ */
void Select(int i, bool force) {
#ifdef JAPAN
time_t secs_now;
@@ -1168,19 +1647,24 @@ void Select(int i, bool force) {
}
// New highlight box
- switch (cd.Box[i].boxType) {
+ switch (cd.box[i].boxType) {
case RGROUP:
- iconArray[HL2] = RectangleObject(BackPal(), COL_HILIGHT, cd.Box[i].w, cd.Box[i].h);
+ iconArray[HL2] = RectangleObject(BgPal(),
+ (TinselV2 ? HighlightColour() : COL_HILIGHT), cd.box[i].w, cd.box[i].h);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL2]);
MultiSetAniXY(iconArray[HL2],
- InvD[ino].inventoryX + cd.Box[i].xpos,
- InvD[ino].inventoryY + cd.Box[i].ypos);
+ InvD[ino].inventoryX + cd.box[i].xpos,
+ InvD[ino].inventoryY + cd.box[i].ypos);
// Z-position of box, and add edit text if appropriate
if (cd.editableRgroup) {
MultiSetZPosition(iconArray[HL2], Z_INV_ITEXT+1);
- assert(cd.Box[i].ixText == USE_POINTER);
+ if (TinselV2) {
+ assert(cd.box[i].textMethod == TM_POINTER);
+ } else {
+ assert(cd.box[i].ixText == USE_POINTER);
+ }
#ifdef JAPAN
// Current date and time
time(&secs_now);
@@ -1188,21 +1672,23 @@ void Select(int i, bool force) {
strftime(sedit, SG_DESC_LEN, "%D %H:%M", time_now);
#else
// Current description with cursor appended
- if (cd.Box[i].boxText != NULL) {
- strcpy(sedit, cd.Box[i].boxText);
+ if (cd.box[i].boxText != NULL) {
+ strcpy(sedit, cd.box[i].boxText);
strcat(sedit, sCursor);
} else {
strcpy(sedit, sCursor);
}
#endif
- iconArray[HL3] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), sedit, 0,
- InvD[ino].inventoryX + cd.Box[i].xpos + 2,
+
+ iconArray[HL3] = ObjectTextOut(nullContext,
+ GetPlayfieldList(FIELD_STATUS), sedit, 0,
+ InvD[ino].inventoryX + cd.box[i].xpos + 2,
#ifdef JAPAN
- InvD[ino].inventoryY + cd.Box[i].ypos + 2,
+ InvD[ino].inventoryY + cd.box[i].ypos + 2,
#else
- InvD[ino].inventoryY + cd.Box[i].ypos,
+ InvD[ino].inventoryY + cd.box[i].ypos + TYOFF,
#endif
- hTagFontHandle(), 0);
+ GetTagFontHandle(), 0);
MultiSetZPosition(iconArray[HL3], Z_INV_ITEXT + 2);
} else {
MultiSetZPosition(iconArray[HL2], Z_INV_ICONS + 1);
@@ -1212,17 +1698,17 @@ void Select(int i, bool force) {
break;
+#if defined(USE_3FLAGS) || defined(USE_4FLAGS) || defined(USE_5FLAGS)
case FRGROUP:
- assert((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS));
- iconArray[HL2] = RectangleObject(BackPal(), COL_HILIGHT, cd.Box[i].w+6, cd.Box[i].h+6);
+ iconArray[HL2] = RectangleObject(BgPal(), COL_HILIGHT, cd.box[i].w+6, cd.box[i].h+6);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL2]);
MultiSetAniXY(iconArray[HL2],
- InvD[ino].inventoryX + cd.Box[i].xpos - 2,
- InvD[ino].inventoryY + cd.Box[i].ypos - 2);
+ InvD[ino].inventoryX + cd.box[i].xpos - 2,
+ InvD[ino].inventoryY + cd.box[i].ypos - 2);
MultiSetZPosition(iconArray[HL2], Z_INV_BRECT+1);
break;
-
+#endif
default:
break;
}
@@ -1234,32 +1720,8 @@ void Select(int i, bool force) {
/**************************************************************************/
/**
- * If the item is not already held, hold it.
- */
-
-void HoldItem(int item) {
- INV_OBJECT *invObj;
-
- if (HeldItem != item) {
- if (item == INV_NOICON && HeldItem != INV_NOICON)
- DelAuxCursor(); // no longer aux cursor
-
- if (item != INV_NOICON) {
- invObj = findInvObject(item);
- SetAuxCursor(invObj->hFilm); // and is aux. cursor
- }
-
- HeldItem = item; // Item held
- }
-
- // Redraw contents - held item not displayed as a content.
- ItemsChanged = true;
-}
-
-/**
* Stop holding an item.
*/
-
void DropItem(int item) {
if (HeldItem == item) {
HeldItem = INV_NOICON; // Item not held
@@ -1271,17 +1733,27 @@ void DropItem(int item) {
}
/**
- * Stick the item into an inventory list (ItemOrder[]), and hold the
+ * Clears the specified inventory
+ */
+void ClearInventory(int invno) {
+ assert(invno == INV_1 || invno == INV_2);
+
+ InvD[invno].NoofItems = 0;
+ memset(InvD[invno].contents, 0, sizeof(InvD[invno].contents));
+}
+
+/**
+ * Stick the item into an inventory list (contents[]), and hold the
* item if requested.
*/
void AddToInventory(int invno, int icon, bool hold) {
- int i;
- bool bOpen;
-#ifdef DEBUG
+ int i;
+ bool bOpen;
INV_OBJECT *invObj;
-#endif
- assert((invno == INV_1 || invno == INV_2 || invno == INV_CONV || invno == INV_OPEN)); // Trying to add to illegal inventory
+ // Validate trying to add to a legal inventory
+ assert(invno == INV_1 || invno == INV_2 || invno == INV_CONV
+ || invno == INV_OPEN || (invno == INV_DEFAULT && TinselV2));
if (invno == INV_OPEN) {
assert(InventoryState == ACTIVE_INV && (ino == INV_1 || ino == INV_2)); // addopeninv() with inventry not open
@@ -1290,15 +1762,19 @@ void AddToInventory(int invno, int icon, bool hold) {
// Make sure it doesn't get in both!
RemFromInventory(ino == INV_1 ? INV_2 : INV_1, icon);
- } else
+ } else {
bOpen = false;
-#ifdef DEBUG
- invObj = findInvObject(icon);
- if ((invObj->attribute & IO_ONLYINV1 && invno != INV_1)
- || (invObj->attribute & IO_ONLYINV2 && invno != INV_2))
- error("Trying to add resticted object to wrong inventory");
-#endif
+ if (TinselV2 && invno == INV_DEFAULT) {
+ invObj = GetInvObject(icon);
+ if (invObj->attribute & DEFINV2)
+ invno = INV_2;
+ else if (invObj->attribute & DEFINV1)
+ invno = INV_1;
+ else
+ invno = SysVar(SV_DEFAULT_INV);
+ }
+ }
if (invno == INV_1)
RemFromInventory(INV_2, icon);
@@ -1307,7 +1783,7 @@ void AddToInventory(int invno, int icon, bool hold) {
// See if it's already there
for (i = 0; i < InvD[invno].NoofItems; i++) {
- if (InvD[invno].ItemOrder[i] == icon)
+ if (InvD[invno].contents[i] == icon)
break;
}
@@ -1315,28 +1791,52 @@ void AddToInventory(int invno, int icon, bool hold) {
if (i == InvD[invno].NoofItems) {
if (!bOpen) {
if (invno == INV_CONV) {
- // For conversation, insert before last icon
- // which will always be the goodbye icon
- InvD[invno].ItemOrder[InvD[invno].NoofItems] = InvD[invno].ItemOrder[InvD[invno].NoofItems-1];
- InvD[invno].ItemOrder[InvD[invno].NoofItems-1] = icon;
- InvD[invno].NoofItems++;
+ if (TinselV2) {
+ int nei;
+
+ // Count how many current contents have end attribute
+ for (i = 0, nei = 0; i < InvD[INV_CONV].NoofItems; i++) {
+ invObj = GetInvObject(InvD[INV_CONV].contents[i]);
+ if (invObj->attribute & CONVENDITEM)
+ nei++;
+ }
+
+ // For conversation, insert before end icons
+ memmove(&InvD[INV_CONV].contents[i-nei+1],
+ &InvD[INV_CONV].contents[i-nei], nei * sizeof(int));
+ InvD[INV_CONV].contents[i - nei] = icon;
+ InvD[INV_CONV].NoofItems++;
+ InvD[INV_CONV].NoofHicons = InvD[INV_CONV].NoofItems;
+
+ // Get the window to re-position
+ bMoveOnUnHide = true;
+ } else {
+ // For conversation, insert before last icon
+ // which will always be the goodbye icon
+ InvD[invno].contents[InvD[invno].NoofItems] = InvD[invno].contents[InvD[invno].NoofItems-1];
+ InvD[invno].contents[InvD[invno].NoofItems-1] = icon;
+ InvD[invno].NoofItems++;
+ }
} else {
- InvD[invno].ItemOrder[InvD[invno].NoofItems++] = icon;
+ InvD[invno].contents[InvD[invno].NoofItems++] = icon;
}
ItemsChanged = true;
} else {
// It could be that the index is beyond what you'd expect
// as delinv may well have been called
if (GlitterIndex < InvD[invno].NoofItems) {
- memmove(&InvD[invno].ItemOrder[GlitterIndex + 1],
- &InvD[invno].ItemOrder[GlitterIndex],
- (InvD[invno].NoofItems-GlitterIndex)*sizeof(int));
- InvD[invno].ItemOrder[GlitterIndex] = icon;
+ memmove(&InvD[invno].contents[GlitterIndex + 1],
+ &InvD[invno].contents[GlitterIndex],
+ (InvD[invno].NoofItems - GlitterIndex) * sizeof(int));
+ InvD[invno].contents[GlitterIndex] = icon;
} else {
- InvD[invno].ItemOrder[InvD[invno].NoofItems] = icon;
+ InvD[invno].contents[InvD[invno].NoofItems] = icon;
}
InvD[invno].NoofItems++;
}
+
+ // Move here after bug on Japenese DW1
+ ItemsChanged = true;
}
// Hold it if requested
@@ -1345,10 +1845,9 @@ void AddToInventory(int invno, int icon, bool hold) {
}
/**
- * Take the item from the inventory list (ItemOrder[]).
+ * Take the item from the inventory list (contents[]).
* Return FALSE if item wasn't present, true if it was.
*/
-
bool RemFromInventory(int invno, int icon) {
int i;
@@ -1356,36 +1855,83 @@ bool RemFromInventory(int invno, int icon) {
// See if it's there
for (i = 0; i < InvD[invno].NoofItems; i++) {
- if (InvD[invno].ItemOrder[i] == icon)
+ if (InvD[invno].contents[i] == icon)
break;
}
if (i == InvD[invno].NoofItems)
return false; // Item wasn't there
else {
- memmove(&InvD[invno].ItemOrder[i], &InvD[invno].ItemOrder[i+1], (InvD[invno].NoofItems-i)*sizeof(int));
+ memmove(&InvD[invno].contents[i], &InvD[invno].contents[i+1], (InvD[invno].NoofItems-i)*sizeof(int));
InvD[invno].NoofItems--;
+
+ if (TinselV2 && invno == INV_CONV) {
+ InvD[INV_CONV].NoofHicons = InvD[invno].NoofItems;
+
+ // Get the window to re-position
+ bMoveOnUnHide = true;
+ }
+
ItemsChanged = true;
return true; // Item removed
}
}
+/**
+ * If the item is not already held, hold it.
+ */
+void HoldItem(int item, bool bKeepFilm) {
+ INV_OBJECT *invObj;
+
+ if (HeldItem != item) {
+ if (TinselV2 && (HeldItem != NOOBJECT)) {
+ // No longer holding previous item
+ DelAuxCursor(); // no longer aux cursor
+
+ // If old held object is not in an inventory, and
+ // has a default, stick it in its default inventory.
+ if (!IsInInventory(HeldItem, INV_1) && !IsInInventory(HeldItem, INV_2)) {
+ invObj = GetInvObject(HeldItem);
+
+ if (invObj->attribute & DEFINV1)
+ AddToInventory(INV_1, HeldItem);
+ else if (invObj->attribute & DEFINV2)
+ AddToInventory(INV_2, HeldItem);
+ else
+ // Hook for definable default inventory
+ AddToInventory(INV_1, HeldItem);
+ }
+
+ } else if (!TinselV2) {
+ if (item == INV_NOICON && HeldItem != INV_NOICON)
+ DelAuxCursor(); // no longer aux cursor
+
+ if (item != INV_NOICON) {
+ invObj = GetInvObject(item);
+ SetAuxCursor(invObj->hIconFilm); // and is aux. cursor
+ }
+ }
+
+ HeldItem = item; // Item held
+
+ if (TinselV2) {
+ InventoryIconCursor(!bKeepFilm);
+
+ // Redraw contents - held item not displayed as a content.
+ ItemsChanged = true;
+ }
+ }
+
+ if (!TinselV2)
+ // Redraw contents - held item not displayed as a content.
+ ItemsChanged = true;
+}
/**************************************************************************/
/***/
/**************************************************************************/
-/*---------------------------------------------------------------------*\
-| InvArea() |
-|-----------------------------------------------------------------------|
-| Work out which area of the inventory window the cursor is in. |
-|-----------------------------------------------------------------------|
-| This used to be worked out with appropriately defined magic numbers. |
-| Then the graphic changed and I got it right again. Then the graphic |
-| changed and I got fed up of faffing about. It's probably easier just |
-| to rework all this. |
-\*---------------------------------------------------------------------*/
-enum { I_NOTIN, I_MOVE, I_BODY,
+enum { I_NOTIN, I_HEADER, I_BODY,
I_TLEFT, I_TRIGHT, I_BLEFT, I_BRIGHT,
I_TOP, I_BOTTOM, I_LEFT, I_RIGHT,
I_UP, I_SLIDE_UP, I_SLIDE, I_SLIDE_DOWN, I_DOWN,
@@ -1400,68 +1946,137 @@ enum { I_NOTIN, I_MOVE, I_BODY,
#define TopY InvD[ino].inventoryY
/*---------------------------------*/
+/**
+ * Work out which area of the inventory window the cursor is in.
+ *
+ * This used to be worked out with appropriately defined magic numbers.
+ * Then the graphic changed and I got it right again. Then the graphic
+ * changed and I got fed up of faffing about. It's probably easier just
+ * to rework all this.
+ */
int InvArea(int x, int y) {
- int RightX = MultiRightmost(RectObject) + 1;
- int BottomY = MultiLowest(RectObject) + 1;
-
- // Outside the whole rectangle?
- if (x <= LeftX - EXTRA || x > RightX + EXTRA
- || y <= TopY - EXTRA || y > BottomY + EXTRA)
- return I_NOTIN;
-
- // The bottom line
- if (y > BottomY - 2 - EXTRA) { // Below top of bottom line?
- if (x <= LeftX + 2 + EXTRA)
- return I_BLEFT; // Bottom left corner
- else if (x > RightX - 2 - EXTRA)
- return I_BRIGHT; // Bottom right corner
- else
- return I_BOTTOM; // Just plain bottom
- }
+ if (TinselV2) {
+ int RightX = MultiRightmost(RectObject) - NM_BG_SIZ_X - NM_BG_POS_X - NM_RS_R_INSET;
+ int BottomY = MultiLowest(RectObject) - NM_BG_SIZ_Y - NM_BG_POS_Y - NM_RS_B_INSET;
+
+ // Outside the whole rectangle?
+ if (x <= LeftX || x > RightX || y <= TopY || y > BottomY)
+ return I_NOTIN;
+
+ // The bottom line
+ if (y > BottomY - NM_RS_THICKNESS) {
+ // Below top of bottom line?
+ if (x <= LeftX + NM_RS_THICKNESS)
+ return I_BLEFT; // Bottom left corner
+ else if (x > RightX - NM_RS_THICKNESS)
+ return I_BRIGHT; // Bottom right corner
+ else
+ return I_BOTTOM; // Just plain bottom
+ }
- // The top line
- if (y <= TopY + 2 + EXTRA) { // Above bottom of top line?
- if (x <= LeftX + 2 + EXTRA)
- return I_TLEFT; // Top left corner
- else if (x > RightX - 2 - EXTRA)
- return I_TRIGHT; // Top right corner
- else
- return I_TOP; // Just plain top
- }
+ // The top line
+ if (y <= TopY + NM_RS_THICKNESS) {
+ // Above bottom of top line?
+ if (x <= LeftX + NM_RS_THICKNESS)
+ return I_TLEFT; // Top left corner
+ else if (x > RightX - NM_RS_THICKNESS)
+ return I_TRIGHT; // Top right corner
+ else
+ return I_TOP; // Just plain top
+ }
- // Sides
- if (x <= LeftX + 2 + EXTRA) // Left of right of left side?
- return I_LEFT;
- else if (x > RightX - 2 - EXTRA) // Right of left of right side?
- return I_RIGHT;
+ // Sides
+ if (x <= LeftX + NM_RS_THICKNESS) // Left of right of left side?
+ return I_LEFT;
+ else if (x > RightX - NM_RS_THICKNESS) // Right of left of right side?
+ return I_RIGHT;
+
+ // In the move area?
+ if (y < TopY + NM_MOVE_AREA_B_Y)
+ return I_HEADER;
+
+ // Scroll bits
+ if (!(ino == INV_MENU && cd.bExtraWin)) {
+ if (x > RightX - NM_SLIDE_INSET && x <= RightX - NM_SLIDE_INSET + NM_SLIDE_THICKNESS) {
+ if (y > TopY + NM_UP_ARROW_TOP && y < TopY + NM_UP_ARROW_BOTTOM)
+ return I_UP;
+ if (y > BottomY - NM_DN_ARROW_TOP && y <= BottomY - NM_DN_ARROW_BOTTOM)
+ return I_DOWN;
+
+ /* '3' is a magic adjustment with no apparent sense */
+
+ if (y >= TopY + sliderYmin - 3 && y < TopY + sliderYmax + NM_SLH) {
+ if (y < TopY + sliderYpos - 3)
+ return I_SLIDE_UP;
+ if (y < TopY + sliderYpos + NM_SLH - 3)
+ return I_SLIDE;
+ else
+ return I_SLIDE_DOWN;
+ }
+ }
+ }
+ } else {
+ int RightX = MultiRightmost(RectObject) + 1;
+ int BottomY = MultiLowest(RectObject) + 1;
+
+ // Outside the whole rectangle?
+ if (x <= LeftX - EXTRA || x > RightX + EXTRA
+ || y <= TopY - EXTRA || y > BottomY + EXTRA)
+ return I_NOTIN;
+
+ // The bottom line
+ if (y > BottomY - 2 - EXTRA) { // Below top of bottom line?
+ if (x <= LeftX + 2 + EXTRA)
+ return I_BLEFT; // Bottom left corner
+ else if (x > RightX - 2 - EXTRA)
+ return I_BRIGHT; // Bottom right corner
+ else
+ return I_BOTTOM; // Just plain bottom
+ }
- // From here down still needs fixing up properly
- /*
- * In the move area?
- */
- if (ino != INV_CONF
- && x >= LeftX + M_SW - 2 && x <= RightX - M_SW + 3 &&
- y >= TopY + M_TH - 2 && y < TopY + M_TBB + 2)
- return I_MOVE;
+ // The top line
+ if (y <= TopY + 2 + EXTRA) { // Above bottom of top line?
+ if (x <= LeftX + 2 + EXTRA)
+ return I_TLEFT; // Top left corner
+ else if (x > RightX - 2 - EXTRA)
+ return I_TRIGHT; // Top right corner
+ else
+ return I_TOP; // Just plain top
+ }
- /*
- * Scroll bits
- */
- if (ino == INV_CONF && cd.bExtraWin) {
- } else {
- if (x > RightX - M_IAL + 3 && x <= RightX - M_IAR + 1) {
- if (y > TopY + M_IUT + 1 && y < TopY + M_IUB - 1)
- return I_UP;
- if (y > BottomY - M_IDT + 4 && y <= BottomY - M_IDB + 1)
- return I_DOWN;
-
- if (y >= TopY + slideYmin && y < TopY + slideYmax + M_SH) {
- if (y < TopY + slideY)
- return I_SLIDE_UP;
- if (y < TopY + slideY + M_SH)
- return I_SLIDE;
- else
- return I_SLIDE_DOWN;
+ // Sides
+ if (x <= LeftX + 2 + EXTRA) // Left of right of left side?
+ return I_LEFT;
+ else if (x > RightX - 2 - EXTRA) // Right of left of right side?
+ return I_RIGHT;
+
+ // From here down still needs fixing up properly
+ /*
+ * In the move area?
+ */
+ if (ino != INV_CONF
+ && x >= LeftX + M_SW - 2 && x <= RightX - M_SW + 3 &&
+ y >= TopY + M_TH - 2 && y < TopY + M_TBB + 2)
+ return I_HEADER;
+
+ /*
+ * Scroll bits
+ */
+ if (!(ino == INV_CONF && cd.bExtraWin)) {
+ if (x > RightX - NM_SLIDE_INSET && x <= RightX - NM_SLIDE_INSET + NM_SLIDE_THICKNESS) {
+ if (y > TopY + M_IUT + 1 && y < TopY + M_IUB - 1)
+ return I_UP;
+ if (y > BottomY - M_IDT + 4 && y <= BottomY - M_IDB + 1)
+ return I_DOWN;
+
+ if (y >= TopY + sliderYmin && y < TopY + sliderYmax + M_SH) {
+ if (y < TopY + sliderYpos)
+ return I_SLIDE_UP;
+ if (y < TopY + sliderYpos + M_SH)
+ return I_SLIDE;
+ else
+ return I_SLIDE_DOWN;
+ }
}
}
}
@@ -1503,6 +2118,14 @@ int InvItem(int *x, int *y, bool update) {
return INV_NOICON;
}
+int InvItem(Common::Point &coOrds, bool update) {
+ int x = coOrds.x;
+ int y = coOrds.y;
+ return InvItem(&x, &y, update);
+ coOrds.x = x;
+ coOrds.y = y;
+}
+
/**
* Returns the id of the icon displayed under the given position.
*/
@@ -1524,7 +2147,7 @@ int InvItemId(int x, int y) {
for (col = 0; col < InvD[ino].NoofHicons; col++, item++) {
if (x >= ileft && x < ileft + ITEM_WIDTH &&
y >= itop && y < itop + ITEM_HEIGHT) {
- return InvD[ino].ItemOrder[item];
+ return InvD[ino].contents[item];
}
ileft += ITEM_WIDTH + 1;
@@ -1537,18 +2160,7 @@ int InvItemId(int x, int y) {
/**
* Finds which box the cursor is in.
*/
-static int WhichInvBox(int curX, int curY, bool bSlides) {
- enum {
- MD_YSLIDTOP = 7,
- MD_YSLIDBOT = 18,
- MD_YBUTTOP = 9,
- MD_YBUTBOT = 16,
- MD_XLBUTL = 1,
- MD_XLBUTR = 10,
- MD_XRBUTL = 105,
- MD_XRBUTR = 114
- };
-
+static int WhichMenuBox(int curX, int curY, bool bSlides) {
if (bSlides) {
for (int i = 0; i < numMdSlides; i++) {
if (curY > MultiHighest(mdSlides[i].obj) && curY < MultiLowest(mdSlides[i].obj)
@@ -1561,13 +2173,13 @@ static int WhichInvBox(int curX, int curY, bool bSlides) {
curY -= InvD[ino].inventoryY;
for (int i = 0; i < cd.NumBoxes; i++) {
- switch (cd.Box[i].boxType) {
+ switch (cd.box[i].boxType) {
case SLIDER:
if (bSlides) {
- if (curY >= cd.Box[i].ypos+MD_YBUTTOP && curY < cd.Box[i].ypos+MD_YBUTBOT) {
- if (curX >= cd.Box[i].xpos+MD_XLBUTL && curX < cd.Box[i].xpos+MD_XLBUTR)
+ if (curY >= cd.box[i].ypos+MD_YBUTTOP && curY < cd.box[i].ypos+MD_YBUTBOT) {
+ if (curX >= cd.box[i].xpos+MD_XLBUTL && curX < cd.box[i].xpos+MD_XLBUTR)
return i | IS_LEFT;
- if (curX >= cd.Box[i].xpos+MD_XRBUTL && curX < cd.Box[i].xpos+MD_XRBUTR)
+ if (curX >= cd.box[i].xpos+MD_XRBUTL && curX < cd.box[i].xpos+MD_XRBUTR)
return i | IS_RIGHT;
}
}
@@ -1576,36 +2188,59 @@ static int WhichInvBox(int curX, int curY, bool bSlides) {
case AAGBUT:
case ARSGBUT:
case TOGGLE:
+ case TOGGLE1:
+ case TOGGLE2:
case FLIP:
- if (curY > cd.Box[i].ypos && curY < cd.Box[i].ypos + cd.Box[i].h
- && curX > cd.Box[i].xpos && curX < cd.Box[i].xpos + cd.Box[i].w)
+ if (curY > cd.box[i].ypos && curY < cd.box[i].ypos + cd.box[i].h
+ && curX > cd.box[i].xpos && curX < cd.box[i].xpos + cd.box[i].w)
return i;
break;
+ case ROTATE:
+ if (bNoLanguage)
+ break;
+
+ if (curY > cd.box[i].ypos && curY < cd.box[i].ypos + cd.box[i].h) {
+ // Left one?
+ if (curX > cd.box[i].xpos-ROTX1 && curX < cd.box[i].xpos-ROTX1 + cd.box[i].w) {
+ cd.box[i].bi = IX2_LEFT1;
+ return i;
+ }
+ // Right one?
+ if (curX > cd.box[i].xpos+ROTX1 && curX < cd.box[i].xpos+ROTX1 + cd.box[i].w) {
+ cd.box[i].bi = IX2_RIGHT1;
+ return i;
+ }
+ }
+ break;
+
default:
// 'Normal' box
- if (curY >= cd.Box[i].ypos && curY < cd.Box[i].ypos + cd.Box[i].h
- && curX >= cd.Box[i].xpos && curX < cd.Box[i].xpos + cd.Box[i].w)
+ if (curY >= cd.box[i].ypos && curY < cd.box[i].ypos + cd.box[i].h
+ && curX >= cd.box[i].xpos && curX < cd.box[i].xpos + cd.box[i].w)
return i;
break;
}
}
+ // Slider on extra window
if (cd.bExtraWin) {
- if (curX > 20 + 181 && curX < 20 + 181 + 8 &&
- curY > 24 + 2 && curY < 24 + 139 + 5) {
+ const Common::Rect &r = TinselV2 ?
+ Common::Rect(411, 46, 425, 339) :
+ Common::Rect(20 + 181, 24 + 2, 20 + 181 + 8, 24 + 139 + 5);
- if (curY < 24 + 2 + 5) {
+ if (r.contains(curX, curY)) {
+
+ if (curY < (r.top + (TinselV2 ? 18 : 5)))
return IB_UP;
- } else if (curY > 24 + 139) {
+ else if (curY > (r.bottom - (TinselV2 ? 18 : 5)))
return IB_DOWN;
- } else if (curY+InvD[ino].inventoryY >= slideY && curY+InvD[ino].inventoryY < slideY + 5) {
- return IB_SLIDE;
- } else if (curY+InvD[ino].inventoryY < slideY) {
+ else if (curY + InvD[ino].inventoryY < sliderYpos)
return IB_SLIDE_UP;
- } else if (curY+InvD[ino].inventoryY >= slideY + 5) {
+ else if (curY + InvD[ino].inventoryY >= sliderYpos + NM_SLH)
return IB_SLIDE_DOWN;
- }
+ else
+ return IB_SLIDE;
}
}
@@ -1616,10 +2251,13 @@ static int WhichInvBox(int curX, int curY, bool bSlides) {
/***/
/**************************************************************************/
+#define ROTX1 60 // Rotate button's offsets from the centre
+
/**
- * InBoxes
+ * InvBoxes
*/
void InvBoxes(bool InBody, int curX, int curY) {
+ static int rotateIndex = -1;
int index; // Box pointed to on this call
const FILM *pfilm;
@@ -1627,7 +2265,7 @@ void InvBoxes(bool InBody, int curX, int curY) {
if (!InBody)
index = -1;
else {
- index = WhichInvBox(curX, curY, false);
+ index = WhichMenuBox(curX, curY, false);
}
// If no icon pointed to, or points to (logical position of)
@@ -1646,28 +2284,53 @@ void InvBoxes(bool InBody, int curX, int curY) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
iconArray[HL1] = NULL;
}
- if ((cd.Box[cd.pointBox].boxType == ARSBUT && cd.selBox != NOBOX) ||
-///* I don't agree */ cd.Box[cd.pointBox].boxType == RGROUP ||
- cd.Box[cd.pointBox].boxType == AATBUT ||
- cd.Box[cd.pointBox].boxType == AABUT) {
- iconArray[HL1] = RectangleObject(BackPal(), COL_HILIGHT, cd.Box[cd.pointBox].w, cd.Box[cd.pointBox].h);
+ if ((cd.box[cd.pointBox].boxType == ARSBUT && cd.selBox != NOBOX) ||
+///* I don't agree */ cd.box[cd.pointBox].boxType == RGROUP ||
+ cd.box[cd.pointBox].boxType == AATBUT ||
+ cd.box[cd.pointBox].boxType == AABUT) {
+ iconArray[HL1] = RectangleObject(BgPal(),
+ (TinselV2 ? HighlightColour() : COL_HILIGHT),
+ cd.box[cd.pointBox].w, cd.box[cd.pointBox].h);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
MultiSetAniXY(iconArray[HL1],
- InvD[ino].inventoryX + cd.Box[cd.pointBox].xpos,
- InvD[ino].inventoryY + cd.Box[cd.pointBox].ypos);
+ InvD[ino].inventoryX + cd.box[cd.pointBox].xpos,
+ InvD[ino].inventoryY + cd.box[cd.pointBox].ypos);
MultiSetZPosition(iconArray[HL1], Z_INV_ICONS+1);
}
- else if (cd.Box[cd.pointBox].boxType == AAGBUT ||
- cd.Box[cd.pointBox].boxType == ARSGBUT ||
- cd.Box[cd.pointBox].boxType == TOGGLE) {
- pfilm = (const FILM *)LockMem(winPartsf);
-
- iconArray[HL1] = AddObject(&pfilm->reels[cd.Box[cd.pointBox].bi+HIGRAPH], -1);
+ else if (cd.box[cd.pointBox].boxType == AAGBUT ||
+ cd.box[cd.pointBox].boxType == ARSGBUT ||
+ cd.box[cd.pointBox].boxType == TOGGLE ||
+ cd.box[cd.pointBox].boxType == TOGGLE1 ||
+ cd.box[cd.pointBox].boxType == TOGGLE2) {
+ pfilm = (const FILM *)LockMem(hWinParts);
+
+ iconArray[HL1] = AddObject(&pfilm->reels[cd.box[cd.pointBox].bi+HIGRAPH], -1);
MultiSetAniXY(iconArray[HL1],
- InvD[ino].inventoryX + cd.Box[cd.pointBox].xpos,
- InvD[ino].inventoryY + cd.Box[cd.pointBox].ypos);
+ InvD[ino].inventoryX + cd.box[cd.pointBox].xpos,
+ InvD[ino].inventoryY + cd.box[cd.pointBox].ypos);
MultiSetZPosition(iconArray[HL1], Z_INV_ICONS+1);
}
+ else if (cd.box[cd.pointBox].boxType == ROTATE) {
+ if (bNoLanguage)
+ return;
+
+ pfilm = (const FILM *)LockMem(hWinParts);
+
+ rotateIndex = cd.box[cd.pointBox].bi;
+ if (rotateIndex == IX2_LEFT1) {
+ iconArray[HL1] = AddObject(&pfilm->reels[IX2_LEFT2], -1 );
+ MultiSetAniXY(iconArray[HL1],
+ InvD[ino].inventoryX + cd.box[cd.pointBox].xpos - ROTX1,
+ InvD[ino].inventoryY + cd.box[cd.pointBox].ypos);
+ MultiSetZPosition(iconArray[HL1], Z_INV_ICONS+1);
+ } else if (rotateIndex == IX2_RIGHT1) {
+ iconArray[HL1] = AddObject(&pfilm->reels[IX2_RIGHT2], -1);
+ MultiSetAniXY(iconArray[HL1],
+ InvD[ino].inventoryX + cd.box[cd.pointBox].xpos + ROTX1,
+ InvD[ino].inventoryY + cd.box[cd.pointBox].ypos);
+ MultiSetZPosition(iconArray[HL1], Z_INV_ICONS + 1);
+ }
+ }
}
}
@@ -1682,10 +2345,10 @@ static void ButtonPress(CORO_PARAM, CONFBOX *box) {
assert(box->boxType == AAGBUT || box->boxType == ARSGBUT);
// Replace highlight image with normal image
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
if (iconArray[HL1] != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
iconArray[HL1] = AddObject(&pfilm->reels[box->bi+NORMGRAPH], -1);
MultiSetAniXY(iconArray[HL1], InvD[ino].inventoryX + box->xpos, InvD[ino].inventoryY + box->ypos);
MultiSetZPosition(iconArray[HL1], Z_INV_ICONS+1);
@@ -1696,7 +2359,7 @@ static void ButtonPress(CORO_PARAM, CONFBOX *box) {
return;
// Replace normal image with depresses image
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
iconArray[HL1] = AddObject(&pfilm->reels[box->bi+DOWNGRAPH], -1);
MultiSetAniXY(iconArray[HL1], InvD[ino].inventoryX + box->xpos, InvD[ino].inventoryY + box->ypos);
@@ -1708,7 +2371,7 @@ static void ButtonPress(CORO_PARAM, CONFBOX *box) {
return;
// Replace depressed image with normal image
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
iconArray[HL1] = AddObject(&pfilm->reels[box->bi+NORMGRAPH], -1);
MultiSetAniXY(iconArray[HL1], InvD[ino].inventoryX + box->xpos, InvD[ino].inventoryY + box->ypos);
@@ -1727,7 +2390,8 @@ static void ButtonToggle(CORO_PARAM, CONFBOX *box) {
const FILM *pfilm;
- assert(box->boxType == TOGGLE);
+ assert((box->boxType == TOGGLE) || (box->boxType == TOGGLE1)
+ || (box->boxType == TOGGLE2));
// Remove hilight image
if (iconArray[HL1] != NULL) {
@@ -1741,7 +2405,7 @@ static void ButtonToggle(CORO_PARAM, CONFBOX *box) {
return;
// Add depressed image
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
iconArray[HL1] = AddObject(&pfilm->reels[box->bi+DOWNGRAPH], -1);
MultiSetAniXY(iconArray[HL1], InvD[ino].inventoryX + box->xpos, InvD[ino].inventoryY + box->ypos);
MultiSetZPosition(iconArray[HL1], Z_INV_ICONS+1);
@@ -1760,7 +2424,7 @@ static void ButtonToggle(CORO_PARAM, CONFBOX *box) {
Select(cd.selBox, true);
// New state, depressed image
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
if (iconArray[HL1] != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
iconArray[HL1] = AddObject(&pfilm->reels[box->bi+DOWNGRAPH], -1);
@@ -1782,7 +2446,7 @@ static void ButtonToggle(CORO_PARAM, CONFBOX *box) {
return;
// New state, highlighted
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
if (iconArray[HL1] != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL1]);
iconArray[HL1] = AddObject(&pfilm->reels[box->bi+HIGRAPH], -1);
@@ -1795,7 +2459,6 @@ static void ButtonToggle(CORO_PARAM, CONFBOX *box) {
/**
* Monitors for POINTED event for inventory icons.
*/
-
void InvLabels(bool InBody, int aniX, int aniY) {
int index; // Icon pointed to on this call
INV_OBJECT *invObj;
@@ -1809,7 +2472,7 @@ void InvLabels(bool InBody, int aniX, int aniY) {
if (index >= InvD[ino].NoofItems)
index = INV_NOICON;
else
- index = InvD[ino].ItemOrder[index];
+ index = InvD[ino].contents[index];
}
}
@@ -1819,9 +2482,9 @@ void InvLabels(bool InBody, int aniX, int aniY) {
pointedIcon = INV_NOICON;
} else if (index != pointedIcon) {
// A new icon is pointed to - run its script with POINTED event
- invObj = findInvObject(index);
+ invObj = GetInvObject(index);
if (invObj->hScript)
- RunInvTinselCode(invObj, POINTED, BE_NONE, index);
+ InvTinselEvent(invObj, POINTED, PLR_NOEVENT, index);
pointedIcon = index;
}
}
@@ -1838,7 +2501,7 @@ void InvLabels(bool InBody, int aniX, int aniY) {
*/
void AdjustTop(void) {
int tMissing, bMissing, nMissing;
- int nslideY;
+ int nsliderYpos;
int rowsWanted;
int slideRange;
int n, i;
@@ -1862,22 +2525,22 @@ void AdjustTop(void) {
bMissing = (rowsWanted > InvD[ino].NoofVicons) ? rowsWanted - InvD[ino].NoofVicons : 0;
nMissing = tMissing + bMissing;
- slideRange = slideYmax - slideYmin;
+ slideRange = sliderYmax - sliderYmin;
if (!tMissing)
- nslideY = slideYmin;
+ nsliderYpos = sliderYmin;
else if (!bMissing)
- nslideY = slideYmax;
+ nsliderYpos = sliderYmax;
else {
- nslideY = tMissing*slideRange/nMissing;
- nslideY += slideYmin;
+ nsliderYpos = tMissing*slideRange/nMissing;
+ nsliderYpos += sliderYmin;
}
if (nMissing) {
n = InvD[ino].FirstDisp - tMissing*InvD[ino].NoofHicons;
for (i = 0; i <= nMissing; i++, n += InvD[ino].NoofHicons) {
slideStuff[i].n = n;
- slideStuff[i].y = (i*slideRange/nMissing) + slideYmin;
+ slideStuff[i].y = (i*slideRange/nMissing) + sliderYmin;
}
if (slideStuff[0].n < 0)
slideStuff[0].n = 0;
@@ -1885,13 +2548,13 @@ void AdjustTop(void) {
slideStuff[i].n = -1;
} else {
slideStuff[0].n = 0;
- slideStuff[0].y = slideYmin;
+ slideStuff[0].y = sliderYmin;
slideStuff[1].n = -1;
}
- if (nslideY != slideY) {
- MultiMoveRelXY(SlideObject, 0, nslideY - slideY);
- slideY = nslideY;
+ if (nsliderYpos != sliderYpos) {
+ MultiMoveRelXY(SlideObject, 0, nsliderYpos - sliderYpos);
+ sliderYpos = nsliderYpos;
}
}
@@ -1904,13 +2567,13 @@ OBJECT *AddInvObject(int num, const FREEL **pfreel, const FILM **pfilm) {
IMAGE *pim; // ... you get the picture
OBJECT *pPlayObj; // The object we insert
- invObj = findInvObject(num);
+ invObj = GetInvObject(num);
// Get pointer to image
- pim = GetImageFromFilm(invObj->hFilm, 0, pfreel, &pmi, pfilm);
+ pim = GetImageFromFilm(invObj->hIconFilm, 0, pfreel, &pmi, pfilm);
// Poke in the background palette
- pim->hImgPal = TO_LE_32(BackPal());
+ pim->hImgPal = TO_LE_32(BgPal());
// Set up the multi-object
pPlayObj = MultiInitObject(pmi);
@@ -1923,7 +2586,7 @@ OBJECT *AddInvObject(int num, const FREEL **pfreel, const FILM **pfilm) {
* Create display objects for the displayed icons in an inventory window.
*/
void FillInInventory(void) {
- int Index; // Index into ItemOrder[]
+ int Index; // Index into contents[]
int n = 0; // index into iconArray[]
int xpos, ypos;
int row, col;
@@ -1945,9 +2608,9 @@ void FillInInventory(void) {
for (col = 0; col < InvD[ino].NoofHicons; col++) {
if (Index >= InvD[ino].NoofItems)
break;
- else if (InvD[ino].ItemOrder[Index] != HeldItem) {
+ else if (InvD[ino].contents[Index] != HeldItem) {
// Create a display object and position it
- iconArray[n] = AddInvObject(InvD[ino].ItemOrder[Index], &pfr, &pfilm);
+ iconArray[n] = AddInvObject(InvD[ino].contents[Index], &pfr, &pfilm);
MultiSetAniXY(iconArray[n], InvD[ino].inventoryX + xpos , InvD[ino].inventoryY + ypos);
MultiSetZPosition(iconArray[n], Z_INV_ICONS);
@@ -1961,45 +2624,74 @@ void FillInInventory(void) {
}
}
+enum {FROM_HANDLE, FROM_STRING};
+
/**
* Set up a rectangle as the background to the inventory window.
* Additionally, sticks the window title up.
*/
-
-enum {FROM_HANDLE, FROM_STRING};
-
-void AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV, int textFrom) {
+static void AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV, int textFrom) {
// Why not 2 ????
- int width = TLwidth + extraH + TRwidth - 3;
- int height = TLheight + extraV + BLheight - 3;
+ int width = TLwidth + extraH + TRwidth + NM_BG_SIZ_X;
+ int height = TLheight + extraV + BLheight + NM_BG_SIZ_Y;
// Create a rectangle object
RectObject = *rect = TranslucentObject(width, height);
// add it to display list and position it
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), *rect);
- MultiSetAniXY(*rect, InvD[ino].inventoryX + 1, InvD[ino].inventoryY + 1);
+ MultiSetAniXY(*rect, InvD[ino].inventoryX + NM_BG_POS_X,
+ InvD[ino].inventoryY + NM_BG_POS_Y);
MultiSetZPosition(*rect, Z_INV_BRECT);
+ if (title == NULL)
+ return;
+
// Create text object using title string
+ CoroContext dummyCoro;
if (textFrom == FROM_HANDLE) {
- LoadStringRes(InvD[ino].hInvTitle, tBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), 0,
+ LoadStringRes(InvD[ino].hInvTitle, TextBufferAddr(), TBUFSZ);
+ *title = ObjectTextOut(dummyCoro, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + width/2, InvD[ino].inventoryY + M_TOFF,
- hTagFontHandle(), TXT_CENTRE);
+ GetTagFontHandle(), TXT_CENTRE);
assert(*title); // Inventory title string produced NULL text
MultiSetZPosition(*title, Z_INV_HTEXT);
} else if (textFrom == FROM_STRING && cd.ixHeading != NO_HEADING) {
- LoadStringRes(configStrings[cd.ixHeading], tBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), 0,
+ LoadStringRes(configStrings[cd.ixHeading], TextBufferAddr(), TBUFSZ);
+ *title = ObjectTextOut(dummyCoro, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + width/2, InvD[ino].inventoryY + M_TOFF,
- hTagFontHandle(), TXT_CENTRE);
+ GetTagFontHandle(), TXT_CENTRE);
assert(*title); // Inventory title string produced NULL text
MultiSetZPosition(*title, Z_INV_HTEXT);
}
}
/**
+ * Set up a rectangle as the background to the inventory window.
+ */
+static void AddBackground(OBJECT **rect, int extraH, int extraV) {
+ AddBackground(rect, NULL, extraH, extraV, 0);
+}
+
+/**
+ * Adds a title for a dialog
+ */
+static void AddTitle(POBJECT *title, int extraH) {
+ int width = TLwidth + extraH + TRwidth + NM_BG_SIZ_X;
+
+ // Create text object using title string
+ if (InvD[ino].hInvTitle != (SCNHANDLE)NO_HEADING) {
+ LoadStringRes(InvD[ino].hInvTitle, TextBufferAddr(), TBUFSZ);
+ *title = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ InvD[ino].inventoryX + (width/2)+NM_BG_POS_X, InvD[ino].inventoryY + NM_TOFF,
+ GetTagFontHandle(), TXT_CENTRE, 0);
+ assert(*title);
+ MultiSetZPosition(*title, Z_INV_HTEXT);
+ }
+}
+
+
+/**
* Insert a part of the inventory window frame onto the display list.
*/
static OBJECT *AddObject(const FREEL *pfreel, int num) {
@@ -2011,17 +2703,17 @@ static OBJECT *AddObject(const FREEL *pfreel, int num) {
pim = GetImageFromReel(pfreel, &pmi);
// Poke in the background palette
- pim->hImgPal = TO_LE_32(BackPal());
+ pim->hImgPal = TO_LE_32(BgPal());
// Horrible bodge involving global variables to save
// width and/or height of some window frame components
if (num == TL) {
TLwidth = FROM_LE_16(pim->imgWidth);
- TLheight = FROM_LE_16(pim->imgHeight);
+ TLheight = FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK;
} else if (num == TR) {
TRwidth = FROM_LE_16(pim->imgWidth);
} else if (num == BL) {
- BLheight = FROM_LE_16(pim->imgHeight);
+ BLheight = FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK;
}
// Set up and insert the multi-object
@@ -2034,70 +2726,85 @@ static OBJECT *AddObject(const FREEL *pfreel, int num) {
/**
* Display the scroll bar slider.
*/
+
void AddSlider(OBJECT **slide, const FILM *pfilm) {
SlideObject = *slide = AddObject(&pfilm->reels[IX_SLIDE], -1);
- MultiSetAniXY(*slide, MultiRightmost(RectObject)-M_SXOFF+2, InvD[ino].inventoryY + slideY);
+ MultiSetAniXY(*slide, MultiRightmost(RectObject) + (TinselV2 ? NM_SLX : -M_SXOFF + 2) - 1,
+ InvD[ino].inventoryY + sliderYpos);
MultiSetZPosition(*slide, Z_INV_MFRAME);
}
-enum {
- SLIDE_RANGE = 81,
- SLIDE_MINX = 8,
- SLIDE_MAXX = 8+SLIDE_RANGE,
-
- MDTEXT_YOFF = 6,
- MDTEXT_XOFF = -4
-};
-
/**
* Display a box with some text in it.
*/
void AddBox(int *pi, int i) {
- int x = InvD[ino].inventoryX + cd.Box[i].xpos;
- int y = InvD[ino].inventoryY + cd.Box[i].ypos;
- int *pival = cd.Box[i].ival;
+ int x = InvD[ino].inventoryX + cd.box[i].xpos;
+ int y = InvD[ino].inventoryY + cd.box[i].ypos;
+ int *pival = cd.box[i].ival;
int xdisp;
- const FILM *pfilm;
+ const FILM *pFilm;
- switch (cd.Box[i].boxType) {
+ switch (cd.box[i].boxType) {
default:
+ // Ignore if it's a blank scene hopper box
+ if (TinselV2 && (cd.box[i].textMethod == TM_NONE))
+ break;
+
// Give us a box
- iconArray[*pi] = RectangleObject(BackPal(), COL_BOX, cd.Box[i].w, cd.Box[i].h);
+ iconArray[*pi] = RectangleObject(BgPal(), TinselV2 ? BoxColour() : COL_BOX,
+ cd.box[i].w, cd.box[i].h);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), iconArray[*pi]);
MultiSetAniXY(iconArray[*pi], x, y);
- MultiSetZPosition(iconArray[*pi], Z_INV_BRECT+1);
+ MultiSetZPosition(iconArray[*pi], Z_INV_BRECT + 1);
*pi += 1;
// Stick in the text
- if (cd.Box[i].ixText == USE_POINTER) {
- if (cd.Box[i].boxText != NULL) {
- if (cd.Box[i].boxType == RGROUP) {
- iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.Box[i].boxText, 0,
+ if ((cd.box[i].textMethod == TM_POINTER) ||
+ (!TinselV2 && (cd.box[i].ixText == USE_POINTER))) {
+ if (cd.box[i].boxText != NULL) {
+ if (cd.box[i].boxType == RGROUP) {
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
#ifdef JAPAN
- x+2, y+2, hTagFontHandle(), 0);
+ x + 2, y+2, GetTagFontHandle(), 0);
#else
- x+2, y, hTagFontHandle(), 0);
+ x + 2, y + TYOFF, GetTagFontHandle(), 0);
#endif
} else {
- iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.Box[i].boxText, 0,
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
#ifdef JAPAN
// Note: it never seems to go here!
- x + cd.Box[i].w/2, y+2, hTagFontHandle(), TXT_CENTRE);
+ x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTRE);
#else
- x + cd.Box[i].w/2, y, hTagFontHandle(), TXT_CENTRE);
+ x + cd.box[i].w / 2, y + TYOFF, GetTagFontHandle(), TXT_CENTRE);
#endif
}
+
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
}
} else {
- LoadStringRes(configStrings[cd.Box[i].ixText], tBufferAddr(), TBUFSZ);
- assert(cd.Box[i].boxType != RGROUP); // You'll need to add some code!
- iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), 0,
+ if (TinselV2) {
+ if (cd.box[i].textMethod == TM_INDEX)
+ LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
+ else {
+ assert(cd.box[i].textMethod == TM_STRINGNUM);
+ LoadStringRes(cd.box[i].ixText, TextBufferAddr(), TBUFSZ);
+ }
+ } else {
+ LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
+ assert(cd.box[i].boxType != RGROUP); // You'll need to add some code!
+ }
+
+ if (TinselV2 && (cd.box[i].boxType == RGROUP))
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ 0, x + 2, y + TYOFF, GetTagFontHandle(), 0, 0);
+ else
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0,
#ifdef JAPAN
- x + cd.Box[i].w/2, y+2, hTagFontHandle(), TXT_CENTRE);
+ x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTRE);
#else
- x + cd.Box[i].w/2, y, hTagFontHandle(), TXT_CENTRE);
+ x + cd.box[i].w / 2, y + TYOFF, GetTagFontHandle(), TXT_CENTRE);
#endif
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
@@ -2106,118 +2813,212 @@ void AddBox(int *pi, int i) {
case AAGBUT:
case ARSGBUT:
- pfilm = (const FILM *)LockMem(winPartsf);
+ pFilm = (const FILM *)LockMem(hWinParts);
- iconArray[*pi] = AddObject(&pfilm->reels[cd.Box[i].bi+NORMGRAPH], -1);
+ iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi + NORMGRAPH], -1);
MultiSetAniXY(iconArray[*pi], x, y);
- MultiSetZPosition(iconArray[*pi], Z_INV_BRECT+1);
+ MultiSetZPosition(iconArray[*pi], Z_INV_BRECT + 1);
*pi += 1;
break;
+#if defined(USE_3FLAGS) || defined(USE_4FLAGS) || defined(USE_5FLAGS)
case FRGROUP:
- assert((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS));
assert(flagFilm != 0); // Language flags not declared!
- pfilm = (const FILM *)LockMem(flagFilm);
+ pFilm = (const FILM *)LockMem(flagFilm);
- if (bAmerica && cd.Box[i].bi == FIX_UK)
- cd.Box[i].bi = FIX_USA;
+ if (bAmerica && cd.box[i].bi == FIX_UK)
+ cd.box[i].bi = FIX_USA;
- iconArray[*pi] = AddObject(&pfilm->reels[cd.Box[i].bi], -1);
+ iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi], -1);
MultiSetAniXY(iconArray[*pi], x, y);
MultiSetZPosition(iconArray[*pi], Z_INV_BRECT+2);
*pi += 1;
break;
-
+#endif
case FLIP:
- pfilm = (const FILM *)LockMem(winPartsf);
+ pFilm = (const FILM *)LockMem(hWinParts);
- if (*(cd.Box[i].ival))
- iconArray[*pi] = AddObject(&pfilm->reels[cd.Box[i].bi], -1);
+ if (*(cd.box[i].ival))
+ iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi], -1);
else
- iconArray[*pi] = AddObject(&pfilm->reels[cd.Box[i].bi+1], -1);
+ iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi+1], -1);
MultiSetAniXY(iconArray[*pi], x, y);
MultiSetZPosition(iconArray[*pi], Z_INV_BRECT+1);
*pi += 1;
// Stick in the text
- assert(cd.Box[i].ixText != USE_POINTER);
- LoadStringRes(configStrings[cd.Box[i].ixText], tBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), 0,
- x+MDTEXT_XOFF, y+MDTEXT_YOFF, hTagFontHandle(), TXT_RIGHT);
+ if (TinselV2) {
+ assert(cd.box[i].textMethod == TM_INDEX);
+ LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
+ } else {
+ assert(cd.box[i].ixText != USE_POINTER);
+ LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
+ }
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
break;
case TOGGLE:
- pfilm = (const FILM *)LockMem(winPartsf);
+ case TOGGLE1:
+ case TOGGLE2:
+ pFilm = (const FILM *)LockMem(hWinParts);
- cd.Box[i].bi = *(cd.Box[i].ival) ? IX_TICK1 : IX_CROSS1;
- iconArray[*pi] = AddObject(&pfilm->reels[cd.Box[i].bi+NORMGRAPH], -1);
+ cd.box[i].bi = *(cd.box[i].ival) ? IX_TICK1 : IX_CROSS1;
+ iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi + NORMGRAPH], -1);
MultiSetAniXY(iconArray[*pi], x, y);
MultiSetZPosition(iconArray[*pi], Z_INV_BRECT+1);
*pi += 1;
// Stick in the text
- assert(cd.Box[i].ixText != USE_POINTER);
- LoadStringRes(configStrings[cd.Box[i].ixText], tBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), 0,
- x+MDTEXT_XOFF, y+MDTEXT_YOFF, hTagFontHandle(), TXT_RIGHT);
+ if (TinselV2) {
+ assert(cd.box[i].textMethod == TM_INDEX);
+ LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
+ } else {
+ assert(cd.box[i].ixText != USE_POINTER);
+ LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
+ }
+
+ if (cd.box[i].boxType == TOGGLE2) {
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
+ GetTagFontHandle(), TXT_CENTRE, 0);
+ } else {
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF,
+ GetTagFontHandle(), TXT_RIGHT, 0);
+ }
+
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
break;
case SLIDER:
- pfilm = (const FILM *)LockMem(winPartsf);
- xdisp = SLIDE_RANGE*(*pival)/cd.Box[i].w;
+ pFilm = (const FILM *)LockMem(hWinParts);
+ xdisp = SLIDE_RANGE*(*pival)/cd.box[i].w;
- iconArray[*pi] = AddObject(&pfilm->reels[IX_MDGROOVE], -1);
+ iconArray[*pi] = AddObject(&pFilm->reels[IX_MDGROOVE], -1);
MultiSetAniXY(iconArray[*pi], x, y);
MultiSetZPosition(iconArray[*pi], Z_MDGROOVE);
*pi += 1;
- iconArray[*pi] = AddObject(&pfilm->reels[IX_MDSLIDER], -1);
+ iconArray[*pi] = AddObject(&pFilm->reels[IX_MDSLIDER], -1);
MultiSetAniXY(iconArray[*pi], x+SLIDE_MINX+xdisp, y);
MultiSetZPosition(iconArray[*pi], Z_MDSLIDER);
assert(numMdSlides < MAXSLIDES);
mdSlides[numMdSlides].num = i;
- mdSlides[numMdSlides].min = x+SLIDE_MINX;
- mdSlides[numMdSlides].max = x+SLIDE_MAXX;
+ mdSlides[numMdSlides].min = x + SLIDE_MINX;
+ mdSlides[numMdSlides].max = x + SLIDE_MAXX;
mdSlides[numMdSlides++].obj = iconArray[*pi];
*pi += 1;
// Stick in the text
- assert(cd.Box[i].ixText != USE_POINTER);
- LoadStringRes(configStrings[cd.Box[i].ixText], tBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), 0,
- x+MDTEXT_XOFF, y+MDTEXT_YOFF, hTagFontHandle(), TXT_RIGHT);
+ if (TinselV2) {
+ assert(cd.box[i].textMethod == TM_INDEX);
+ LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
+ } else {
+ assert(cd.box[i].ixText != USE_POINTER);
+ LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
+ }
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, x+MDTEXT_XOFF, y+MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
break;
+
+ case ROTATE:
+ pFilm = (const FILM *)LockMem(hWinParts);
+
+ // Left one
+ if (!bNoLanguage) {
+ iconArray[*pi] = AddObject(&pFilm->reels[IX2_LEFT1], -1);
+ MultiSetAniXY(iconArray[*pi], x-ROTX1, y);
+ MultiSetZPosition(iconArray[*pi], Z_INV_BRECT + 1);
+ *pi += 1;
+
+ // Right one
+ iconArray[*pi] = AddObject( &pFilm->reels[IX2_RIGHT1], -1);
+ MultiSetAniXY(iconArray[*pi], x + ROTX1, y);
+ MultiSetZPosition(iconArray[*pi], Z_INV_BRECT + 1);
+ *pi += 1;
+
+ // Stick in the text
+ assert(cd.box[i].textMethod == TM_INDEX);
+ LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
+ GetTagFontHandle(), TXT_CENTRE, 0);
+ MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
+ *pi += 1;
+ }
+
+ // Current language's text
+ if (LanguageDesc(displayedLanguage) == 0)
+ break;
+
+ LoadStringRes(LanguageDesc(displayedLanguage), TextBufferAddr(), TBUFSZ);
+ iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ x + cd.box[i].w / 2, y + ROT_YOFF, GetTagFontHandle(), TXT_CENTRE, 0);
+ MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
+ *pi += 1;
+
+ // Current language's flag
+ pFilm = (const FILM *)LockMem(LanguageFlag(displayedLanguage));
+ iconArray[*pi] = AddObject(&pFilm->reels[0], -1);
+ MultiSetAniXY(iconArray[*pi], x + FLAGX, y + FLAGY);
+ MultiSetZPosition(iconArray[*pi], Z_INV_BRECT + 1);
+ *pi += 1;
+ break;
}
}
/**
* Display some boxes.
*/
-static void AddBoxes(bool posnSlide) {
- int oCount = NUMHL; // Object count - allow for HL1, HL2 etc.
+static void AddBoxes(bool bPosnSlide) {
+ int objCount = NUMHL; // Object count - allow for HL1, HL2 etc.
DumpIconArray();
numMdSlides = 0;
for (int i = 0; i < cd.NumBoxes; i++) {
- AddBox(&oCount, i);
+ AddBox(&objCount, i);
}
if (cd.bExtraWin) {
- if (posnSlide)
- slideY = slideYmin + (cd.fileBase*(slideYmax-slideYmin))/(MAX_SFILES-NUM_SL_RGROUP);
- MultiSetAniXY(SlideObject, InvD[ino].inventoryX + 24 + 179, slideY);
+ if (bPosnSlide && !TinselV2)
+ sliderYpos = sliderYmin + (cd.extraBase*(sliderYmax-sliderYmin))/(MAX_SAVED_FILES-NUM_RGROUP_BOXES);
+ else if (bPosnSlide) {
+ // Tinsel 2 bPosnSlide code
+ int lastY = sliderYpos;
+
+ if (cd.box == loadBox || cd.box == saveBox)
+ sliderYpos = sliderYmin + (cd.extraBase * (sliderRange)) /
+ (MAX_SAVED_FILES - NUM_RGROUP_BOXES);
+ else if (cd.box == hopperBox1) {
+ if (numScenes <= NUM_RGROUP_BOXES)
+ sliderYpos = sliderYmin;
+ else
+ sliderYpos = sliderYmin + (cd.extraBase*(sliderRange))/(numScenes-NUM_RGROUP_BOXES);
+ } else if (cd.box == hopperBox2) {
+ if (numEntries <= NUM_RGROUP_BOXES)
+ sliderYpos = sliderYmin;
+ else
+ sliderYpos = sliderYmin + (cd.extraBase * (sliderRange)) /
+ (numEntries-NUM_RGROUP_BOXES);
+ }
+
+ MultiMoveRelXY(SlideObject, 0, sliderYpos - lastY);
+ }
+
+ if (!TinselV2)
+ MultiSetAniXY(SlideObject, InvD[ino].inventoryX + 24 + 179, sliderYpos);
}
- assert(oCount < MAX_ICONS); // added too many icons
+ assert(objCount < MAX_ICONS); // added too many icons
}
/**
@@ -2225,7 +3026,7 @@ static void AddBoxes(bool posnSlide) {
*/
void AddEWSlider(OBJECT **slide, const FILM *pfilm) {
SlideObject = *slide = AddObject(&pfilm->reels[IX_SLIDE], -1);
- MultiSetAniXY(*slide, InvD[ino].inventoryX + 24 + 127, slideY);
+ MultiSetAniXY(*slide, InvD[ino].inventoryX + 24 + 127, sliderYpos);
MultiSetZPosition(*slide, Z_INV_MFRAME);
}
@@ -2237,54 +3038,69 @@ int AddExtraWindow(int x, int y, OBJECT **retObj) {
const FILM *pfilm;
// Get the frame's data
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
- x += 20;
- y += 24;
+ x += TinselV2 ? 30 : 20;
+ y += TinselV2 ? 38 : 24;
-// Draw the four corners
+ // Draw the four corners
retObj[n] = AddObject(&pfilm->reels[IX_RTL], -1); // Top left
MultiSetAniXY(retObj[n], x, y);
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_NTR], -1); // Top right
- MultiSetAniXY(retObj[n], x + 152, y);
+ MultiSetAniXY(retObj[n], x + (TinselV2 ? TLwidth + 312 : 152), y);
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_BL], -1); // Bottom left
- MultiSetAniXY(retObj[n], x, y + 124);
+ MultiSetAniXY(retObj[n], x, y + (TinselV2 ? TLheight + 208 : 124));
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_BR], -1); // Bottom right
- MultiSetAniXY(retObj[n], x + 152, y + 124);
+ MultiSetAniXY(retObj[n], x + (TinselV2 ? TLwidth + 312 : 152),
+ y + (TinselV2 ? TLheight + 208 : 124));
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
-// Draw the edges
+ // Draw the edges
retObj[n] = AddObject(&pfilm->reels[IX_H156], -1); // Top
- MultiSetAniXY(retObj[n], x + 6, y);
+ MultiSetAniXY(retObj[n], x + (TinselV2 ? TLwidth : 6), y + NM_TBT);
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_H156], -1); // Bottom
- MultiSetAniXY(retObj[n], x + 6, y + 143);
+ MultiSetAniXY(retObj[n], x + (TinselV2 ? TLwidth : 6), y +
+ (TinselV2 ? TLheight + 208 + BLheight + NM_BSY : 143));
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_V104], -1); // Left
- MultiSetAniXY(retObj[n], x, y + 20);
+ MultiSetAniXY(retObj[n], x + NM_LSX, y + (TinselV2 ? TLheight : 20));
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_V104], -1); // Right 1
- MultiSetAniXY(retObj[n], x + 179, y + 20);
+ MultiSetAniXY(retObj[n], x + (TinselV2 ? TLwidth + 312 + TRwidth + NM_RSX : 179),
+ y + (TinselV2 ? TLheight : 20));
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
retObj[n] = AddObject(&pfilm->reels[IX_V104], -1); // Right 2
- MultiSetAniXY(retObj[n], x + 188, y + 20);
+ MultiSetAniXY(retObj[n], x + (TinselV2 ? TLwidth + 312 + TRwidth + NM_SBL : 188),
+ y + (TinselV2 ? TLheight : 20));
MultiSetZPosition(retObj[n], Z_INV_MFRAME);
n++;
- slideY = slideYmin = y + 9;
- slideYmax = y + 134;
- AddEWSlider(&retObj[n++], pfilm);
+ if (TinselV2) {
+ sliderYpos = sliderYmin = y + 27;
+ sliderYmax = y + 273;
+
+ retObj[n++] = SlideObject = AddObject( &pfilm->reels[IX_SLIDE], -1);
+ MultiSetAniXY(SlideObject,
+ x + TLwidth + 320 + TRwidth - NM_BG_POS_X + NM_BG_SIZ_X - 2,
+ sliderYpos);
+ MultiSetZPosition(SlideObject, Z_INV_MFRAME);
+ } else {
+ sliderYpos = sliderYmin = y + 9;
+ sliderYmax = y + 134;
+ AddEWSlider(&retObj[n++], pfilm);
+ }
return n;
}
@@ -2324,7 +3140,7 @@ void ConstructInventory(InventoryType filling) {
}
// Get the frame's data
- pfilm = (const FILM *)LockMem(winPartsf);
+ pfilm = (const FILM *)LockMem(hWinParts);
// Standard window is of granular dimensions
if (filling == FULL) {
@@ -2337,11 +3153,16 @@ void ConstructInventory(InventoryType filling) {
}
// Extra width and height
- eH = (InvD[ino].NoofHicons - 1) * (ITEM_WIDTH+1) + SuppH;
- eV = (InvD[ino].NoofVicons - 1) * (ITEM_HEIGHT+1) + SuppV;
+ eH = (InvD[ino].NoofHicons - 1) * (ITEM_WIDTH+I_SEPARATION) + SuppH;
+ eV = (InvD[ino].NoofVicons - 1) * (ITEM_HEIGHT+I_SEPARATION) + SuppV;
// Which window frame corners to use
- if (filling == FULL && ino != INV_CONV) {
+ if (TinselV2 && (ino == INV_CONV)) {
+ TL = IX_TL;
+ TR = IX2_TR4;
+ BL = IX_BL;
+ BR = IX_RBR;
+ } else if ((filling == FULL) && (ino != INV_CONV)) {
TL = IX_TL;
TR = IX_TR;
BL = IX_BL;
@@ -2353,7 +3174,7 @@ void ConstructInventory(InventoryType filling) {
BR = IX_RBR;
}
-// Draw the four corners
+ // Draw the four corners
retObj[n] = AddObject(&pfilm->reels[TL], TL);
MultiSetAniXY(retObj[n], invX, invY);
MultiSetZPosition(retObj[n], zpos);
@@ -2371,95 +3192,108 @@ void ConstructInventory(InventoryType filling) {
MultiSetZPosition(retObj[n], zpos);
n++;
-// Draw extra Top and bottom parts
+ // Draw extra Top and bottom parts
if (InvD[ino].NoofHicons > 1) {
// Top side
retObj[n] = AddObject(&pfilm->reels[hFillers[InvD[ino].NoofHicons-2]], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth, invY);
+ MultiSetAniXY(retObj[n], invX + TLwidth, invY + NM_TBT);
MultiSetZPosition(retObj[n], zpos);
n++;
// Bottom of header box
if (filling == FULL) {
- retObj[n] = AddObject(&pfilm->reels[hFillers[InvD[ino].NoofHicons-2]], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth, invY + M_TBB + 1);
- MultiSetZPosition(retObj[n], zpos);
- n++;
-
- // Extra bits for conversation - hopefully temporary
- if (ino == INV_CONV) {
- retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth - 2, invY + M_TBB + 1);
+ if (TinselV2) {
+ retObj[n] = AddObject(&pfilm->reels[hFillers[InvD[ino].NoofHicons-2]], -1);
+ MultiSetAniXY(retObj[n], invX + TLwidth, invY + NM_TBB);
MultiSetZPosition(retObj[n], zpos);
n++;
-
- retObj[n] = AddObject(&pfilm->reels[IX_H52], -1);
- MultiSetAniXY(retObj[n], invX + eH - 10, invY + M_TBB + 1);
+ } else {
+ retObj[n] = AddObject(&pfilm->reels[hFillers[InvD[ino].NoofHicons-2]], -1);
+ MultiSetAniXY(retObj[n], invX + TLwidth, invY + M_TBB + 1);
MultiSetZPosition(retObj[n], zpos);
n++;
+
+ // Extra bits for conversation - hopefully temporary
+ if (ino == INV_CONV) {
+ retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
+ MultiSetAniXY(retObj[n], invX + TLwidth - 2, invY + M_TBB + 1);
+ MultiSetZPosition(retObj[n], zpos);
+ n++;
+
+ retObj[n] = AddObject(&pfilm->reels[IX_H52], -1);
+ MultiSetAniXY(retObj[n], invX + eH - 10, invY + M_TBB + 1);
+ MultiSetZPosition(retObj[n], zpos);
+ n++;
+ }
}
}
// Bottom side
retObj[n] = AddObject(&pfilm->reels[hFillers[InvD[ino].NoofHicons-2]], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth, invY + TLheight + eV + BLheight - M_TH + 1);
+ MultiSetAniXY(retObj[n], invX + TLwidth, invY + TLheight + eV + BLheight + NM_BSY);
+
MultiSetZPosition(retObj[n], zpos);
n++;
}
if (SuppH) {
- int offx = TLwidth + eH - 26;
+ int offx = TLwidth + eH - (TinselV2 ? ITEM_WIDTH + I_SEPARATION : 26);
if (offx < TLwidth) // Not too far!
offx = TLwidth;
// Top side extra
retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
- MultiSetAniXY(retObj[n], invX + offx, invY);
+ MultiSetAniXY(retObj[n], invX + offx, invY + NM_TBT);
MultiSetZPosition(retObj[n], zpos);
n++;
// Bottom side extra
retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
- MultiSetAniXY(retObj[n], invX + offx, invY + TLheight + eV + BLheight - M_TH + 1);
+ MultiSetAniXY(retObj[n], invX + offx, invY + TLheight + eV + BLheight + NM_BSY);
+
MultiSetZPosition(retObj[n], zpos);
n++;
}
-// Draw extra side parts
+ // Draw extra side parts
if (InvD[ino].NoofVicons > 1) {
// Left side
retObj[n] = AddObject(&pfilm->reels[vFillers[InvD[ino].NoofVicons-2]], -1);
- MultiSetAniXY(retObj[n], invX, invY + TLheight);
+ MultiSetAniXY(retObj[n], invX + NM_LSX, invY + TLheight);
MultiSetZPosition(retObj[n], zpos);
n++;
// Left side of scroll bar
if (filling == FULL && ino != INV_CONV) {
retObj[n] = AddObject(&pfilm->reels[vFillers[InvD[ino].NoofVicons-2]], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth + eH + M_SBL + 1, invY + TLheight);
+ if (TinselV2)
+ MultiSetAniXY(retObj[n], invX + TLwidth + eH + TRwidth + NM_SBL, invY + TLheight);
+ else
+ MultiSetAniXY(retObj[n], invX + TLwidth + eH + M_SBL + 1, invY + TLheight);
MultiSetZPosition(retObj[n], zpos);
n++;
}
// Right side
retObj[n] = AddObject(&pfilm->reels[vFillers[InvD[ino].NoofVicons-2]], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth + eH + TRwidth - M_SW + 1, invY + TLheight);
+ MultiSetAniXY(retObj[n], invX + TLwidth + eH + TRwidth + NM_RSX, invY + TLheight);
MultiSetZPosition(retObj[n], zpos);
n++;
}
if (SuppV) {
- int offy = TLheight + eV - 26;
- if (offy < 5)
- offy = 5;
+ int offy = TLheight + eV - (TinselV2 ? ITEM_HEIGHT + I_SEPARATION : 26);
+ int minAmount = TinselV2 ? 20 : 5;
+ if (offy < minAmount)
+ offy = minAmount;
// Left side extra
retObj[n] = AddObject(&pfilm->reels[IX_V26], -1);
- MultiSetAniXY(retObj[n], invX, invY + offy);
+ MultiSetAniXY(retObj[n], invX + NM_LSX, invY + offy);
MultiSetZPosition(retObj[n], zpos);
n++;
// Right side extra
retObj[n] = AddObject(&pfilm->reels[IX_V26], -1);
- MultiSetAniXY(retObj[n], invX + TLwidth + eH + TRwidth - M_SW + 1, invY + offy);
+ MultiSetAniXY(retObj[n], invX + TLwidth + eH + TRwidth + NM_RSX, invY + offy);
MultiSetZPosition(retObj[n], zpos);
n++;
}
@@ -2467,35 +3301,58 @@ void ConstructInventory(InventoryType filling) {
OBJECT **rect, **title;
// Draw background, slider and icons
+ if (TinselV2 && (filling != EMPTY)) {
+ AddBackground(&retObj[n++], eH, eV);
+ AddTitle(&retObj[n++], eH);
+ }
+
if (filling == FULL) {
- rect = &retObj[n++];
- title = &retObj[n++];
+ if (!TinselV2) {
+ rect = &retObj[n++];
+ title = &retObj[n++];
- AddBackground(rect, title, eH, eV, FROM_HANDLE);
+ AddBackground(rect, title, eH, eV, FROM_HANDLE);
+ }
- if (ino == INV_CONV)
+ if (ino == INV_CONV) {
SlideObject = NULL;
- else if (InvD[ino].NoofItems > InvD[ino].NoofHicons*InvD[ino].NoofVicons) {
- slideYmin = TLheight - 2;
- slideYmax = TLheight + eV + 10;
+
+ if (TinselV2) {
+ // !!!!! MAGIC NUMBER ALERT !!!!!
+ // Make sure it's big enough for the heading
+ if (MultiLeftmost(retObj[n-1]) < InvD[INV_CONV].inventoryX + 10) {
+ InvD[INV_CONV].NoofHicons++;
+ ConstructInventory(FULL);
+ }
+ }
+ } else if (InvD[ino].NoofItems > InvD[ino].NoofHicons*InvD[ino].NoofVicons) {
+ sliderYmin = TLheight - (TinselV2 ? 2 : 1);
+ sliderYmax = TLheight + eV + (TinselV2 ? 12 : 10);
AddSlider(&retObj[n++], pfilm);
}
FillInInventory();
- } else if (filling == CONF) {
- rect = &retObj[n++];
- title = &retObj[n++];
+ }
+ else if (filling == CONF) {
+ if (!TinselV2) {
+ rect = &retObj[n++];
+ title = &retObj[n++];
+
+ AddBackground(rect, title, eH, eV, FROM_STRING);
+ if (cd.bExtraWin)
+ n += AddExtraWindow(invX, invY, &retObj[n]);
+ } else {
+ if (cd.bExtraWin)
+ AddExtraWindow(invX, invY, &retObj[n]);
+ }
- AddBackground(rect, title, eH, eV, FROM_STRING);
- if (cd.bExtraWin)
- n += AddExtraWindow(invX, invY, &retObj[n]);
AddBoxes(true);
}
assert(n < MAX_WCOMP); // added more parts than we can handle!
- // Reposition returns TRUE if needs to move
- if (InvD[ino].moveable && filling == FULL && RePosition()) {
+ // Reposition returns true if needs to move
+ if (InvD[ino].bMoveable && filling == FULL && RePosition()) {
ConstructInventory(FULL);
}
}
@@ -2504,7 +3361,7 @@ void ConstructInventory(InventoryType filling) {
/**
* Call this when drawing a 'FULL', movable inventory. Checks that the
* position of the Translucent object is within limits. If it isn't,
- * adjusts the x/y position of the current inventory and returns TRUE.
+ * adjusts the x/y position of the current inventory and returns true.
*/
bool RePosition(void) {
int p;
@@ -2555,10 +3412,10 @@ void AlterCursor(int num) {
IMAGE *pim;
// Get pointer to image
- pim = GetImageFromFilm(winPartsf, num, &pfreel);
+ pim = GetImageFromFilm(hWinParts, num, &pfreel);
// Poke in the background palette
- pim->hImgPal = TO_LE_32(BackPal());
+ pim->hImgPal = TO_LE_32(BgPal());
SetTempCursor(FROM_LE_32(pfreel->script));
}
@@ -2652,7 +3509,7 @@ void InvCursor(InvCursorFN fn, int CurX, int CurY) {
case I_DOWN:
case I_SLIDE_DOWN:
case I_SLIDE:
- case I_MOVE:
+ case I_HEADER:
case I_BODY:
restoreMain = true;
break;
@@ -2679,40 +3536,101 @@ void InvCursor(InvCursorFN fn, int CurX, int CurY) {
void ConvAction(int index) {
assert(ino == INV_CONV); // not conv. window!
+ PMOVER pMover = TinselV2 ? GetMover(GetLeadId()) : NULL;
switch (index) {
case INV_NOICON:
return;
case INV_CLOSEICON:
- thisConvIcon = -1; // Postamble
+ thisIcon = -1; // Postamble
break;
case INV_OPENICON:
- thisConvIcon = -2; // Preamble
+ // Store the direction the lead character is facing in when the conversation starts
+ if (TinselV2)
+ initialDirection = GetMoverDirection(pMover);
+ thisIcon = -2; // Preamble
break;
default:
- thisConvIcon = InvD[ino].ItemOrder[index];
+ thisIcon = InvD[ino].contents[index];
break;
}
- RunPolyTinselCode(thisConvPoly, CONVERSE, BE_NONE, true);
+ if (!TinselV2)
+ RunPolyTinselCode(thisConvPoly, CONVERSE, PLR_NOEVENT, true);
+ else {
+ // If the lead's direction has changed for any reason (such as having broken the
+ // fourth wall and talked to the screen), reset back to the original direction
+ DIRECTION currDirection = GetMoverDirection(pMover);
+ if (currDirection != initialDirection) {
+ SetMoverDirection(pMover, initialDirection);
+ SetMoverStanding(pMover);
+ }
+
+ if (thisConvPoly != NOPOLY)
+ PolygonEvent(nullContext, thisConvPoly, CONVERSE, 0, false, 0);
+ else
+ ActorEvent(nullContext, thisConvActor, CONVERSE, false, 0);
+ }
+
+}
+
+/**
+ * Called to specify whether conversation window is going to
+ * appear at the top or bottom of the screen.
+ * Also to specify which polygon or actor is opening the conversation.
+ *
+ * Note: ano may (will probably) be set when it's a polygon.
+ */
+void SetConvDetails(CONV_PARAM fn, HPOLYGON hPoly, int ano) {
+ thisConvFn = fn;
+ thisConvPoly = hPoly;
+ thisConvActor = ano;
+
+ bMoveOnUnHide = true;
+
+ // Get the Actor Tag's or Tagged Actor's label for the conversation window title
+ if (hPoly != NOPOLY) {
+ int x, y;
+ GetTagTag(hPoly, &InvD[INV_CONV].hInvTitle, &x, &y);
+ } else {
+ InvD[INV_CONV].hInvTitle = GetActorTagHandle(ano);
+ }
}
+
/*-------------------------------------------------------------------------*/
-void AddIconToPermanentDefaultList(int icon) {
+/**
+ * Add an icon to the permanent conversation list.
+ */
+void PermaConvIcon(int icon, bool bEnd) {
int i;
// See if it's already there
- for (i = 0; i < Num0Order; i++) {
- if (Inv0Order[i] == icon)
+ for (i = 0; i < numPermIcons; i++) {
+ if (permIcons[i] == icon)
break;
}
// Add it if it isn't already there
- if (i == Num0Order) {
- Inv0Order[Num0Order++] = icon;
+ if (i == numPermIcons) {
+ assert(numPermIcons < MAX_PERMICONS);
+
+ if (bEnd || !numEndIcons) {
+ // Add it at the end
+ permIcons[numPermIcons++] = icon;
+ if (bEnd)
+ numEndIcons++;
+ } else {
+ // Insert before end icons
+ memmove(&permIcons[numPermIcons-numEndIcons+1],
+ &permIcons[numPermIcons-numEndIcons],
+ numEndIcons * sizeof(int));
+ permIcons[numPermIcons-numEndIcons] = icon;
+ numPermIcons++;
+ }
}
}
@@ -2729,8 +3647,8 @@ void ConvPoly(HPOLYGON hPoly) {
thisConvPoly = hPoly;
}
-int convIcon(void) {
- return thisConvIcon;
+int GetIcon(void) {
+ return thisIcon;
}
void CloseDownConv(void) {
@@ -2739,32 +3657,151 @@ void CloseDownConv(void) {
}
}
-void convHide(bool hide) {
+void HideConversation(bool bHide) {
int aniX, aniY;
int i;
if (InventoryState == ACTIVE_INV && ino == INV_CONV) {
- if (hide) {
+ if (bHide) {
+ // Move all the window and icons off-screen
for (i = 0; objArray[i] && i < MAX_WCOMP; i++) {
- MultiAdjustXY(objArray[i], 2*SCREEN_WIDTH, 0);
+ MultiAdjustXY(objArray[i], 2 * SCREEN_WIDTH, 0);
}
for (i = 0; iconArray[i] && i < MAX_ICONS; i++) {
- MultiAdjustXY(iconArray[i], 2*SCREEN_WIDTH, 0);
+ MultiAdjustXY(iconArray[i], 2 * SCREEN_WIDTH, 0);
}
+
+ // Window is hidden
InventoryHidden = true;
+ // Remove any labels
InvLabels(false, 0, 0);
} else {
+ // Window is not hidden
InventoryHidden = false;
- for (i = 0; objArray[i] && i < MAX_WCOMP; i++) {
- MultiAdjustXY(objArray[i], -2*SCREEN_WIDTH, 0);
+ if (TinselV2 && ItemsChanged)
+ // Just rebuild the whole thing
+ ConstructInventory(FULL);
+ else {
+ // Move it all back on-screen
+ for (i = 0; objArray[i] && i < MAX_WCOMP; i++) {
+ MultiAdjustXY(objArray[i], -2 * SCREEN_WIDTH, 0);
+ }
+
+ // Don't flash if items changed. If they have, will be redrawn anyway.
+ if (TinselV2 || !ItemsChanged) {
+ for (i = 0; iconArray[i] && i < MAX_ICONS; i++) {
+ MultiAdjustXY(iconArray[i], -2*SCREEN_WIDTH, 0);
+ }
+ }
}
- // Don't flash if items changed. If they have, will be redrawn anyway.
- if (!ItemsChanged) {
+ if (TinselV2 && bMoveOnUnHide) {
+ /*
+ * First time, position it appropriately
+ */
+ int left, centre;
+ int x, y, deltay;
+
+ // Only do it once per conversation
+ bMoveOnUnHide = false;
+
+ // Current centre of the window
+ left = MultiLeftmost(RectObject);
+ centre = (MultiRightmost(RectObject) + left) / 2;
+
+ // Get the x-offset for the conversation window
+ if (thisConvActor) {
+ int Loffset, Toffset;
+
+ GetActorMidTop(thisConvActor, &x, &y);
+ PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+ x -= Loffset;
+ y -= Toffset;
+ } else {
+ x = SCREEN_WIDTH / 2;
+ y = SCREEN_BOX_HEIGHT2 / 2;
+ }
+
+ // Save old y-position
+ deltay = InvD[INV_CONV].inventoryY;
+
+ switch (thisConvFn) {
+ case CONV_TOP:
+ InvD[INV_CONV].inventoryY = SysVar(SV_CONV_TOPY);
+ break;
+
+ case CONV_BOTTOM:
+ InvD[INV_CONV].inventoryY = SysVar(SV_CONV_BOTY);
+ break;
+
+ case CONV_DEF:
+ InvD[INV_CONV].inventoryY = y - SysVar(SV_CONV_ABOVE_Y);
+ break;
+
+ default:
+ break;
+ }
+
+ // Calculate y change
+ deltay = InvD[INV_CONV].inventoryY - deltay;
+
+ // Move it all
+ for (i = 0; objArray[i] && i < MAX_WCOMP; i++) {
+ MultiMoveRelXY(objArray[i], x - centre, deltay);
+ }
for (i = 0; iconArray[i] && i < MAX_ICONS; i++) {
- MultiAdjustXY(iconArray[i], -2*SCREEN_WIDTH, 0);
+ MultiMoveRelXY(iconArray[i], x - centre, deltay);
+ }
+ InvD[INV_CONV].inventoryX += x - centre;
+
+ /*
+ * Now positioned as worked out
+ * - but it must be in a sensible place
+ */
+ if (MultiLeftmost(RectObject) < SysVar(SV_CONV_MINX))
+ x = SysVar(SV_CONV_MINX) - MultiLeftmost(RectObject);
+ else if (MultiRightmost(RectObject) > SCREEN_WIDTH - SysVar(SV_CONV_MINX))
+ x = SCREEN_WIDTH - SysVar(SV_CONV_MINX) - MultiRightmost(RectObject);
+ else
+ x = 0;
+
+ if (thisConvFn == CONV_DEF && MultiHighest(RectObject) < SysVar(SV_CONV_MINY)
+ && thisConvActor) {
+ int Loffset, Toffset;
+
+ PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+ y = GetActorBottom(thisConvActor) - MultiHighest(RectObject) +
+ SysVar(SV_CONV_BELOW_Y);
+ y -= Toffset;
+ }
+ else
+ y = 0;
+
+ if (x || y) {
+ for (i = 0; objArray[i] && i < MAX_WCOMP; i++) {
+ MultiMoveRelXY(objArray[i], x, y);
+ }
+ for (i = 0; iconArray[i] && i < MAX_ICONS; i++) {
+ MultiMoveRelXY(iconArray[i], x, y);
+ }
+ InvD[INV_CONV].inventoryX += x;
+ InvD[INV_CONV].inventoryY += y;
+ }
+
+ /*
+ * Oh shit! We might have gone off the bottom
+ */
+ if (MultiLowest(RectObject) > SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) {
+ y = (SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) - MultiLowest(RectObject);
+ for (i = 0; objArray[i] && i < MAX_WCOMP; i++) {
+ MultiMoveRelXY(objArray[i], 0, y);
+ }
+ for (i = 0; iconArray[i] && i < MAX_ICONS; i++) {
+ MultiMoveRelXY(iconArray[i], 0, y);
+ }
+ InvD[INV_CONV].inventoryY += y;
}
}
@@ -2774,7 +3811,7 @@ void convHide(bool hide) {
}
}
-bool convHid(void) {
+bool ConvIsHidden(void) {
return InventoryHidden;
}
@@ -2787,19 +3824,29 @@ bool convHid(void) {
* Start up an inventory window.
*/
void PopUpInventory(int invno) {
- assert((invno == INV_1 || invno == INV_2 || invno == INV_CONV || invno == INV_CONF)); // Trying to open illegal inventory
+ assert(invno == INV_1 || invno == INV_2 || invno == INV_CONV
+ || invno == INV_CONF || invno == INV_MENU); // Trying to open illegal inventory
if (InventoryState == IDLE_INV) {
- bOpenConf = false; // Better safe than sorry...
+ bReOpenMenu = false; // Better safe than sorry...
DisableTags(); // Tags disabled during inventory
+ if (TinselV2)
+ DisablePointing(nullContext); // Pointing disabled during inventory
if (invno == INV_CONV) { // Conversation window?
+ if (TinselV2)
+ // Quiet please..
+ _vm->_pcmMusic->dim(false);
+
// Start conversation with permanent contents
- memset(InvD[INV_CONV].ItemOrder, 0, MAX_ININV*sizeof(int));
- memcpy(InvD[INV_CONV].ItemOrder, Inv0Order, Num0Order*sizeof(int));
- InvD[INV_CONV].NoofItems = Num0Order;
- thisConvIcon = 0;
+ memset(InvD[INV_CONV].contents, 0, MAX_ININV*sizeof(int));
+ memcpy(InvD[INV_CONV].contents, permIcons, numPermIcons*sizeof(int));
+ InvD[INV_CONV].NoofItems = numPermIcons;
+ if (TinselV2)
+ InvD[INV_CONV].NoofHicons = numPermIcons;
+ else
+ thisIcon = 0;
} else if (invno == INV_CONF) { // Configuration window?
cd.selBox = NOBOX;
cd.pointBox = NOBOX;
@@ -2820,74 +3867,103 @@ void PopUpInventory(int invno) {
}
}
-void SetConfGlobals(CONFINIT *ci) {
+void SetMenuGlobals(CONFINIT *ci) {
InvD[INV_CONF].MinHicons = InvD[INV_CONF].MaxHicons = InvD[INV_CONF].NoofHicons = ci->h;
InvD[INV_CONF].MaxVicons = InvD[INV_CONF].MinVicons = InvD[INV_CONF].NoofVicons = ci->v;
InvD[INV_CONF].inventoryX = ci->x;
InvD[INV_CONF].inventoryY = ci->y;
cd.bExtraWin = ci->bExtraWin;
- cd.Box = ci->Box;
+ cd.box = ci->Box;
cd.NumBoxes = ci->NumBoxes;
cd.ixHeading = ci->ixHeading;
+
+ if (TinselV2) {
+ if ((ci->ixHeading != NO_HEADING) && SysString(ci->ixHeading))
+ InvD[INV_MENU].hInvTitle = SysString(ci->ixHeading);
+ else
+ InvD[INV_MENU].hInvTitle = NO_HEADING;
+ }
}
/**
* PopupConf
*/
-void PopUpConf(CONFTYPE type) {
+void OpenMenu(CONFTYPE menuType) {
int curX, curY;
if (InventoryState != IDLE_INV)
return;
InvD[INV_CONF].resizable = false;
- InvD[INV_CONF].moveable = false;
+ InvD[INV_CONF].bMoveable = false;
- switch (type) {
- case SAVE:
- case LOAD:
- if (type == SAVE) {
+ switch (menuType) {
+ case MAIN_MENU:
+ SetMenuGlobals(&ciOption);
+ break;
+
+ case SAVE_MENU:
+ if (!TinselV2)
SetCursorScreenXY(262, 91);
- SetConfGlobals(&ciSave);
- cd.editableRgroup = true;
- } else {
- SetConfGlobals(&ciLoad);
- cd.editableRgroup = false;
- }
- firstFile(0);
+ SetMenuGlobals(&ciSave);
+ cd.editableRgroup = true;
+ FirstFile(0);
break;
- case QUIT:
-#ifdef JAPAN
- SetCursorScreenXY(180, 106);
-#else
- SetCursorScreenXY(180, 90);
-#endif
- SetConfGlobals(&ciQuit);
+ case LOAD_MENU:
+ SetMenuGlobals(&ciLoad);
+ cd.editableRgroup = false;
+ FirstFile(0);
break;
- case RESTART:
-#ifdef JAPAN
- SetCursorScreenXY(180, 106);
-#else
- SetCursorScreenXY(180, 90);
-#endif
- SetConfGlobals(&ciRestart);
+ case RESTART_MENU:
+ if (TinselV2)
+ SetCursorScreenXY(360, 153);
+ else if (_vm->getLanguage() == Common::JA_JPN)
+ SetCursorScreenXY(180, 106);
+ else
+ SetCursorScreenXY(180, 90);
+
+ SetMenuGlobals(&ciRestart);
+ break;
+
+ case SOUND_MENU:
+ if (TinselV2)
+ displayedLanguage = TextLanguage();
+ SetMenuGlobals(&ciSound);
break;
- case OPTION:
- SetConfGlobals(&ciOption);
+ case CONTROLS_MENU:
+ SetMenuGlobals(&ciControl);
break;
- case CONTROLS:
- SetConfGlobals(&ciControl);
+ case QUIT_MENU:
+ if (TinselV2)
+ SetCursorScreenXY(360, 153);
+ else if (_vm->getLanguage() == Common::JA_JPN)
+ SetCursorScreenXY(180, 106);
+ else
+ SetCursorScreenXY(180, 90);
+
+ SetMenuGlobals(&ciQuit);
break;
- case SOUND:
- SetConfGlobals(&ciSound);
+ case HOPPER_MENU1:
+ PrimeSceneHopper();
+ SetMenuGlobals(&ciHopper1);
+ cd.editableRgroup = false;
+ RememberChosenScene();
+ FirstScene(0);
break;
- case SUBT:
+ case HOPPER_MENU2:
+ SetMenuGlobals(&ciHopper2);
+ cd.editableRgroup = false;
+ SetChosenScene();
+ FirstEntry(0);
+ break;
+
+ case SUBTITLES_MENU:
if (_vm->getFeatures() & GF_USE_3FLAGS) {
ciSubtitles.v = 6;
ciSubtitles.Box = subtitlesBox3Flags;
@@ -2906,11 +3982,11 @@ void PopUpConf(CONFTYPE type) {
ciSubtitles.NumBoxes = ARRAYSIZE(subtitlesBox);
}
- SetConfGlobals(&ciSubtitles);
+ SetMenuGlobals(&ciSubtitles);
break;
- case TOPWIN:
- SetConfGlobals(&ciTopWin);
+ case TOP_WINDOW:
+ SetMenuGlobals(&ciTopWin);
ino = INV_CONF;
ConstructInventory(CONF); // Draw it up
InventoryState = BOGUS_INV;
@@ -2925,9 +4001,11 @@ void PopUpConf(CONFTYPE type) {
PopUpInventory(INV_CONF);
- if (type == SAVE || type == LOAD)
+ // Make initial box selections if appropriate
+ if (menuType == SAVE_MENU || menuType == LOAD_MENU
+ || menuType == HOPPER_MENU1 || menuType == HOPPER_MENU2)
Select(0, false);
- else if (type == SUBT) {
+ else if (menuType == SUBTITLES_MENU) {
if (_vm->getFeatures() & GF_USE_3FLAGS) {
// VERY quick dirty bodges
if (g_language == TXT_FRENCH)
@@ -2950,6 +4028,7 @@ void PopUpConf(CONFTYPE type) {
/**
* Close down an inventory window.
*/
+
void KillInventory(void) {
if (objArray[0] != NULL) {
DumpObjArray();
@@ -2959,6 +4038,8 @@ void KillInventory(void) {
if (InventoryState == ACTIVE_INV) {
EnableTags();
+ if (TinselV2)
+ EnablePointing();
InvD[ino].bMax = InventoryMaximised;
@@ -2968,14 +4049,21 @@ void KillInventory(void) {
InventoryState = IDLE_INV;
- if (bOpenConf) {
- bOpenConf = false;
- PopUpConf(OPTION);
-
+ if (bReOpenMenu) {
+ bReOpenMenu = false;
+ OpenMenu(MAIN_MENU);
+
// Write config changes
WriteConfig();
+
} else if (ino == INV_CONF)
- InventoryIconCursor();
+ InventoryIconCursor(false);
+
+ if (TinselV2)
+ // Pump up the volume
+ if (ino == INV_CONV)
+ _vm->_pcmMusic->unDim(false);
+
}
void CloseInventory(void) {
@@ -3012,6 +4100,9 @@ void InventoryProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CODE(_ctx);
+ if (NumberOfLanguages() <= 1)
+ bNoLanguage = true;
+
while (1) {
CORO_SLEEP(1); // allow scheduling
@@ -3037,17 +4128,17 @@ void InventoryProcess(CORO_PARAM, const void *) {
int sval, index, *pival;
index = cd.selBox & ~IS_MASK;
- pival = cd.Box[index].ival;
+ pival = cd.box[index].ival;
sval = *pival;
if (cd.selBox & IS_LEFT) {
- *pival -= cd.Box[index].h;
+ *pival -= cd.box[index].h;
if (*pival < 0)
*pival = 0;
} else if (cd.selBox & IS_RIGHT) {
- *pival += cd.Box[index].h;
- if (*pival > cd.Box[index].w)
- *pival = cd.Box[index].w;
+ *pival += cd.box[index].h;
+ if (*pival > cd.box[index].w)
+ *pival = cd.box[index].w;
}
if (sval != *pival) {
@@ -3075,39 +4166,44 @@ void InventoryProcess(CORO_PARAM, const void *) {
break;
case CLOSEWIN:
KillInventory();
+ if ((cd.box == hopperBox1) || (cd.box == hopperBox2))
+ FreeSceneHopper();
break;
case OPENLOAD:
KillInventory();
- PopUpConf(LOAD);
+ OpenMenu(LOAD_MENU);
break;
case OPENSAVE:
KillInventory();
- PopUpConf(SAVE);
+ OpenMenu(SAVE_MENU);
break;
case OPENREST:
KillInventory();
- PopUpConf(RESTART);
+ OpenMenu(RESTART_MENU);
break;
case OPENSOUND:
KillInventory();
- PopUpConf(SOUND);
+ OpenMenu(SOUND_MENU);
break;
case OPENCONT:
KillInventory();
- PopUpConf(CONTROLS);
+ OpenMenu(CONTROLS_MENU);
break;
+ #ifndef JAPAN
case OPENSUBT:
KillInventory();
- PopUpConf(SUBT);
+ OpenMenu(SUBTITLES_MENU);
break;
+ #endif
case OPENQUIT:
KillInventory();
- PopUpConf(QUIT);
+ OpenMenu(QUIT_MENU);
break;
case INITGAME:
KillInventory();
bRestart = true;
break;
+ #if defined(USE_3FLAGS) || defined(USE_4FLAGS) || defined(USE_5FLAGS)
case CLANG:
if (!LanguageChange())
KillInventory();
@@ -3115,6 +4211,16 @@ void InventoryProcess(CORO_PARAM, const void *) {
case RLANG:
KillInventory();
break;
+ #endif
+ case HOPPER2:
+ KillInventory();
+ OpenMenu(HOPPER_MENU2);
+ break;
+ case BF_CHANGESCENE:
+ KillInventory();
+ HopAction();
+ FreeSceneHopper();
+ break;
default:
break;
}
@@ -3166,26 +4272,26 @@ void SlideSlider(int y, SSFN fn) {
switch (fn) {
case S_START: // Start of a drag on the slider
- newY = slideY;
- lasti = NearestSlideY(slideY);
+ newY = sliderYpos;
+ lasti = NearestSlideY(sliderYpos);
break;
case S_SLIDE: // Y-movement during drag
newY = newY + y; // New y-position
- if (newY < slideYmin)
- gotoY = slideYmin; // Above top limit
- else if (newY > slideYmax)
- gotoY = slideYmax; // Below bottom limit
+ if (newY < sliderYmin)
+ gotoY = sliderYmin; // Above top limit
+ else if (newY > sliderYmax)
+ gotoY = sliderYmax; // Below bottom limit
else
gotoY = newY; // Hunky-Dory
// Move slider to new position
- MultiMoveRelXY(SlideObject, 0, gotoY - slideY);
- slideY = gotoY;
+ MultiMoveRelXY(SlideObject, 0, gotoY - sliderYpos);
+ sliderYpos = gotoY;
// Re-draw icons if necessary
- ati = NearestSlideY(slideY);
+ ati = NearestSlideY(sliderYpos);
if (ati != lasti) {
InvD[ino].FirstDisp = slideStuff[ati].n;
assert(InvD[ino].FirstDisp >= 0); // negative first displayed
@@ -3196,7 +4302,7 @@ void SlideSlider(int y, SSFN fn) {
case S_END: // End of a drag on the slider
// Draw icons from new start icon
- ati = NearestSlideY(slideY);
+ ati = NearestSlideY(sliderYpos);
InvD[ino].FirstDisp = slideStuff[ati].n;
ItemsChanged = true;
break;
@@ -3210,7 +4316,6 @@ void SlideSlider(int y, SSFN fn) {
* Gets called at the start and end of a drag on the slider, and upon
* y-movement during such a drag.
*/
-
void SlideCSlider(int y, SSFN fn) {
static int newY = 0; // FIXME: local static var
int gotoY;
@@ -3222,31 +4327,46 @@ void SlideCSlider(int y, SSFN fn) {
switch (fn) {
case S_START: // Start of a drag on the slider
- newY = slideY;
+ newY = sliderYpos;
break;
case S_SLIDE: // Y-movement during drag
newY = newY + y; // New y-position
- if (newY < slideYmin)
- gotoY = slideYmin; // Above top limit
- else if (newY > slideYmax)
- gotoY = slideYmax; // Below bottom limit
+ if (newY < sliderYmin)
+ gotoY = sliderYmin; // Above top limit
+ else if (newY > sliderYmax)
+ gotoY = sliderYmax; // Below bottom limit
else
gotoY = newY; // Hunky-Dory
- slideY = gotoY;
-
- fc = cd.fileBase;
- firstFile((slideY-slideYmin)*(MAX_SFILES-NUM_SL_RGROUP)/(slideYmax-slideYmin));
- if (fc != cd.fileBase) {
+ // Move slider to new position
+ if (TinselV2)
+ MultiMoveRelXY(SlideObject, 0, gotoY - sliderYpos);
+ sliderYpos = gotoY;
+
+ fc = cd.extraBase;
+
+ if ((cd.box == saveBox || cd.box == loadBox))
+ FirstFile((sliderYpos - sliderYmin) * (MAX_SAVED_FILES - NUM_RGROUP_BOXES) /
+ (sliderYmax - sliderYmin));
+ else if (cd.box == hopperBox1)
+ FirstScene((sliderYpos - sliderYmin) * (numScenes - NUM_RGROUP_BOXES) / sliderRange);
+ else if (cd.box == hopperBox2)
+ FirstEntry((sliderYpos - sliderYmin) * (numEntries - NUM_RGROUP_BOXES) / sliderRange);
+
+ // If extraBase has changed...
+ if (fc != cd.extraBase) {
AddBoxes(false);
- fc -= cd.fileBase;
+ fc -= cd.extraBase;
cd.selBox += fc;
+
+ // Ensure within legal limits
if (cd.selBox < 0)
cd.selBox = 0;
- else if (cd.selBox >= NUM_SL_RGROUP)
- cd.selBox = NUM_SL_RGROUP-1;
+ else if (cd.selBox >= NUM_RGROUP_BOXES)
+ cd.selBox = NUM_RGROUP_BOXES-1;
+
Select(cd.selBox, true);
}
break;
@@ -3263,7 +4383,6 @@ void SlideCSlider(int y, SSFN fn) {
* Gets called at the start and end of a drag on a mixing desk slider,
* and upon x-movement during such a drag.
*/
-
static void SlideMSlider(int x, SSFN fn) {
static int newX = 0; // FIXME: local static var
int gotoX;
@@ -3306,15 +4425,15 @@ static void SlideMSlider(int x, SSFN fn) {
sX = gotoX;
if (lX != sX) {
- *cd.Box[index].ival = (sX - mdSlides[i].min)*cd.Box[index].w/SLIDE_RANGE;
- if (cd.Box[index].boxFunc == MIDIVOL)
- SetMidiVolume(*cd.Box[index].ival);
+ *cd.box[index].ival = (sX - mdSlides[i].min)*cd.box[index].w/SLIDE_RANGE;
+ if (cd.box[index].boxFunc == MUSICVOL)
+ SetMidiVolume(*cd.box[index].ival);
#ifdef MAC_OPTIONS
- if (cd.Box[index].boxFunc == MASTERVOL)
- SetSystemVolume(*cd.Box[index].ival);
+ if (cd.box[index].boxFunc == MASTERVOL)
+ SetSystemVolume(*cd.box[index].ival);
- if (cd.Box[index].boxFunc == SAMPVOL)
- SetSampleVolume(*cd.Box[index].ival);
+ if (cd.box[index].boxFunc == SAMPVOL)
+ SetSampleVolume(*cd.box[index].ival);
#endif
lX = sX;
}
@@ -3322,23 +4441,23 @@ static void SlideMSlider(int x, SSFN fn) {
case S_TIMEUP:
case S_TIMEDN:
- gotoX = SLIDE_RANGE*(*cd.Box[index].ival)/cd.Box[index].w;
+ gotoX = SLIDE_RANGE*(*cd.box[index].ival)/cd.box[index].w;
MultiSetAniX(mdSlides[i].obj, mdSlides[i].min+gotoX);
- if (cd.Box[index].boxFunc == MIDIVOL)
- SetMidiVolume(*cd.Box[index].ival);
+ if (cd.box[index].boxFunc == MUSICVOL)
+ SetMidiVolume(*cd.box[index].ival);
#ifdef MAC_OPTIONS
- if (cd.Box[index].boxFunc == MASTERVOL)
- SetSystemVolume(*cd.Box[index].ival);
+ if (cd.box[index].boxFunc == MASTERVOL)
+ SetSystemVolume(*cd.box[index].ival);
- if (cd.Box[index].boxFunc == SAMPVOL)
- SetSampleVolume(*cd.Box[index].ival);
+ if (cd.box[index].boxFunc == SAMPVOL)
+ SetSampleVolume(*cd.box[index].ival);
#endif
break;
case S_END: // End of a drag on the slider
AddBoxes(false); // Might change position slightly
- if (ino == INV_CONF && cd.Box == subtitlesBox)
+ if (ino == INV_CONF && cd.box == subtitlesBox)
Select(g_language, false);
break;
}
@@ -3347,7 +4466,6 @@ static void SlideMSlider(int x, SSFN fn) {
/**
* Called from ChangeingSize() during re-sizing.
*/
-
void GettingTaller(void) {
if (SuppV) {
Ychange += SuppV;
@@ -3372,7 +4490,6 @@ void GettingTaller(void) {
/**
* Called from ChangeingSize() during re-sizing.
*/
-
void GettingShorter(void) {
int StartNvi = InvD[ino].NoofVicons;
int StartUv = SuppV;
@@ -3398,7 +4515,6 @@ void GettingShorter(void) {
/**
* Called from ChangeingSize() during re-sizing.
*/
-
void GettingWider(void) {
int StartNhi = InvD[ino].NoofHicons;
int StartUh = SuppH;
@@ -3422,7 +4538,6 @@ void GettingWider(void) {
/**
* Called from ChangeingSize() during re-sizing.
*/
-
void GettingNarrower(void) {
int StartNhi = InvD[ino].NoofHicons;
int StartUh = SuppH;
@@ -3449,7 +4564,6 @@ void GettingNarrower(void) {
/**
* Called from Xmovement()/Ymovement() during re-sizing.
*/
-
void ChangeingSize(void) {
/* Make it taller or shorter if necessary. */
if (Ychange > 0)
@@ -3469,7 +4583,6 @@ void ChangeingSize(void) {
/**
* Called from cursor module when cursor moves while inventory is up.
*/
-
void Xmovement(int x) {
int aniX, aniY;
int i;
@@ -3518,7 +4631,6 @@ void Xmovement(int x) {
/**
* Called from cursor module when cursor moves while inventory is up.
*/
-
void Ymovement(int y) {
int aniX, aniY;
int i;
@@ -3571,19 +4683,18 @@ void Ymovement(int y) {
/**
* Called when a drag is commencing.
*/
-
void InvDragStart(void) {
int curX, curY; // cursor's animation position
GetCursorXY(&curX, &curY, false);
-/*
-* Do something different for Save/Restore screens
-*/
+ /*
+ * Do something different for Save/Restore screens
+ */
if (ino == INV_CONF) {
int whichbox;
- whichbox = WhichInvBox(curX, curY, true);
+ whichbox = WhichMenuBox(curX, curY, true);
if (whichbox == IB_SLIDE) {
InvDragging = ID_CSLIDE;
@@ -3596,12 +4707,12 @@ void InvDragStart(void) {
return;
}
-/*
-* Normal operation
-*/
+ /*
+ * Normal operation
+ */
switch (InvArea(curX, curY)) {
- case I_MOVE:
- if (InvD[ino].moveable) {
+ case I_HEADER:
+ if (InvD[ino].bMoveable) {
InvDragging = ID_MOVE;
}
break;
@@ -3688,7 +4799,6 @@ void InvDragStart(void) {
/**
* Called when a drag is over.
*/
-
void InvDragEnd(void) {
int curX, curY; // cursor's animation position
@@ -3716,7 +4826,9 @@ void InvDragEnd(void) {
InvD[ino].otherY = InvD[ino].inventoryY;
}
}
+
InvDragging = ID_NONE;
+ ProcessedProvisional();
}
// Cursor could well now be inappropriate
@@ -3725,29 +4837,76 @@ void InvDragEnd(void) {
Xchange = Ychange = 0; // Probably no need, but does no harm!
}
+static void MenuPageDown(void) {
+ if (cd.box == loadBox || cd.box == saveBox) {
+ if (cd.extraBase < MAX_SAVED_FILES-NUM_RGROUP_BOXES) {
+ FirstFile(cd.extraBase+(NUM_RGROUP_BOXES - 1));
+ AddBoxes(true);
+ cd.selBox = NUM_RGROUP_BOXES - 1;
+ Select(cd.selBox, true);
+ }
+ } else if (cd.box == hopperBox1) {
+ if (cd.extraBase < numScenes - NUM_RGROUP_BOXES)
+ {
+ FirstScene(cd.extraBase + (NUM_RGROUP_BOXES - 1));
+ AddBoxes(true);
+ if (cd.selBox)
+ cd.selBox = NUM_RGROUP_BOXES - 1;
+ Select(cd.selBox, true);
+ }
+ } else if (cd.box == hopperBox2) {
+ if (cd.extraBase < numEntries - NUM_RGROUP_BOXES)
+ {
+ FirstEntry(cd.extraBase+(NUM_RGROUP_BOXES - 1));
+ AddBoxes(true);
+ if (cd.selBox)
+ cd.selBox = NUM_RGROUP_BOXES - 1;
+ Select(cd.selBox, true);
+ }
+ }
+}
+
+static void MenuPageUp(void) {
+ if (cd.extraBase > 0) {
+ if (cd.box == loadBox || cd.box == saveBox)
+ FirstFile(cd.extraBase-(NUM_RGROUP_BOXES - 1));
+ else if (cd.box == hopperBox1)
+ FirstScene(cd.extraBase-(NUM_RGROUP_BOXES - 1));
+ else if (cd.box == hopperBox2)
+ FirstEntry(cd.extraBase-(NUM_RGROUP_BOXES - 1));
+ else
+ return;
+
+ AddBoxes(true);
+ cd.selBox = 0;
+ Select(cd.selBox, true);
+ }
+}
/**************************************************************************/
/************** Incoming events - further processing **********************/
/**************************************************************************/
/**
- * ConfAction
+ * MenuAction
*/
-void ConfAction(int i, bool dbl) {
+void MenuAction(int i, bool dbl) {
if (i >= 0) {
- switch (cd.Box[i].boxType) {
+ switch (cd.box[i].boxType) {
case FLIP:
if (dbl) {
- *(cd.Box[i].ival) ^= 1; // XOR with true
+ *(cd.box[i].ival) ^= 1; // XOR with true
AddBoxes(false);
}
break;
case TOGGLE:
+ case TOGGLE1:
+ case TOGGLE2:
if (!g_buttonEffect.bButAnim) {
g_buttonEffect.bButAnim = true;
- g_buttonEffect.box = &cd.Box[i];
+ g_buttonEffect.box = &cd.box[i];
g_buttonEffect.press = false;
}
break;
@@ -3755,7 +4914,7 @@ void ConfAction(int i, bool dbl) {
case RGROUP:
if (dbl) {
// Already highlighted
- switch (cd.Box[i].boxFunc) {
+ switch (cd.box[i].boxFunc) {
case SAVEGAME:
KillInventory();
InvSaveGame();
@@ -3764,6 +4923,15 @@ void ConfAction(int i, bool dbl) {
KillInventory();
InvLoadGame();
break;
+ case HOPPER2:
+ KillInventory();
+ OpenMenu(HOPPER_MENU2);
+ break;
+ case BF_CHANGESCENE:
+ KillInventory();
+ HopAction();
+ FreeSceneHopper();
+ break;
default:
break;
}
@@ -3772,8 +4940,8 @@ void ConfAction(int i, bool dbl) {
}
break;
+#if defined(USE_3FLAGS) || defined(USE_4FLAGS) || defined(USE_5FLAGS)
case FRGROUP:
- assert((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS));
if (dbl) {
Select(i, false);
LanguageChange();
@@ -3781,6 +4949,7 @@ void ConfAction(int i, bool dbl) {
Select(i, false);
}
break;
+#endif
case AAGBUT:
case ARSGBUT:
@@ -3791,7 +4960,7 @@ void ConfAction(int i, bool dbl) {
break;
g_buttonEffect.bButAnim = true;
- g_buttonEffect.box = &cd.Box[i];
+ g_buttonEffect.box = &cd.box[i];
g_buttonEffect.press = true;
break;
default:
@@ -3807,38 +4976,54 @@ static void ConfActionSpecial(int i) {
case IB_NONE:
break;
case IB_UP: // Scroll up
- if (cd.fileBase > 0) {
- firstFile(cd.fileBase-1);
+ if (cd.extraBase > 0) {
+ if ((cd.box == loadBox) || (cd.box == saveBox))
+ FirstFile(cd.extraBase - 1);
+ else if (cd.box == hopperBox1)
+ FirstScene(cd.extraBase - 1);
+ else if (cd.box == hopperBox2)
+ FirstEntry(cd.extraBase - 1);
+
AddBoxes(true);
- if (cd.selBox < NUM_SL_RGROUP-1)
+ if (cd.selBox < NUM_RGROUP_BOXES - 1)
cd.selBox += 1;
Select(cd.selBox, true);
}
break;
case IB_DOWN: // Scroll down
- if (cd.fileBase < MAX_SFILES-NUM_SL_RGROUP) {
- firstFile(cd.fileBase+1);
- AddBoxes(true);
- if (cd.selBox)
- cd.selBox -= 1;
- Select(cd.selBox, true);
+ if ((cd.box == loadBox) || (cd.box == saveBox)) {
+ if (cd.extraBase < MAX_SAVED_FILES - NUM_RGROUP_BOXES) {
+ FirstFile(cd.extraBase + 1);
+ AddBoxes(true);
+ if (cd.selBox)
+ cd.selBox -= 1;
+ Select(cd.selBox, true);
+ }
+ } else if (cd.box == hopperBox1) {
+ if (cd.extraBase < numScenes - NUM_RGROUP_BOXES) {
+ FirstScene(cd.extraBase + 1);
+ AddBoxes(true);
+ if (cd.selBox)
+ cd.selBox -= 1;
+ Select(cd.selBox, true);
+ }
+ } else if (cd.box == hopperBox2) {
+ if (cd.extraBase < numEntries - NUM_RGROUP_BOXES) {
+ FirstEntry(cd.extraBase + 1);
+ AddBoxes(true);
+ if (cd.selBox)
+ cd.selBox -= 1;
+ Select(cd.selBox, true);
+ }
}
break;
+
case IB_SLIDE_UP:
- if (cd.fileBase > 0) {
- firstFile(cd.fileBase-(NUM_SL_RGROUP-1));
- AddBoxes(true);
- cd.selBox = 0;
- Select(cd.selBox, true);
- }
+ MenuPageUp();
break;
- case IB_SLIDE_DOWN: // Scroll down
- if (cd.fileBase < MAX_SFILES-NUM_SL_RGROUP) {
- firstFile(cd.fileBase+(NUM_SL_RGROUP-1));
- AddBoxes(true);
- cd.selBox = NUM_SL_RGROUP-1;
- Select(cd.selBox, true);
- }
+
+ case IB_SLIDE_DOWN:
+ MenuPageDown();
break;
}
}
@@ -3851,7 +5036,7 @@ void InvPutDown(int index) {
// Find where the held item is positioned in this inventory (if it is)
for (hiIndex = 0; hiIndex < InvD[ino].NoofItems; hiIndex++)
- if (InvD[ino].ItemOrder[hiIndex] == HeldItem)
+ if (InvD[ino].contents[hiIndex] == HeldItem)
break;
// If drop position would leave a gap, move it up
@@ -3877,13 +5062,13 @@ void InvPutDown(int index) {
// Position it in the inventory
if (index < hiIndex) {
- memmove(&InvD[ino].ItemOrder[index + 1], &InvD[ino].ItemOrder[index], (hiIndex-index)*sizeof(int));
- InvD[ino].ItemOrder[index] = HeldItem;
+ memmove(&InvD[ino].contents[index + 1], &InvD[ino].contents[index], (hiIndex-index)*sizeof(int));
+ InvD[ino].contents[index] = HeldItem;
} else if (index > hiIndex) {
- memmove(&InvD[ino].ItemOrder[hiIndex], &InvD[ino].ItemOrder[hiIndex+1], (index-hiIndex)*sizeof(int));
- InvD[ino].ItemOrder[index] = HeldItem;
+ memmove(&InvD[ino].contents[hiIndex], &InvD[ino].contents[hiIndex+1], (index-hiIndex)*sizeof(int));
+ InvD[ino].contents[index] = HeldItem;
} else {
- InvD[ino].ItemOrder[index] = HeldItem;
+ InvD[ino].contents[index] = HeldItem;
}
HeldItem = INV_NOICON;
@@ -3906,7 +5091,7 @@ void InvPdProcess(CORO_PARAM, const void *param) {
FreeToken(TOKEN_LEFT_BUT);
// get the stuff copied to process when it was created
- int *pindex = (int *)param;
+ const int *pindex = (const int *)param;
InvPutDown(*pindex);
@@ -3916,39 +5101,51 @@ void InvPdProcess(CORO_PARAM, const void *param) {
void InvPickup(int index) {
INV_OBJECT *invObj;
- if (index != INV_NOICON) {
- if (HeldItem == INV_NOICON && InvD[ino].ItemOrder[index] && InvD[ino].ItemOrder[index] != HeldItem) {
- // Pick-up
- invObj = findInvObject(InvD[ino].ItemOrder[index]);
- if (invObj->hScript)
- RunInvTinselCode(invObj, WALKTO, INV_PICKUP, index);
- } else if (HeldItem != INV_NOICON) { // Put icon down
- // Put-down
- invObj = findInvObject(HeldItem);
-
- if (invObj->attribute & IO_DROPCODE && invObj->hScript)
- RunInvTinselCode(invObj, PUTDOWN, INV_PICKUP, index);
-
- else if (!(invObj->attribute & IO_ONLYINV1 && ino !=INV_1)
- && !(invObj->attribute & IO_ONLYINV2 && ino !=INV_2))
+ // Do nothing if not clicked on anything
+ if (index == NOOBJECT)
+ return;
+
+ // If not holding anything
+ if (HeldItem == INV_NOICON && InvD[ino].contents[index] &&
+ (!TinselV2 || InvD[ino].contents[index] != HeldItem)) {
+ // Pick-up
+ invObj = GetInvObject(InvD[ino].contents[index]);
+ thisIcon = InvD[ino].contents[index];
+ if (TinselV2)
+ InvTinselEvent(invObj, PICKUP, INV_PICKUP, index);
+ else if (invObj->hScript)
+ InvTinselEvent(invObj, WALKTO, INV_PICKUP, index);
+
+ } else if (HeldItem != INV_NOICON) {
+ // Put-down
+ invObj = GetInvObject(HeldItem);
+
+ // If DROPCODE set, send event, otherwise it's a putdown
+ if (invObj->attribute & IO_DROPCODE && invObj->hScript)
+ InvTinselEvent(invObj, PUTDOWN, INV_PICKUP, index);
+
+ else if (!(invObj->attribute & IO_ONLYINV1 && ino != INV_1)
+ && !(invObj->attribute & IO_ONLYINV2 && ino != INV_2)) {
+ if (TinselV2)
+ InvPutDown(index);
+ else
g_scheduler->createProcess(PID_TCODE, InvPdProcess, &index, sizeof(index));
}
}
}
/**
- * Pick up/put down icon
+ * Handle WALKTO event (Pick up/put down event)
*/
-void InvSLClick(void) {
+static void InvWalkTo(const Common::Point &coOrds) {
int i;
- int aniX, aniY; // Cursor's animation position
- GetCursorXY(&aniX, &aniY, false);
-
- switch (InvArea(aniX, aniY)) {
+ switch (InvArea(coOrds.x, coOrds.y)) {
case I_NOTIN:
if (ino == INV_CONV)
ConvAction(INV_CLOSEICON);
+ if ((cd.box == hopperBox1) || (cd.box == hopperBox2))
+ FreeSceneHopper();
KillInventory();
break;
@@ -3990,22 +5187,24 @@ void InvSLClick(void) {
case I_BODY:
if (ino == INV_CONF) {
if (!InventoryHidden)
- ConfAction(WhichInvBox(aniX, aniY, false), false);
+ MenuAction(WhichMenuBox(coOrds.x, coOrds.y, false), false);
} else {
- i = InvItem(&aniX, &aniY, false);
-
- // Special bodge for David, to
- // cater for drop in dead space between icons
- if (i == INV_NOICON && HeldItem != INV_NOICON && (ino == INV_1 || ino == INV_2)) {
- aniX += 1; // 1 to the right
- i = InvItem(&aniX, &aniY, false);
+ Common::Point pt = coOrds;
+ i = InvItem(pt, false);
+
+ // To cater for drop in dead space between icons,
+ // look 1 pixel right, then 1 down, then 1 right and down.
+ if (i == INV_NOICON && HeldItem != INV_NOICON &&
+ (ino == INV_1 || ino == INV_2)) {
+ pt.x += 1; // 1 to the right
+ i = InvItem(pt, false);
if (i == INV_NOICON) {
- aniX -= 1; // 1 down
- aniY += 1;
- i = InvItem(&aniX, &aniY, false);
+ pt.x -= 1; // 1 down
+ pt.y += 1;
+ i = InvItem(pt, false);
if (i == INV_NOICON) {
- aniX += 1; // 1 down-right
- i = InvItem(&aniX, &aniY, false);
+ pt.x += 1; // 1 down-right
+ i = InvItem(pt, false);
}
}
}
@@ -4031,23 +5230,25 @@ void InvAction(void) {
case I_BODY:
if (ino == INV_CONF) {
if (!InventoryHidden)
- ConfAction(WhichInvBox(aniX, aniY, false), true);
+ MenuAction(WhichMenuBox(aniX, aniY, false), true);
} else if (ino == INV_CONV) {
index = InvItem(&aniX, &aniY, false);
ConvAction(index);
} else {
index = InvItem(&aniX, &aniY, false);
if (index != INV_NOICON) {
- if (InvD[ino].ItemOrder[index] && InvD[ino].ItemOrder[index] != HeldItem) {
- invObj = findInvObject(InvD[ino].ItemOrder[index]);
- if (invObj->hScript)
- RunInvTinselCode(invObj, ACTION, INV_ACTION, index);
+ if (InvD[ino].contents[index] && InvD[ino].contents[index] != HeldItem) {
+ invObj = GetInvObject(InvD[ino].contents[index]);
+ if (TinselV2)
+ thisIcon = InvD[ino].contents[index];
+ if (TinselV2 || (invObj->hScript))
+ InvTinselEvent(invObj, ACTION, INV_ACTION, index);
}
}
}
break;
- case I_MOVE: // Maximise/unmaximise inventory
+ case I_HEADER: // Maximise/unmaximise inventory
if (!InvD[ino].resizable)
break;
@@ -4100,21 +5301,19 @@ void InvAction(void) {
}
-void InvLook(void) {
+static void InvLook(const Common::Point &coOrds) {
int index;
INV_OBJECT *invObj;
- int aniX, aniY;
+ Common::Point pt = coOrds;
- GetCursorXY(&aniX, &aniY, false);
-
- switch (InvArea(aniX, aniY)) {
+ switch (InvArea(pt.x, pt.y)) {
case I_BODY:
- index = InvItem(&aniX, &aniY, false);
+ index = InvItem(pt, false);
if (index != INV_NOICON) {
- if (InvD[ino].ItemOrder[index] && InvD[ino].ItemOrder[index] != HeldItem) {
- invObj = findInvObject(InvD[ino].ItemOrder[index]);
+ if (InvD[ino].contents[index] && InvD[ino].contents[index] != HeldItem) {
+ invObj = GetInvObject(InvD[ino].contents[index]);
if (invObj->hScript)
- RunInvTinselCode(invObj, LOOK, INV_LOOK, index);
+ InvTinselEvent(invObj, LOOK, INV_LOOK, index);
}
}
break;
@@ -4132,74 +5331,64 @@ void InvLook(void) {
/********************* Incoming events ************************************/
/**************************************************************************/
-
-void ButtonToInventory(BUTEVENT be) {
+void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) {
if (InventoryHidden)
return;
- switch (be) {
- case INV_PICKUP: // BE_SLEFT
- InvSLClick();
+ switch (pEvent) {
+ case PLR_PROV_WALKTO:
+ if (MenuActive()) {
+ ProcessedProvisional();
+ InvWalkTo(coOrds);
+ }
+ break;
+
+ case PLR_WALKTO: // PLR_SLEFT
+ InvWalkTo(coOrds);
break;
- case INV_LOOK: // BE_SRIGHT
- if (IsConfWindow())
- InvSLClick();
+ case INV_LOOK: // PLR_SRIGHT
+ if (MenuActive())
+ InvWalkTo(coOrds);
else
- InvLook();
+ InvLook(coOrds);
break;
- case INV_ACTION: // BE_DLEFT
+ case PLR_ACTION: // PLR_DLEFT
if (InvDragging != ID_MDCONT)
InvDragEnd();
InvAction();
break;
- case BE_LDSTART: // Left drag start
+ case PLR_DRAG1_START: // Left drag start
InvDragStart();
break;
- case BE_LDEND: // Left drag end
+ case PLR_DRAG1_END: // Left drag end
InvDragEnd();
break;
-// case BE_DLEFT: // Double click left (also ends left drag)
-// ButtonToInventory(LDEND);
-// break;
-
- case BE_RDSTART:
- case BE_RDEND:
- case BE_UNKNOWN:
- break;
- default:
- break;
- }
-}
-
-void KeyToInventory(KEYEVENT ke) {
- int i;
-
- switch (ke) {
- case ESC_KEY:
- if (InventoryState == ACTIVE_INV && ino == INV_CONF && cd.Box != optionBox)
- bOpenConf = true;
+ case PLR_ESCAPE:
+ if (MenuActive()) {
+ if (cd.box != optionBox && cd.box != hopperBox1 && cd.box != hopperBox2)
+ bReOpenMenu = true;
+ if ((cd.box == hopperBox1) || (cd.box == hopperBox2))
+ FreeSceneHopper();
+ }
CloseInventory();
break;
- case PGDN_KEY:
- if (ino == INV_CONF) {
+ case PLR_PGDN:
+ if (ino == INV_MENU) {
// Only act if load or save screen
- if (cd.Box != loadBox && cd.Box != saveBox)
- break;
-
- ConfActionSpecial(IB_SLIDE_DOWN);
+ MenuPageDown();
} else {
// This code is a copy of SLClick on IB_SLIDE_DOWN
// TODO: So share this duplicate code
if (InvD[ino].NoofVicons == 1)
if (InvD[ino].FirstDisp + InvD[ino].NoofHicons*InvD[ino].NoofVicons < InvD[ino].NoofItems)
InvD[ino].FirstDisp += InvD[ino].NoofHicons;
- for (i = 1; i < InvD[ino].NoofVicons; i++) {
+ for (int i = 1; i < InvD[ino].NoofVicons; i++) {
if (InvD[ino].FirstDisp + InvD[ino].NoofHicons*InvD[ino].NoofVicons < InvD[ino].NoofItems)
InvD[ino].FirstDisp += InvD[ino].NoofHicons;
}
@@ -4207,19 +5396,16 @@ void KeyToInventory(KEYEVENT ke) {
}
break;
- case PGUP_KEY:
- if (ino == INV_CONF) {
+ case PLR_PGUP:
+ if (ino == INV_MENU) {
// Only act if load or save screen
- if (cd.Box != loadBox && cd.Box != saveBox)
- break;
-
- ConfActionSpecial(IB_SLIDE_UP);
+ MenuPageUp();
} else {
// This code is a copy of SLClick on I_SLIDE_UP
// TODO: So share this duplicate code
if (InvD[ino].NoofVicons == 1)
InvD[ino].FirstDisp -= InvD[ino].NoofHicons;
- for (i = 1; i < InvD[ino].NoofVicons; i++)
+ for (int i = 1; i < InvD[ino].NoofVicons; i++)
InvD[ino].FirstDisp -= InvD[ino].NoofHicons;
if (InvD[ino].FirstDisp < 0)
InvD[ino].FirstDisp = 0;
@@ -4227,13 +5413,18 @@ void KeyToInventory(KEYEVENT ke) {
}
break;
- case HOME_KEY:
- if (ino == INV_CONF) {
+ case PLR_HOME:
+ if (ino == INV_MENU) {
// Only act if load or save screen
- if (cd.Box != loadBox && cd.Box != saveBox)
+ if (cd.box == loadBox || cd.box == saveBox)
+ FirstFile(0);
+ else if (cd.box == hopperBox1)
+ FirstScene(0);
+ else if (cd.box == hopperBox2)
+ FirstEntry(0);
+ else
break;
- firstFile(0);
AddBoxes(true);
cd.selBox = 0;
Select(cd.selBox, true);
@@ -4243,13 +5434,17 @@ void KeyToInventory(KEYEVENT ke) {
}
break;
- case END_KEY:
- if (ino == INV_CONF) {
- // Only act if load or save screen
- if (cd.Box != loadBox && cd.Box != saveBox)
+ case PLR_END:
+ if (ino == INV_MENU) {
+ if (cd.box == loadBox || cd.box == saveBox)
+ FirstFile(MAX_SAVED_FILES); // Will get reduced to appropriate value
+ else if (cd.box == hopperBox1)
+ FirstScene(numScenes); // Will get reduced to appropriate value
+ else if (cd.box == hopperBox2)
+ FirstEntry(numEntries); // Will get reduced to appropriate value
+ else
break;
- firstFile(MAX_SFILES); // Will get reduced to appropriate value
AddBoxes(true);
cd.selBox = 0;
Select(cd.selBox, true);
@@ -4260,9 +5455,8 @@ void KeyToInventory(KEYEVENT ke) {
ItemsChanged = true;
}
break;
-
default:
- error("We're at KeyToInventory(), with default");
+ break;
}
}
@@ -4274,12 +5468,11 @@ void KeyToInventory(KEYEVENT ke) {
* Called from Glitter function invdepict()
* Changes (permanently) the animation film for that object.
*/
-
-void invObjectFilm(int object, SCNHANDLE hFilm) {
+void SetObjectFilm(int object, SCNHANDLE hFilm) {
INV_OBJECT *invObj;
- invObj = findInvObject(object);
- invObj->hFilm = hFilm;
+ invObj = GetInvObject(object);
+ invObj->hIconFilm = hFilm;
if (HeldItem != object)
ItemsChanged = true;
@@ -4288,7 +5481,6 @@ void invObjectFilm(int object, SCNHANDLE hFilm) {
/**
* (Un)serialize the inventory data for save/restore game.
*/
-
void syncInvInfo(Serializer &s) {
for (int i = 0; i < NUM_INV; i++) {
s.syncAsSint32LE(InvD[i].MinHicons);
@@ -4298,7 +5490,7 @@ void syncInvInfo(Serializer &s) {
s.syncAsSint32LE(InvD[i].NoofHicons);
s.syncAsSint32LE(InvD[i].NoofVicons);
for (int j = 0; j < MAX_ININV; j++) {
- s.syncAsSint32LE(InvD[i].ItemOrder[j]);
+ s.syncAsSint32LE(InvD[i].contents[j]);
}
s.syncAsSint32LE(InvD[i].NoofItems);
s.syncAsSint32LE(InvD[i].FirstDisp);
@@ -4309,11 +5501,17 @@ void syncInvInfo(Serializer &s) {
s.syncAsSint32LE(InvD[i].MaxInvObj);
s.syncAsSint32LE(InvD[i].hInvTitle);
s.syncAsSint32LE(InvD[i].resizable);
- s.syncAsSint32LE(InvD[i].moveable);
+ s.syncAsSint32LE(InvD[i].bMoveable);
s.syncAsSint32LE(InvD[i].sNoofHicons);
s.syncAsSint32LE(InvD[i].sNoofVicons);
s.syncAsSint32LE(InvD[i].bMax);
}
+
+ if (TinselV2) {
+ for (int i = 0; i < numObjects; ++i)
+ s.syncAsUint32LE(invFilms[i]);
+ s.syncAsUint32LE(heldFilm);
+ }
}
/**************************************************************************/
@@ -4327,20 +5525,39 @@ void syncInvInfo(Serializer &s) {
// Note: the SCHANDLE type here has been changed to a void*
void RegisterIcons(void *cptr, int num) {
numObjects = num;
- pio = (INV_OBJECT *) cptr;
+ invObjects = (INV_OBJECT *) cptr;
+
+ if (TinselV2) {
+ if (invFilms == NULL)
+ // First time - allocate memory
+ invFilms = (SCNHANDLE *)MemoryAlloc(DWM_FIXED | DWM_ZEROINIT, numObjects * sizeof(SCNHANDLE));
+
+ if (invFilms == NULL)
+ error(NO_MEM, "inventory scripts");
+
+ // Add defined permanent conversation icons
+ // and store all the films separately
+ int i;
+ INV_OBJECT *pio;
+ for (i = 0, pio = invObjects; i < numObjects; i++, pio++) {
+ if (pio->attribute & PERMACONV)
+ PermaConvIcon(pio->id, pio->attribute & CONVENDITEM);
+
+ invFilms[i] = pio->hIconFilm;
+ }
+ }
}
/**
* Called from Glitter function 'dec_invw()' - Declare the bits that the
* inventory windows are constructed from, and special cursors.
*/
-
void setInvWinParts(SCNHANDLE hf) {
#ifdef DEBUG
const FILM *pfilm;
#endif
- winPartsf = hf;
+ hWinParts = hf;
#ifdef DEBUG
pfilm = (const FILM *)LockMem(hf);
@@ -4352,7 +5569,6 @@ void setInvWinParts(SCNHANDLE hf) {
* Called from Glitter function 'dec_flags()' - Declare the language
* flag films
*/
-
void setFlagFilms(SCNHANDLE hf) {
#ifdef DEBUG
const FILM *pfilm;
@@ -4374,7 +5590,6 @@ void setConfigStrings(SCNHANDLE *tp) {
* Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
* - Declare the heading text and dimensions etc.
*/
-
void idec_inv(int num, SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
@@ -4402,7 +5617,7 @@ void idec_inv(int num, SCNHANDLE text, int MaxContents,
InvD[num].NoofHicons = StartWidth;
InvD[num].NoofVicons = StartHeight;
- memset(InvD[num].ItemOrder, 0, sizeof(InvD[num].ItemOrder));
+ memset(InvD[num].contents, 0, sizeof(InvD[num].contents));
InvD[num].NoofItems = 0;
InvD[num].FirstDisp = 0;
@@ -4419,7 +5634,7 @@ void idec_inv(int num, SCNHANDLE text, int MaxContents,
if (MaxWidth != MinWidth && MaxHeight != MinHeight)
InvD[num].resizable = true;
- InvD[num].moveable = moveable;
+ InvD[num].bMoveable = moveable;
InvD[num].bMax = false;
}
@@ -4428,7 +5643,6 @@ void idec_inv(int num, SCNHANDLE text, int MaxContents,
* Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
* - Declare the heading text and dimensions etc.
*/
-
void idec_convw(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
@@ -4442,7 +5656,6 @@ void idec_convw(SCNHANDLE text, int MaxContents,
* Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
* - Declare the heading text and dimensions etc.
*/
-
void idec_inv1(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
@@ -4456,7 +5669,6 @@ void idec_inv1(SCNHANDLE text, int MaxContents,
* Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
* - Declare the heading text and dimensions etc.
*/
-
void idec_inv2(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
@@ -4513,12 +5725,10 @@ bool IsTopWindow(void) {
return (InventoryState == BOGUS_INV);
}
-
-bool IsConfWindow(void) {
+bool MenuActive(void) {
return (InventoryState == ACTIVE_INV && ino == INV_CONF);
}
-
bool IsConvWindow(void) {
return (InventoryState == ACTIVE_INV && ino == INV_CONV);
}
diff --git a/engines/tinsel/inventory.h b/engines/tinsel/dialogs.h
index d83439c68f..faf95c7d1c 100644
--- a/engines/tinsel/inventory.h
+++ b/engines/tinsel/dialogs.h
@@ -29,52 +29,67 @@
#define TINSEL_INVENTORY_H
#include "tinsel/dw.h"
-#include "tinsel/events.h" // for KEYEVENT, BUTEVENT
+#include "tinsel/events.h" // for PLR_EVENT, PLR_EVENT
namespace Tinsel {
class Serializer;
enum {
- INV_OPEN = -1,
+ INV_OPEN = -1, // DW1 only
INV_CONV = 0,
INV_1 = 1,
INV_2 = 2,
INV_CONF = 3,
+ INV_MENU = 3, // DW2 constant
+ NUM_INV = 4,
- NUM_INV = 4
+ // Discworld 2 constants
+ DW2_INV_OPEN = 5,
+ INV_DEFAULT = 6
};
/** structure of each inventory object */
struct INV_OBJECT {
int32 id; // inventory objects id
- SCNHANDLE hFilm; // inventory objects animation film
+ SCNHANDLE hIconFilm; // inventory objects animation film
SCNHANDLE hScript; // inventory objects event handling script
int32 attribute; // inventory object's attribute
};
+// attribute values - not a bit bit field to prevent portability problems
+#define DROPCODE 0x01
+#define ONLYINV1 0x02
+#define ONLYINV2 0x04
+#define DEFINV1 0x08
+#define DEFINV2 0x10
+#define PERMACONV 0x20
+#define CONVENDITEM 0x40
+
void PopUpInventory(int invno);
enum CONFTYPE {
- SAVE, LOAD, QUIT, OPTION, RESTART, SOUND, CONTROLS, SUBT, TOPWIN
+ MAIN_MENU, SAVE_MENU, LOAD_MENU, QUIT_MENU, RESTART_MENU, SOUND_MENU,
+ CONTROLS_MENU, SUBTITLES_MENU, HOPPER_MENU1, HOPPER_MENU2, TOP_WINDOW
};
-void PopUpConf(CONFTYPE type);
+void OpenMenu(CONFTYPE type);
void Xmovement(int x);
void Ymovement(int y);
-void ButtonToInventory(BUTEVENT be);
-
-void KeyToInventory(KEYEVENT ke);
+void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds);
+void ButtonToInventory(PLR_EVENT be);
+void KeyToInventory(PLR_EVENT ke);
int WhichItemHeld(void);
-void HoldItem(int item);
+void HoldItem(int item, bool bKeepFilm = false);
void DropItem(int item);
-void AddToInventory(int invno, int icon, bool hold);
+void ClearInventory(int invno);
+void AddToInventory(int invno, int icon, bool hold = false);
bool RemFromInventory(int invno, int icon);
@@ -89,25 +104,34 @@ void idec_inv2(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
bool InventoryActive(void);
-void AddIconToPermanentDefaultList(int icon);
+void PermaConvIcon(int icon, bool bEnd = false);
void convPos(int bpos);
void ConvPoly(HPOLYGON hp);
-int convIcon(void);
+int GetIcon(void);
void CloseDownConv(void);
-void convHide(bool hide);
-bool convHid(void);
+void HideConversation(bool hide);
+bool ConvIsHidden(void);
enum {
+ NOOBJECT = -1,
INV_NOICON = -1,
INV_CLOSEICON = -2,
INV_OPENICON = -3,
INV_HELDNOTIN = -4
};
-void ConvAction(int index);
+enum CONV_PARAM {
+ CONV_DEF,
+ CONV_BOTTOM,
+ CONV_END,
+ CONV_TOP
+};
-void InventoryIconCursor(void);
+
+void ConvAction(int index);
+void SetConvDetails(CONV_PARAM fn, HPOLYGON hPoly, int ano);
+void InventoryIconCursor(bool bNewItem);
void setInvWinParts(SCNHANDLE hf);
void setFlagFilms(SCNHANDLE hf);
@@ -122,8 +146,6 @@ bool IsInInventory(int object, int invnum);
void KillInventory(void);
-void invObjectFilm(int object, SCNHANDLE hFilm);
-
void syncInvInfo(Serializer &s);
int InvGetLimit(int invno);
@@ -134,9 +156,13 @@ void InvSetSize(int invno, int MinWidth, int MinHeight,
int WhichInventoryOpen(void);
bool IsTopWindow(void);
-bool IsConfWindow(void);
+bool MenuActive(void);
bool IsConvWindow(void);
+void SetObjectFilm(int object, SCNHANDLE hFilm);
+
+void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, int myEscape, bool *result = NULL);
+
} // end of namespace Tinsel
#endif /* TINSEL_INVENTRY_H */
diff --git a/engines/tinsel/drives.cpp b/engines/tinsel/drives.cpp
new file mode 100644
index 0000000000..2e87eb1715
--- /dev/null
+++ b/engines/tinsel/drives.cpp
@@ -0,0 +1,129 @@
+/* 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$
+ *
+ * CD/drive handling functions
+ */
+
+#include "tinsel/drives.h"
+#include "tinsel/scene.h"
+#include "tinsel/tinsel.h"
+#include "tinsel/sched.h"
+#include "tinsel/strres.h"
+
+namespace Tinsel {
+
+static char currentCD = '1';
+static uint32 cdFlags[] = { fCd1, fCd2, fCd3, fCd4, fCd5, fCd6, fCd7, fCd8 };
+
+static bool bChangingCD = false;
+static char nextCD = '\0';
+
+
+void CdCD(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ while (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;
+}
+
+int GetCurrentCD(void) {
+ // count from 1
+ return (currentCD - '1' + 1);
+}
+
+void SetCD(int flags) {
+ if (flags & cdFlags[currentCD - '1'])
+ return;
+
+ error("SetCD() problem");
+}
+
+int GetCD(int flags) {
+ int i;
+ char cd = '\0';
+
+ if (flags & cdFlags[currentCD - '1'])
+ return GetCurrentCD();
+
+ for (i = 0; i < 8; i++) {
+ if (flags & cdFlags[i]) {
+ cd = (char)(i + '1');
+ break;
+ }
+ }
+ assert(i != 8);
+
+ nextCD = cd;
+ return cd;
+}
+
+void DoCdChange(void) {
+ if (bChangingCD) {
+ _vm->_sound->closeSampleStream();
+ _vm->_sound->openSampleFiles();
+ ChangeLanguage(TextLanguage());
+ bChangingCD = false;
+ }
+}
+
+void SetNextCD(int cdNumber) {
+ assert(cdNumber == 1 || cdNumber == 2);
+
+ nextCD = (char)(cdNumber + '1' - 1);
+}
+
+bool GotoCD(void) {
+ // WORKAROUND: Somehow, CdDoChange() is called twice... Hopefully, this guard helps
+ if (currentCD == nextCD)
+ return false;
+
+ currentCD = nextCD;
+
+/* if (bNoCD) {
+ strcpy(cdDirectory, hdDirectory);
+ cdLastBit[3] = currentCD;
+ strcat(cdDirectory, cdLastBit);
+ }
+*/
+ bChangingCD = true;
+
+ return true;
+}
+
+} // end of namespace Tinsel
diff --git a/engines/tinsel/drives.h b/engines/tinsel/drives.h
new file mode 100644
index 0000000000..4441466f82
--- /dev/null
+++ b/engines/tinsel/drives.h
@@ -0,0 +1,63 @@
+/* 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$
+ *
+ * CD/drive handling functions
+ */
+
+#ifndef TINSEL_DRIVES_H
+#define TINSEL_DRIVES_H
+
+#include "tinsel/dw.h"
+#include "tinsel/coroutine.h"
+
+namespace Tinsel {
+
+// flags2
+#define fCd1 0x00000001L
+#define fCd2 0x00000002L
+#define fCd3 0x00000004L
+#define fCd4 0x00000008L
+#define fCd5 0x00000010L
+#define fCd6 0x00000020L
+#define fCd7 0x00000040L
+#define fCd8 0x00000080L
+
+#define fAllCds (fCd1|fCd2|fCd3|fCd4|fCd5|fCd6|fCd7|fCd8)
+
+void DoCdChange(void);
+
+void CdCD(CORO_PARAM);
+
+int GetCurrentCD(void);
+
+void SetCD(int flags);
+
+int GetCD(int flags);
+
+void SetNextCD(int cdNumber);
+
+bool GotoCD(void);
+
+} // end of namespace Tinsel
+
+#endif /* TINSEL_DRIVES_H */
diff --git a/engines/tinsel/dw.h b/engines/tinsel/dw.h
index 9ceef37858..af5ce99d29 100644
--- a/engines/tinsel/dw.h
+++ b/engines/tinsel/dw.h
@@ -47,9 +47,8 @@ typedef int HPOLYGON;
#define MIDI_FILE "midi.dat" // all MIDI sequences
#define INDEX_FILENAME "index" // name of index file
-#define SCNHANDLE_SHIFT 23 // amount to shift scene handles by
#define NO_SCNHANDLES 300 // number of memory handles for scenes
-#define MASTER_SCNHANDLE (0 << SCNHANDLE_SHIFT) // master scene memory handle
+#define MASTER_SCNHANDLE 0 // master scene memory handle
// the minimum value a integer number can have
#define MIN_INT (1 << (8*sizeof(int) - 1))
@@ -70,8 +69,7 @@ typedef int HPOLYGON;
#define FIELD_WORLD 0
#define FIELD_STATUS 1
-
-
+#define ZSHIFT 10
// We don't set the Z position for print and talk text
// i.e. it gets a Z position of 0
@@ -101,6 +99,7 @@ typedef int HPOLYGON;
#define MAX_MOVERS 6 // Moving actors using path system
#define MAX_SAVED_ACTORS 32 // Saved 'Normal' actors
#define MAX_SAVED_ALIVES 512 // Saves actors'lives
+#define MAX_SAVED_ACTOR_Z 512 // Saves actors' Z-ness
// Legal non-existant entrance number for LoadScene()
#define NO_ENTRY_NUM (-3458) // Magic unlikely number
@@ -110,13 +109,19 @@ typedef int HPOLYGON;
// Language for the resource strings
enum LANGUAGE {
- TXT_ENGLISH,
- TXT_FRENCH,
- TXT_GERMAN,
- TXT_ITALIAN,
- TXT_SPANISH
+ TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH,
+ TXT_HEBREW, TXT_HUNGARIAN, TXT_JAPANESE, TXT_US,
+ NUM_LANGUAGES
};
+#define MAX_READ_RETRIES 5
+
+// Definitions used for error messages
+#define FILE_IS_CORRUPT "File %s is corrupt"
+#define FILE_READ_ERROR "Error reading file %s"
+#define CANNOT_FIND_FILE "Cannot find file %s"
+#define NO_MEM "Cannot allocate memory for %s!"
+
} // end of namespace Tinsel
#endif // TINSEL_DW_H
diff --git a/engines/tinsel/effect.cpp b/engines/tinsel/effect.cpp
index 91645da71b..95785e5840 100644
--- a/engines/tinsel/effect.cpp
+++ b/engines/tinsel/effect.cpp
@@ -41,13 +41,14 @@
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/sched.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
struct EP_INIT {
HPOLYGON hEpoly;
- PMACTOR pActor;
+ PMOVER pMover;
int index;
};
@@ -61,24 +62,32 @@ static void EffectProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
- EP_INIT *to = (EP_INIT *)param; // get the stuff copied to process when it was created
+ const EP_INIT *to = (const EP_INIT *)param; // get the stuff copied to process when it was created
CORO_BEGIN_CODE(_ctx);
int x, y; // Lead actor position
// Run effect poly enter script
- effRunPolyTinselCode(to->hEpoly, ENTER, to->pActor->actorID);
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKIN,
+ GetMoverId(to->pMover), false, 0));
+ else
+ effRunPolyTinselCode(to->hEpoly, WALKIN, to->pMover->actorID);
do {
CORO_SLEEP(1);
- GetMActorPosition(to->pActor, &x, &y);
+ GetMoverPosition(to->pMover, &x, &y);
} while (InPolygon(x, y, EFFECT) == to->hEpoly);
// Run effect poly leave script
- effRunPolyTinselCode(to->hEpoly, LEAVE, to->pActor->actorID);
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKOUT,
+ GetMoverId(to->pMover), false, 0));
+ else
+ effRunPolyTinselCode(to->hEpoly, WALKOUT, to->pMover->actorID);
- SetMAinEffectPoly(to->index, false);
+ SetMoverInEffect(to->index, false);
CORO_END_CODE;
}
@@ -88,7 +97,7 @@ static void EffectProcess(CORO_PARAM, const void *param) {
* it has just entered one. If it has, a process is started up to run
* the polygon's Glitter code.
*/
-static void FettleEffectPolys(int x, int y, int index, PMACTOR pActor) {
+static void FettleEffectPolys(int x, int y, int index, PMOVER pActor) {
HPOLYGON hPoly;
EP_INIT epi;
@@ -97,10 +106,10 @@ static void FettleEffectPolys(int x, int y, int index, PMACTOR pActor) {
hPoly = InPolygon(x, y, EFFECT);
if (hPoly != NOPOLY) {
//Just entered effect polygon
- SetMAinEffectPoly(index, true);
+ SetMoverInEffect(index, true);
epi.hEpoly = hPoly;
- epi.pActor = pActor;
+ epi.pMover = pActor;
epi.index = index;
g_scheduler->createProcess(PID_TCODE, EffectProcess, &epi, sizeof(epi));
}
@@ -118,10 +127,10 @@ void EffectPolyProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
while (1) {
for (int i = 0; i < MAX_MOVERS; i++) {
- PMACTOR pActor = GetLiveMover(i);
+ PMOVER pActor = GetLiveMover(i);
if (pActor != NULL) {
int x, y;
- GetMActorPosition(pActor, &x, &y);
+ GetMoverPosition(pActor, &x, &y);
FettleEffectPolys(x, y, i, pActor);
}
}
diff --git a/engines/tinsel/events.cpp b/engines/tinsel/events.cpp
index bf9f428fd4..91f75fe71e 100644
--- a/engines/tinsel/events.cpp
+++ b/engines/tinsel/events.cpp
@@ -26,14 +26,17 @@
*/
#include "tinsel/actors.h"
+#include "tinsel/background.h"
#include "tinsel/config.h"
+#include "tinsel/coroutine.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/handle.h" // For LockMem()
-#include "tinsel/inventory.h"
+#include "tinsel/dialogs.h"
#include "tinsel/move.h" // For walking lead actor
#include "tinsel/pcode.h" // For Interpret()
+#include "tinsel/pdisplay.h"
#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h" // For walking lead actor
@@ -41,6 +44,7 @@
#include "tinsel/scroll.h" // For DontScrollCursor()
#include "tinsel/timers.h" // DwGetCurrentTime()
#include "tinsel/tinlib.h" // For control()
+#include "tinsel/tinsel.h"
#include "tinsel/token.h"
namespace Tinsel {
@@ -51,22 +55,25 @@ namespace Tinsel {
extern int GetTaggedActor(void);
extern HPOLYGON GetTaggedPoly(void);
-
//----------------- EXTERNAL GLOBAL DATA ---------------------
-extern bool bEnableF1;
-
+extern bool bEnableMenu;
//----------------- LOCAL GLOBAL DATA --------------------
-static int userEvents = 0; // Whenever a button or a key comes in
static uint32 lastUserEvent = 0; // Time it hapenned
-static int butEvents = 0; // Single or double, left or right. Or escape key.
-static int escEvents = 0; // Escape key
-
+static int leftEvents = 0; // Single or double, left or right. Or escape key.
+static int escEvents = 1; // Escape key
+static int userEvents = 0; // Whenever a button or a key comes in
static int eCount = 0;
+static int controlState;
+static bool bStartOff;
+
+static int controlX, controlY;
+static bool bProvNotProcessed = false;
+
/**
* Gets called before each schedule, only 1 user action per schedule
* is allowed.
@@ -87,12 +94,12 @@ void IncUserEvents(void) {
* If this is a double click, the process from the waiting single click
* gets killed.
*/
-void AllowDclick(CORO_PARAM, BUTEVENT be) {
+void AllowDclick(CORO_PARAM, PLR_EVENT be) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- if (be == BE_SLEFT) {
+ if (be == PLR_SLEFT) {
GetToken(TOKEN_LEFT_BUT);
CORO_SLEEP(dclickSpeed+1);
FreeToken(TOKEN_LEFT_BUT);
@@ -103,7 +110,7 @@ void AllowDclick(CORO_PARAM, BUTEVENT be) {
break;
- } else if (be == BE_DLEFT) {
+ } else if (be == PLR_DLEFT) {
GetToken(TOKEN_LEFT_BUT);
FreeToken(TOKEN_LEFT_BUT);
}
@@ -111,86 +118,112 @@ void AllowDclick(CORO_PARAM, BUTEVENT be) {
}
/**
- * Take control from player, if the player has it.
- * Return TRUE if control taken, FALSE if not.
+ * Re-enables user control
*/
+void ControlOn(void) {
+ if (!TinselV2) {
+ Control(CONTROL_ON);
+ return;
+ }
-bool GetControl(int param) {
- if (TestToken(TOKEN_CONTROL)) {
- control(param);
- return true;
- } else
- return false;
-}
+ bEnableMenu = false;
-struct TP_INIT {
- HPOLYGON hPoly; // Polygon
- USER_EVENT event; // Trigerring event
- BUTEVENT bev; // To allow for double clicks
- bool take_control; // Set if control should be taken
- // while code is running.
- int actor;
-};
+ if (controlState == CONTROL_OFF) {
+ // Control is on
+ controlState = CONTROL_ON;
+
+ // Restore cursor to where it was
+ if (bStartOff == true)
+ bStartOff = false;
+ else
+ SetCursorXY(controlX, controlY);
+
+ // Re-instate cursor
+ UnHideCursor();
+
+ // Turn tags back on
+ if (!InventoryActive())
+ EnableTags();
+ }
+}
/**
- * Runs glitter code associated with a polygon.
+ * Takes control from the user
*/
-static void PolyTinselProcess(CORO_PARAM, const void *param) {
- // COROUTINE
- CORO_BEGIN_CONTEXT;
- INT_CONTEXT *pic;
- bool took_control; // Set if this function takes control
- CORO_END_CONTEXT(_ctx);
+void ControlOff(void) {
+ if (!TinselV2) {
+ Control(CONTROL_ON);
+ return;
+ }
- TP_INIT *to = (TP_INIT *)param; // get the stuff copied to process when it was created
+ bEnableMenu = false;
- CORO_BEGIN_CODE(_ctx);
+ if (controlState == CONTROL_ON) {
+ // Control is off
+ controlState = CONTROL_OFF;
- CORO_INVOKE_1(AllowDclick, to->bev); // May kill us if single click
+ // Store cursor position
+ GetCursorXY(&controlX, &controlY, true);
- // Control may have gone off during AllowDclick()
- if (!TestToken(TOKEN_CONTROL)
- && (to->event == WALKTO || to->event == ACTION || to->event == LOOK))
- CORO_KILL_SELF();
+ // Blank out cursor
+ DwHideCursor();
- // Take control, if requested
- if (to->take_control)
- _ctx->took_control = GetControl(CONTROL_OFF);
- else
- _ctx->took_control = false;
+ // Switch off tags
+ DisableTags();
+ }
+}
- // Hide conversation if appropriate
- if (to->event == CONVERSE)
- convHide(true);
+/**
+ * Prevent tags and cursor re-appearing
+ */
+void ControlStartOff(void) {
+ if (!TinselV2) {
+ Control(CONTROL_STARTOFF);
+ return;
+ }
- // Run the code
- _ctx->pic = InitInterpretContext(GS_POLYGON, getPolyScript(to->hPoly), to->event, to->hPoly, to->actor, NULL);
- CORO_INVOKE_1(Interpret, _ctx->pic);
+ bEnableMenu = false;
- // Free control if we took it
- if (_ctx->took_control)
- control(CONTROL_ON);
+ // Control is off
+ controlState = CONTROL_OFF;
- // Restore conv window if applicable
- if (to->event == CONVERSE)
- convHide(false);
+ // Blank out cursor
+ DwHideCursor();
- CORO_END_CODE;
+ // Switch off tags
+ DisableTags();
+
+ bStartOff = true;
}
/**
- * Runs glitter code associated with a polygon.
+ * Take control from player, if the player has it.
+ * Return TRUE if control taken, FALSE if not.
*/
-void RunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, BUTEVENT be, bool tc) {
- TP_INIT to = { hPoly, event, be, tc, 0 };
+bool GetControl(int param) {
+ if (TinselV2)
+ return GetControl();
- g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
+ else if (TestToken(TOKEN_CONTROL)) {
+ Control(param);
+ return true;
+ } else
+ return false;
+}
+
+bool GetControl(void) {
+ if (controlState == CONTROL_ON) {
+ ControlOff();
+ return true;
+ } else
+ return false;
}
-void effRunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, int actor) {
- TP_INIT to = { hPoly, event, BE_NONE, false, actor };
+bool ControlIsOn(void) {
+ if (TinselV2)
+ return (controlState == CONTROL_ON);
- g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
+ return TestToken(TOKEN_CONTROL);
}
//-----------------------------------------------------------------------
@@ -206,22 +239,33 @@ struct WP_INIT {
static void WalkProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
- PMACTOR pActor;
+ PMOVER pMover;
+ int thisWalk;
CORO_END_CONTEXT(_ctx);
- WP_INIT *to = (WP_INIT *)param; // get the co-ordinates - copied to process when it was created
+ const WP_INIT *to = (const WP_INIT *)param; // get the co-ordinates - copied to process when it was created
CORO_BEGIN_CODE(_ctx);
- _ctx->pActor = GetMover(LEAD_ACTOR);
- if (_ctx->pActor->MActorState == NORM_MACTOR) {
- assert(_ctx->pActor->hCpath != NOPOLY); // Lead actor is not in a path
+ _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->pActor, to->x, to->y, false, 0);
+ SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
DontScrollCursor();
- while (MAmoving(_ctx->pActor))
+ while (MoverMoving(_ctx->pMover))
CORO_SLEEP(1);
FreeToken(TOKEN_LEAD);
@@ -230,7 +274,7 @@ static void WalkProcess(CORO_PARAM, const void *param) {
CORO_END_CODE;
}
-void walkto(int x, int y) {
+void WalkTo(int x, int y) {
WP_INIT to = { x, y };
g_scheduler->createProcess(PID_TCODE, WalkProcess, &to, sizeof(to));
@@ -240,7 +284,7 @@ void walkto(int x, int y) {
* Run appropriate actor or polygon glitter code.
* If none, and it's a WALKTO event, do a walk.
*/
-static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
+static void ProcessUserEvent(TINSEL_EVENT uEvent, const Common::Point &coOrds, PLR_EVENT be = PLR_NOEVENT) {
int actor;
int aniX, aniY;
HPOLYGON hPoly;
@@ -249,19 +293,34 @@ static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
if (++eCount != 1)
return;
- if ((actor = GetTaggedActor()) != 0)
- actorEvent(actor, uEvent, be);
- else if ((hPoly = GetTaggedPoly()) != NOPOLY)
- RunPolyTinselCode(hPoly, uEvent, be, false);
- else {
+ if ((actor = GetTaggedActor()) != 0) {
+ // Event for a tagged actor
+ if (TinselV2)
+ ActorEvent(nullContext, actor, uEvent, false, 0);
+ else
+ ActorEvent(actor, uEvent, be);
+ } else if ((hPoly = GetTaggedPoly()) != NOPOLY) {
+ // Event for active tagged polygon
+ if (!TinselV2)
+ RunPolyTinselCode(hPoly, uEvent, be, false);
+ else if (uEvent != PROV_WALKTO)
+ PolygonEvent(nullContext, hPoly, uEvent, 0, false, 0);
+
+ } else {
GetCursorXY(&aniX, &aniY, true);
// There could be a poly involved which has no tag.
- if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY
- || (hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY) {
- RunPolyTinselCode(hPoly, uEvent, be, false);
- } else if (uEvent == WALKTO)
- walkto(aniX, aniY);
+ if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY ||
+ (!TinselV2 && ((hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY))) {
+ if (TinselV2 && (uEvent != PROV_WALKTO))
+ PolygonEvent(nullContext, hPoly, uEvent, 0, false, 0);
+ else if (!TinselV2)
+ RunPolyTinselCode(hPoly, uEvent, be, false);
+ } else if ((uEvent == PROV_WALKTO) || (uEvent == WALKTO)) {
+ if (TinselV2)
+ ProcessedProvisional();
+ WalkTo(aniX, aniY);
+ }
}
}
@@ -269,137 +328,155 @@ static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
/**
* ProcessButEvent
*/
-void ProcessButEvent(BUTEVENT be) {
- IncUserEvents();
-
+void ProcessButEvent(PLR_EVENT be) {
if (bSwapButtons) {
switch (be) {
- case BE_SLEFT:
- be = BE_SRIGHT;
+ case PLR_SLEFT:
+ be = PLR_SRIGHT;
break;
- case BE_DLEFT:
- be = BE_DRIGHT;
+ case PLR_DLEFT:
+ be = PLR_DRIGHT;
break;
- case BE_SRIGHT:
- be = BE_SLEFT;
+ case PLR_SRIGHT:
+ be = PLR_SLEFT;
break;
- case BE_DRIGHT:
- be = BE_DLEFT;
+ case PLR_DRIGHT:
+ be = PLR_DLEFT;
break;
- case BE_LDSTART:
- be = BE_RDSTART;
+ case PLR_DRAG1_START:
+ be = PLR_DRAG2_START;
break;
- case BE_LDEND:
- be = BE_RDEND;
+ case PLR_DRAG1_END:
+ be = PLR_DRAG2_END;
break;
- case BE_RDSTART:
- be = BE_LDSTART;
+ case PLR_DRAG2_START:
+ be = PLR_DRAG1_START;
break;
- case BE_RDEND:
- be = BE_LDEND;
+ case PLR_DRAG2_END:
+ be = PLR_DRAG1_END;
break;
default:
break;
}
}
-// if (be == BE_SLEFT || be == BE_DLEFT || be == BE_SRIGHT || be == BE_DRIGHT)
- if (be == BE_SLEFT || be == BE_SRIGHT)
- butEvents++;
+ PlayerEvent(be, _vm->getMousePosition());
+}
- if (!TestToken(TOKEN_CONTROL) && be != BE_LDEND)
- return;
+/**
+ * ProcessKeyEvent
+ */
- if (InventoryActive()) {
- ButtonToInventory(be);
- } else {
- switch (be) {
- case BE_SLEFT:
- User_Event(WALKTO, BE_SLEFT);
- break;
-
- case BE_DLEFT:
- User_Event(ACTION, BE_DLEFT);
- break;
-
- case BE_SRIGHT:
- User_Event(LOOK, BE_SRIGHT);
- break;
-
- default:
- break;
- }
- }
+void ProcessKeyEvent(PLR_EVENT ke) {
+ // Pass the keyboard event to the player event handler
+ int xp, yp;
+ GetCursorXYNoWait(&xp, &yp, true);
+ const Common::Point mousePos(xp, yp);
+
+ PlayerEvent(ke, mousePos);
+}
+
+#define REAL_ACTION_CHECK if (TinselV2) { \
+ if (DwGetCurrentTime() - lastRealAction < 4) return; \
+ lastRealAction = DwGetCurrentTime(); \
}
/**
- * ProcessKeyEvent
+ * Main interface point for specifying player atcions
*/
+void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
+ // Logging of player actions
+ const char *actionList[] = {
+ "PLR_PROV_WALKTO", "PLR_WALKTO", "PLR_LOOK", "PLR_ACTION", "PLR_ESCAPE",
+ "PLR_MENU", "PLR_QUIT", "PLR_PGUP", "PLR_PGDN", "PLR_HOME", "PLR_END",
+ "PLR_DRAG1_START", "PLR_DRAG1_END", "PLR_DRAG2_START", "PLR_DRAG2_END",
+ "PLR_JUMP", "PLR_NOEVENT"};
+ debugC(DEBUG_BASIC, kTinselDebugActions, "%s - (%d,%d)",
+ actionList[pEvent], coOrds.x, coOrds.y);
+ static uint32 lastRealAction = 0;
-void ProcessKeyEvent(KEYEVENT ke) {
// This stuff to allow F1 key during startup.
- if (bEnableF1 && ke == OPTION_KEY)
- control(CONTROL_ON);
+ if (bEnableMenu && pEvent == PLR_MENU)
+ Control(CONTROL_ON);
else
IncUserEvents();
- if (ke == ESC_KEY) {
- escEvents++;
- butEvents++; // Yes, I do mean this
+ if (pEvent == PLR_ESCAPE) {
+ ++escEvents;
+ ++leftEvents; // Yes, I do mean this
+ } else if ((pEvent == PLR_PROV_WALKTO)
+ || (pEvent == PLR_WALKTO)
+ || (pEvent == PLR_LOOK)
+ || (pEvent == PLR_ACTION)) {
+ ++leftEvents;
}
- // FIXME: This comparison is weird - I added (BUTEVENT) cast for now to suppress warning
- if (!TestToken(TOKEN_CONTROL) && (BUTEVENT)ke != BE_LDEND)
+ // Only allow events if player control is on
+ if (!ControlIsOn() && (pEvent != PLR_DRAG1_END))
return;
- switch (ke) {
- case QUIT_KEY:
- PopUpConf(QUIT);
+ if (TinselV2 && InventoryActive()) {
+ int x, y;
+ PlayfieldGetPos(FIELD_WORLD, &x, &y);
+ EventToInventory(pEvent, Common::Point(coOrds.x - x, coOrds.y - y));
+ return;
+ }
+
+ switch (pEvent) {
+ case PLR_QUIT:
+ OpenMenu(QUIT_MENU);
break;
- case OPTION_KEY:
- PopUpConf(OPTION);
+ case PLR_MENU:
+ OpenMenu(MAIN_MENU);
break;
- case SAVE_KEY:
- PopUpConf(SAVE);
+ case PLR_JUMP:
+ OpenMenu(HOPPER_MENU1);
break;
- case LOAD_KEY:
- PopUpConf(LOAD);
+ case PLR_SAVE:
+ OpenMenu(SAVE_MENU);
break;
- case WALKTO_KEY:
- if (InventoryActive())
- ButtonToInventory(BE_SLEFT);
- else
- User_Event(WALKTO, BE_NONE);
+ case PLR_LOAD:
+ OpenMenu(LOAD_MENU);
break;
- case ACTION_KEY:
- if (InventoryActive())
- ButtonToInventory(BE_DLEFT);
+ case PLR_PROV_WALKTO: // Provisional WALKTO !
+ ProcessUserEvent(PROV_WALKTO, coOrds);
+ break;
+
+ case PLR_WALKTO:
+ REAL_ACTION_CHECK;
+
+ if (TinselV2 || !InventoryActive())
+ ProcessUserEvent(WALKTO, coOrds, PLR_SLEFT);
else
- User_Event(ACTION, BE_NONE);
+ EventToInventory(PLR_SLEFT, coOrds);
break;
- case LOOK_KEY:
- if (InventoryActive())
- ButtonToInventory(BE_SRIGHT);
+ case PLR_ACTION:
+ REAL_ACTION_CHECK;
+
+ if (TinselV2 || !InventoryActive())
+ ProcessUserEvent(ACTION, coOrds, PLR_DLEFT);
else
- User_Event(LOOK, BE_NONE);
+ EventToInventory(PLR_DLEFT, coOrds);
break;
- case ESC_KEY:
- case PGUP_KEY:
- case PGDN_KEY:
- case HOME_KEY:
- case END_KEY:
- if (InventoryActive())
- KeyToInventory(ke);
+ case PLR_LOOK:
+ REAL_ACTION_CHECK;
+
+ if (TinselV2 || !InventoryActive())
+ ProcessUserEvent(LOOK, coOrds, PLR_SRIGHT);
+ else
+ EventToInventory(PLR_SRIGHT, coOrds);
break;
default:
+ if (InventoryActive())
+ EventToInventory(pEvent, coOrds);
break;
}
}
@@ -407,7 +484,6 @@ void ProcessKeyEvent(KEYEVENT ke) {
/**
* For ESCapable Glitter sequences
*/
-
int GetEscEvents(void) {
return escEvents;
}
@@ -415,15 +491,21 @@ int GetEscEvents(void) {
/**
* For cutting short talk()s etc.
*/
-
int GetLeftEvents(void) {
- return butEvents;
+ return leftEvents;
+}
+
+bool LeftEventChange(int myleftEvent) {
+ if (leftEvents != myleftEvent) {
+ ProcessedProvisional();
+ return true;
+ } else
+ return false;
}
/**
* For waitkey() Glitter function
*/
-
int getUserEvents(void) {
return userEvents;
}
@@ -436,4 +518,154 @@ void resetUserEventTime(void) {
lastUserEvent = DwGetCurrentTime();
}
+struct PTP_INIT {
+ HPOLYGON hPoly; // Polygon
+ TINSEL_EVENT event; // Trigerring event
+ PLR_EVENT bev; // To allow for double clicks
+ bool take_control; // Set if control should be taken
+ // while code is running.
+ int actor;
+
+ PINT_CONTEXT pic;
+};
+
+/**
+ * Runs glitter code associated with a polygon.
+ */
+void PolyTinselProcess(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ INT_CONTEXT *pic;
+ bool bTookControl; // Set if this function takes control
+
+ CORO_END_CONTEXT(_ctx);
+
+ const PTP_INIT *to = (const PTP_INIT *)param; // get the stuff copied to process when it was created
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (TinselV2) {
+
+ // Take control for CONVERSE events
+ if (to->event == CONVERSE) {
+ _ctx->bTookControl = GetControl();
+ HideConversation(true);
+ } else
+ _ctx->bTookControl = false;
+
+ CORO_INVOKE_1(Interpret, to->pic);
+
+ // Restore conv window if applicable
+ if (to->event == CONVERSE) {
+ // Free control if we took it
+ if (_ctx->bTookControl)
+ ControlOn();
+
+ HideConversation(false);
+ }
+
+ } else {
+
+ CORO_INVOKE_1(AllowDclick, to->bev); // May kill us if single click
+
+ // Control may have gone off during AllowDclick()
+ if (!TestToken(TOKEN_CONTROL)
+ && (to->event == WALKTO || to->event == ACTION || to->event == LOOK))
+ CORO_KILL_SELF();
+
+ // Take control, if requested
+ if (to->take_control)
+ _ctx->bTookControl = GetControl(CONTROL_OFF);
+ else
+ _ctx->bTookControl = false;
+
+ // Hide conversation if appropriate
+ if (to->event == CONVERSE)
+ HideConversation(true);
+
+ // Run the code
+ _ctx->pic = InitInterpretContext(GS_POLYGON, GetPolyScript(to->hPoly), to->event, to->hPoly, to->actor, NULL);
+ CORO_INVOKE_1(Interpret, _ctx->pic);
+
+ // Free control if we took it
+ if (_ctx->bTookControl)
+ Control(CONTROL_ON);
+
+ // Restore conv window if applicable
+ if (to->event == CONVERSE)
+ HideConversation(false);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Run the Polygon process with the given event
+ */
+void PolygonEvent(CORO_PARAM, HPOLYGON hPoly, TINSEL_EVENT tEvent, int actor, bool bWait,
+ int myEscape, bool *result) {
+ CORO_BEGIN_CONTEXT;
+ PTP_INIT to;
+ PPROCESS pProc;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (result) *result = false;
+ _ctx->to.hPoly = -1;
+ _ctx->to.event = tEvent;
+ _ctx->to.pic = InitInterpretContext(GS_POLYGON,
+ GetPolyScript(hPoly),
+ tEvent,
+ hPoly, // Polygon
+ actor, // Actor
+ NULL, // No Object
+ myEscape);
+ if (_ctx->to.pic != NULL) {
+ _ctx->pProc = g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &_ctx->to, sizeof(_ctx->to));
+ AttachInterpret(_ctx->to.pic, _ctx->pProc);
+
+ if (bWait)
+ CORO_INVOKE_2(WaitInterpret,_ctx->pProc, result);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Runs glitter code associated with a polygon.
+ */
+void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc) {
+ PTP_INIT to = { hPoly, event, be, tc, 0, NULL };
+
+ assert(!TinselV2);
+ g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
+}
+
+void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor) {
+ PTP_INIT to = { hPoly, event, PLR_NOEVENT, false, actor, NULL };
+
+ assert(!TinselV2);
+ g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
+}
+
+/**
+ * If provisional event was processed, calling this prevents the
+ * subsequent 'real' event.
+ */
+void ProcessedProvisional(void) {
+ bProvNotProcessed = false;
+}
+
+/**
+ * Resets the bProvNotProcessed flag
+ */
+void ProvNotProcessed(void) {
+ bProvNotProcessed = true;
+}
+
+bool GetProvNotProcessed() {
+ return bProvNotProcessed;
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/events.h b/engines/tinsel/events.h
index bc49d68717..b26bb2c5b0 100644
--- a/engines/tinsel/events.h
+++ b/engines/tinsel/events.h
@@ -29,49 +29,99 @@
#include "tinsel/dw.h"
#include "tinsel/coroutine.h"
+#include "common/rect.h"
namespace Tinsel {
+/*
enum BUTEVENT {
- BE_NONE, BE_SLEFT, BE_DLEFT, BE_SRIGHT, BE_DRIGHT,
- BE_LDSTART, BE_LDEND, BE_RDSTART, BE_RDEND,
- BE_UNKNOWN
+ PLR_NOEVENT, PLR_SLEFT, PLR_DLEFT, PLR_SRIGHT, PLR_DRIGHT,
+ PLR_DRAG1_START, PLR_DRAG1_END, PLR_DRAG2_START, PLR_DRAG2_END,
+ PLR_UNKNOWN
};
-
enum KEYEVENT {
- ESC_KEY, QUIT_KEY, SAVE_KEY, LOAD_KEY, OPTION_KEY,
- PGUP_KEY, PGDN_KEY, HOME_KEY, END_KEY,
- WALKTO_KEY, ACTION_KEY, LOOK_KEY,
+ PLR_ESCAPE, PLR_QUIT, PLR_SAVE, PLR_LOAD, PLR_MENU,
+ PLR_PGUP, PLR_PGDN, PLR_HOME, PLR_END,
+ PLR_WALKTO, PLR_ACTION, PLR_LOOK,
NOEVENT_KEY
-};
+};*/
+
+enum PLR_EVENT {
+ // action list
+ PLR_PROV_WALKTO = 0, // Provisional WALKTO !
+ PLR_WALKTO = 1,
+ PLR_LOOK = 2,
+ PLR_ACTION = 3,
+ PLR_ESCAPE = 4,
+ PLR_MENU = 5,
+ PLR_QUIT = 6,
+ PLR_PGUP = 7,
+ PLR_PGDN = 8,
+ PLR_HOME = 9,
+ PLR_END = 10,
+ PLR_DRAG1_START = 11,
+ PLR_DRAG1_END = 12,
+ PLR_DRAG2_START = 13,
+ PLR_DRAG2_END = 14,
+ PLR_JUMP = 15, // Call up scene hopper
+ PLR_NOEVENT = 16,
+ PLR_SAVE = 17,
+ PLR_LOAD = 18,
+
+ // Aliases used for DW1 actions
+ PLR_SLEFT = PLR_WALKTO,
+ PLR_DLEFT = PLR_ACTION,
+ PLR_SRIGHT = PLR_LOOK,
+ PLR_DRIGHT = PLR_NOEVENT,
+ PLR_UNKNOWN = PLR_NOEVENT
+} ;
+
/**
* Reasons for running Glitter code.
* Do not re-order these as equivalent CONSTs are defined in the master
* scene Glitter source file for testing against the event() library function.
+ *
+ * Note: DW2 renames ENTER & LEAVE to WALKIN & WALKOUT, and has a new LEAVE event
*/
-enum USER_EVENT {
- POINTED, WALKTO, ACTION, LOOK,
- ENTER, LEAVE, STARTUP, CONVERSE,
- UNPOINT, PUTDOWN,
- NOEVENT
+enum TINSEL_EVENT {
+ NOEVENT, STARTUP, CLOSEDOWN, POINTED, UNPOINT, WALKIN, WALKOUT,
+ PICKUP, PUTDOWN, WALKTO, LOOK, ACTION, CONVERSE, SHOWEVENT,
+ HIDEEVENT, TALKING, ENDTALK, LEAVE_T2, RESTORE, PROV_WALKTO
+};
+
+enum TINSEL1_EVENT {
+ T1_POINTED, T1_WALKTO, T1_ACTION, T1_LOOK, T1_ENTER, T1_LEAVE, T1_STARTUP, T1_CONVERSE,
+ T1_UNPOINT, T1_PUTDOWN, T1_NOEVENT
};
+const TINSEL1_EVENT TINSEL1_EVENT_MAP[] = {
+ T1_NOEVENT, T1_STARTUP, T1_NOEVENT, T1_POINTED, T1_UNPOINT, T1_ENTER, T1_LEAVE,
+ T1_NOEVENT, T1_PUTDOWN, T1_WALKTO, T1_LOOK, T1_ACTION, T1_CONVERSE, T1_NOEVENT,
+ T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT
+};
-void AllowDclick(CORO_PARAM, BUTEVENT be);
+void AllowDclick(CORO_PARAM, PLR_EVENT be);
bool GetControl(int param);
+bool GetControl(void);
+bool ControlIsOn(void);
+void ControlOn(void);
+void ControlOff(void);
+void ControlStartOff(void);
-void RunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, BUTEVENT be, bool tc);
-void effRunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, int actor);
+void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc);
+void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor);
-void ProcessButEvent(BUTEVENT be);
-void ProcessKeyEvent(KEYEVENT ke);
+void ProcessButEvent(PLR_EVENT be);
+void ProcessKeyEvent(PLR_EVENT ke);
int GetEscEvents(void);
int GetLeftEvents(void);
+bool LeftEventChange(int myleftEvent);
+
int getUserEvents(void);
uint32 getUserEventTime(void);
@@ -79,6 +129,16 @@ void resetUserEventTime(void);
void ResetEcount(void);
+void PolygonEvent(CORO_PARAM, HPOLYGON hPoly, TINSEL_EVENT tEvent, int actor, bool bWait,
+ int myEscape, bool *result = NULL);
+
+
+void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds);
+
+void ProcessedProvisional(void);
+void ProvNotProcessed(void);
+bool GetProvNotProcessed();
+
} // end of namespace Tinsel
#endif /* TINSEL_EVENTS_H */
diff --git a/engines/tinsel/faders.cpp b/engines/tinsel/faders.cpp
index 0018727ccb..0233dbcf4a 100644
--- a/engines/tinsel/faders.cpp
+++ b/engines/tinsel/faders.cpp
@@ -24,11 +24,14 @@
* Palette Fader and Flasher processes.
*/
-#include "tinsel/pid.h" // list of all process IDs
-#include "tinsel/sched.h" // scheduler defs
+#include "tinsel/actors.h"
#include "tinsel/faders.h" // fader defs
#include "tinsel/handle.h"
#include "tinsel/palette.h" // Palette Manager defs
+#include "tinsel/pid.h" // list of all process IDs
+#include "tinsel/sched.h" // scheduler defs
+#include "tinsel/sysvar.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -40,9 +43,7 @@ struct FADE {
// fixed point fade multiplier tables
//const long fadeout[] = {0xf000, 0xd000, 0xb000, 0x9000, 0x7000, 0x5000, 0x3000, 0x1000, 0, -1};
-const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
//const long fadein[] = {0, 0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xb000, 0xd000, 0x10000L, -1};
-const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
/**
* Scale 'colour' by the fixed point colour multiplier 'colourMult'
@@ -70,8 +71,18 @@ static COLORREF ScaleColour(COLORREF colour, uint32 colourMult) {
*/
static void FadePalette(COLORREF *pNew, COLORREF *pOrig, int numColours, uint32 mult) {
for (int i = 0; i < numColours; i++, pNew++, pOrig++) {
- // apply multiplier to RGB components
- *pNew = ScaleColour(*pOrig, mult);
+ if (!TinselV2)
+ // apply multiplier to RGB components
+ *pNew = ScaleColour(*pOrig, mult);
+ else if (i == (TalkColour() - 1)) {
+ *pNew = GetTalkColourRef();
+ *pNew = ScaleColour(*pNew, mult);
+ } else if (SysVar(SV_TAGCOLOUR) && i == (SysVar(SV_TAGCOLOUR) - 1)) {
+ *pNew = GetTagColorRef();
+ *pNew = ScaleColour(*pNew, mult);
+ } else {
+ *pNew = ScaleColour(*pOrig, mult);
+ }
}
}
@@ -89,10 +100,14 @@ static void FadeProcess(CORO_PARAM, const void *param) {
CORO_END_CONTEXT(_ctx);
// get the fade data structure - copied to process when it was created
- FADE *pFade = (FADE *)param;
+ 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(pFade->pPalQ->hPal);
@@ -100,8 +115,12 @@ static void FadeProcess(CORO_PARAM, const void *param) {
// go through all multipliers in table - until a negative entry
// fade palette using next multiplier
- FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB,
- FROM_LE_32(_ctx->pPalette->numColours), (uint32) *_ctx->pColMult);
+ if (TinselV2)
+ FadePalette(_ctx->fadeRGB, pFade->pPalQ->palRGB,
+ FROM_LE_32(pFade->pPalQ->numColours), (uint32) *_ctx->pColMult);
+ else
+ FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB,
+ FROM_LE_32(_ctx->pPalette->numColours), (uint32) *_ctx->pColMult);
// send new palette to video DAC
UpdateDACqueue(pFade->pPalQ->posInDAC, FROM_LE_32(_ctx->pPalette->numColours), _ctx->fadeRGB);
@@ -110,6 +129,10 @@ static void FadeProcess(CORO_PARAM, const void *param) {
CORO_SLEEP(1);
}
+ if (TinselV2)
+ // Note that this palette is being faded
+ FadingPalette(pFade->pPalQ, false);
+
CORO_END_CODE;
}
@@ -122,6 +145,13 @@ static void FadeProcess(CORO_PARAM, const void *param) {
static void Fader(const long multTable[], SCNHANDLE noFadeTable[]) {
PALQ *pPal; // palette manager iterator
+ if (TinselV2) {
+ // The is only ever one cuncurrent fade
+ // But this could be a fade out and the fade in is still going!
+ g_scheduler->killMatchingProcess(PID_FADER);
+ NoFadingPalettes();
+ }
+
// create a process for each palette in the palette queue
for (pPal = GetNextPalette(NULL); pPal != NULL; pPal = GetNextPalette(pPal)) {
bool bFade = true;
@@ -156,20 +186,59 @@ static void Fader(const long multTable[], SCNHANDLE noFadeTable[]) {
/**
* Fades a list of palettes down to black.
+ * 'noFadeTable' is a NULL terminated list of palettes not to fade.
+ */
+void FadeOutMedium(SCNHANDLE noFadeTable[]) {
+ // Fixed point fade multiplier table
+ static const long fadeout[] = {0xea00, 0xd000, 0xb600, 0x9c00,
+ 0x8200, 0x6800, 0x4e00, 0x3400, 0x1a00, 0, -1};
+
+ // call generic fader
+ Fader(fadeout, noFadeTable);
+}
+
+/**
+ * Fades a list of palettes down to black.
* @param noFadeTable A NULL terminated list of palettes not to fade.
*/
void FadeOutFast(SCNHANDLE noFadeTable[]) {
+ // Fixed point fade multiplier table
+ static const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
+
// call generic fader
Fader(fadeout, noFadeTable);
}
/**
* Fades a list of palettes from black to their current colours.
+ * 'noFadeTable' is a NULL terminated list of palettes not to fade.
+ */
+void FadeInMedium(SCNHANDLE noFadeTable[]) {
+ // Fade multiplier table
+ static const long fadein[] = {0, 0x1a00, 0x3400, 0x4e00, 0x6800,
+ 0x8200, 0x9c00, 0xb600, 0xd000, 0xea00, 0x10000L, -1};
+
+ // call generic fader
+ Fader(fadein, noFadeTable);
+}
+
+/**
+ * Fades a list of palettes from black to their current colours.
* @param noFadeTable A NULL terminated list of palettes not to fade.
*/
void FadeInFast(SCNHANDLE noFadeTable[]) {
+ // Fade multiplier table
+ static const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
+
// call generic fader
Fader(fadein, noFadeTable);
}
+void PokeInTagColour(void) {
+ if (SysVar(SV_TAGCOLOUR)) {
+ static COLORREF c = GetActorRGB(-1);
+ UpdateDACqueue(SysVar(SV_TAGCOLOUR), 1, &c);
+ }
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/faders.h b/engines/tinsel/faders.h
index 1e9336fae8..6e716c3604 100644
--- a/engines/tinsel/faders.h
+++ b/engines/tinsel/faders.h
@@ -28,16 +28,15 @@
#define TINSEL_FADERS_H
#include "tinsel/dw.h" // for SCNHANDLE
+#include "tinsel/tinsel.h"
namespace Tinsel {
-enum {
- /**
- * Number of iterations in a fade out.
- * Must match which FadeOut() is in use.
- */
- COUNTOUT_COUNT = 6
-};
+/**
+ * Number of iterations in a fade out.
+ */
+// FIXME: There seems to be some confusion in Tinsel 2 whether this should be 9 or 6
+#define COUNTOUT_COUNT 6
/*----------------------------------------------------------------------*\
|* Fader Function Prototypes *|
@@ -47,8 +46,11 @@ enum {
// should not be faded. This parameter can be
// NULL - fade all palettes.
+void FadeOutMedium(SCNHANDLE noFadeTable[]);
void FadeOutFast(SCNHANDLE noFadeTable[]);
+void FadeInMedium(SCNHANDLE noFadeTable[]);
void FadeInFast(SCNHANDLE noFadeTable[]);
+void PokeInTagColour(void);
} // end of namespace Tinsel
diff --git a/engines/tinsel/font.cpp b/engines/tinsel/font.cpp
index 620298867e..10427b6e3b 100644
--- a/engines/tinsel/font.cpp
+++ b/engines/tinsel/font.cpp
@@ -22,11 +22,14 @@
* $Id$
*/
+#include "tinsel/actors.h"
#include "tinsel/dw.h"
#include "tinsel/font.h"
#include "tinsel/handle.h"
#include "tinsel/object.h"
+#include "tinsel/sysvar.h"
#include "tinsel/text.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -35,48 +38,63 @@ namespace Tinsel {
static char tBuffer[TBUFSZ];
static SCNHANDLE hTagFont = 0, hTalkFont = 0;
+static SCNHANDLE hRegularTalkFont = 0, hRegularTagFont = 0;
/**
* Return address of tBuffer
*/
-char *tBufferAddr() {
+char *TextBufferAddr() {
return tBuffer;
}
/**
* Return hTagFont handle.
*/
-SCNHANDLE hTagFontHandle() {
+SCNHANDLE GetTagFontHandle() {
return hTagFont;
}
/**
* Return hTalkFont handle.
*/
-SCNHANDLE hTalkFontHandle() {
+SCNHANDLE GetTalkFontHandle() {
return hTalkFont;
}
/**
* Called from dec_tagfont() Glitter function. Store the tag font handle.
*/
-void TagFontHandle(SCNHANDLE hf) {
- hTagFont = hf; // Store the font handle
+void SetTagFontHandle(SCNHANDLE hFont) {
+ hTagFont = hRegularTagFont = hFont; // Store the font handle
}
/**
* Called from dec_talkfont() Glitter function.
* Store the talk font handle.
*/
-void TalkFontHandle(SCNHANDLE hf) {
- hTalkFont = hf; // Store the font handle
+void SetTalkFontHandle(SCNHANDLE hFont) {
+ hTalkFont = hRegularTalkFont = hFont; // Store the font handle
}
+void SetTempTagFontHandle(SCNHANDLE hFont) {
+ hTagFont = hFont;
+}
+
+void SetTempTalkFontHandle(SCNHANDLE hFont) {
+ hTalkFont = hFont;
+}
+
+void ResetFontHandles(void) {
+ hTagFont = hRegularTagFont;
+ hTalkFont = hRegularTalkFont;
+}
+
+
/**
* Poke the background palette into character 0's images.
*/
-void fettleFontPal(SCNHANDLE fontPal) {
+void FettleFontPal(SCNHANDLE fontPal) {
const FONT *pFont;
IMAGE *pImg;
@@ -86,11 +104,23 @@ void fettleFontPal(SCNHANDLE fontPal) {
pFont = (const FONT *)LockMem(hTagFont);
pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0
- pImg->hImgPal = TO_LE_32(fontPal);
+ if (!TinselV2)
+ pImg->hImgPal = TO_LE_32(fontPal);
+ else
+ pImg->hImgPal = 0;
pFont = (const FONT *)LockMem(hTalkFont);
pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0
- pImg->hImgPal = TO_LE_32(fontPal);
+ if (!TinselV2)
+ pImg->hImgPal = TO_LE_32(fontPal);
+ else
+ pImg->hImgPal = 0;
+
+ if (TinselV2 && SysVar(SV_TAGCOLOUR)) {
+ static COLORREF c = GetActorRGB(-1);
+ SetTagColorRef(c);
+ UpdateDACqueue(SysVar(SV_TAGCOLOUR), 1, &c);
+ }
}
} // End of namespace Tinsel
diff --git a/engines/tinsel/font.h b/engines/tinsel/font.h
index b75c36191c..fe2936eb80 100644
--- a/engines/tinsel/font.h
+++ b/engines/tinsel/font.h
@@ -31,17 +31,27 @@
namespace Tinsel {
// A temporary buffer for extracting text into is defined in font.c
-// Accessed using tBufferAddr(), this is how big it is:
+// Accessed using TextBufferAddr(), this is how big it is:
#define TBUFSZ 512
-char *tBufferAddr(void);
-SCNHANDLE hTagFontHandle(void);
-SCNHANDLE hTalkFontHandle(void);
+char *TextBufferAddr(void);
-void TagFontHandle(SCNHANDLE hf);
-void TalkFontHandle(SCNHANDLE hf);
-void fettleFontPal(SCNHANDLE fontPal);
+SCNHANDLE GetTagFontHandle(void);
+
+SCNHANDLE GetTalkFontHandle(void);
+
+void SetTagFontHandle(SCNHANDLE hFont);
+
+void SetTalkFontHandle(SCNHANDLE hFont);
+
+void SetTempTagFontHandle(SCNHANDLE hFont);
+
+void SetTempTalkFontHandle(SCNHANDLE hFont);
+
+void ResetFontHandles(void);
+
+void FettleFontPal(SCNHANDLE fontPal);
} // end of namespace Tinsel
diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp
index cd0937d944..d1db50bde0 100644
--- a/engines/tinsel/graphics.cpp
+++ b/engines/tinsel/graphics.cpp
@@ -28,6 +28,7 @@
#include "tinsel/handle.h" // LockMem()
#include "tinsel/object.h"
#include "tinsel/palette.h"
+#include "tinsel/scene.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -46,7 +47,6 @@ extern uint8 transPalette[MAX_COLOURS];
/**
* Straight rendering with transparency support
*/
-
static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
// Set up the offset between destination blocks
int rightClip = applyClipping ? pObj->rightClip : 0;
@@ -156,9 +156,90 @@ static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyCl
}
/**
- * Fill the destination area with a constant colour
+ * Tinsel 2 Straight rendering with transparency support
*/
+static void t2WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping, bool horizFlipped) {
+ // Setup for correct clipping of object edges
+ int yClip = applyClipping ? pObj->topClip : 0;
+ if (applyClipping)
+ pObj->height -= pObj->botClip;
+ int numBytes;
+ int clipAmount;
+
+ for (int y = 0; y < pObj->height; ++y) {
+ // Get the position to start writing out from
+ uint8 *tempP = !horizFlipped ? destP :
+ destP + (pObj->width - pObj->leftClip - pObj->rightClip) - 1;
+ int leftClip = applyClipping ? pObj->leftClip : 0;
+ int rightClip = applyClipping ? pObj->rightClip : 0;
+ if (horizFlipped)
+ SWAP(leftClip, rightClip);
+
+ int x = 0;
+ while (x < pObj->width) {
+ // Get the next opcode
+ numBytes = *srcP++;
+ if (numBytes & 0x80) {
+ // Run length following
+ numBytes &= 0x7f;
+ clipAmount = MIN(numBytes, leftClip);
+ leftClip -= clipAmount;
+ x+= clipAmount;
+
+ int runLength = numBytes - clipAmount;
+ uint8 colour = *srcP++;
+
+ if ((yClip == 0) && (runLength > 0) && (colour != 0)) {
+ runLength = MIN(runLength, pObj->width - rightClip - x);
+
+ if (runLength > 0) {
+ // Non-transparent run length
+ colour += pObj->constant;
+ if (horizFlipped)
+ Common::set_to(tempP - runLength + 1, tempP + 1, colour);
+ else
+ Common::set_to(tempP, tempP + runLength, colour);
+ }
+ }
+
+ if (horizFlipped)
+ tempP -= runLength;
+ else
+ tempP += runLength;
+
+ x += numBytes - clipAmount;
+
+ } else {
+ // Dump a length of pixels
+ clipAmount = MIN(numBytes, leftClip);
+ leftClip -= clipAmount;
+ srcP += clipAmount;
+ int runLength = numBytes - clipAmount;
+ x += numBytes - runLength;
+
+ for (int xp = 0; xp < runLength; ++xp) {
+ if ((yClip > 0) || (x >= (pObj->width - rightClip)))
+ ++srcP;
+ else if (horizFlipped)
+ *tempP-- = pObj->constant + *srcP++;
+ else
+ *tempP++ = pObj->constant + *srcP++;
+ ++x;
+ }
+ }
+ }
+ assert(x == pObj->width);
+ if (yClip > 0)
+ --yClip;
+ else
+ destP += SCREEN_WIDTH;
+ }
+}
+
+/**
+ * Fill the destination area with a constant colour
+ */
static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
if (applyClipping) {
pObj->height -= pObj->topClip + pObj->botClip;
@@ -181,7 +262,6 @@ static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
* Translates the destination surface within the object's bounds using the transparency
* lookup table from transpal.cpp (the contents of which have been moved into palette.cpp)
*/
-
static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
if (applyClipping) {
pObj->height -= pObj->topClip + pObj->botClip;
@@ -204,165 +284,135 @@ static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
}
}
-
-#if 0
-// This commented out code is the untested original WrtNonZero/ClpWrtNonZero combo method
-// from the v1 source. It may be needed to be included later on to support v1 gfx files
-
/**
- * Straight rendering with transparency support
- * Possibly only used in the Discworld Demo
+ * Copies an uncompressed block of data straight to the screen
*/
+static void WrtAll(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
+ int objWidth = pObj->width;
-static void DemoWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
- // FIXME: If this method is used for the demo, it still needs to be made Endian safe
+ if (applyClipping) {
+ srcP += (pObj->topClip * pObj->width) + pObj->leftClip;
- // Set up the offset between destination lines
- pObj->lineoffset = SCREEN_WIDTH - pObj->width - (applyClipping ? pObj->leftClip - pObj->rightClip : 0);
+ pObj->height -= pObj->topClip + pObj->botClip;
+ pObj->width -= pObj->leftClip + pObj->rightClip;
- // Top clipped line handling
- while (applyClipping && (pObj->topClip > 0)) {
- // Loop through discarding the data for the line
- int width = pObj->width;
- while (width > 0) {
- int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
- srcP += sizeof(uint32);
-
- if (opcodeOrLen >= 0) {
- // Dump the data
- srcP += ((opcodeOrLen + 3) / 4) * 4;
- width -= opcodeOrLen;
- } else {
- // Dump the run-length opcode
- width -= -opcodeOrLen;
- }
- }
+ if (pObj->width <= 0)
+ return;
+ }
- --pObj->height;
- --pObj->topClip;
+ for (int y = 0; y < pObj->height; ++y) {
+ Common::copy(srcP, srcP + pObj->width, destP);
+ srcP += objWidth;
+ destP += SCREEN_WIDTH;
}
+}
- // Loop for the required number of rows
- while (pObj->height > 0) {
+/**
+ * Renders a packed data stream with a variable sized palette
+ */
+static void PackedWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP,
+ bool applyClipping, bool horizFlipped, int packingType) {
+ uint8 numColours = 0;
+ uint8 *colourTable = NULL;
+ int topClip = 0;
+ int xOffset = 0;
+ int numBytes, colour;
+ int v;
- int width = pObj->width;
-
- // Handling for left edge clipping - this basically involves dumping data until we reach
- // the part of the line to be displayed
- int clipLeft = pObj->leftClip;
- while (applyClipping && (clipLeft > 0)) {
- int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
- srcP += sizeof(uint32);
-
- if (opcodeOrLen >= 0) {
- // Copy a specified number of bytes
- // Make adjustments for past the clipping width
- int remainder = 4 - (opcodeOrLen % 4);
- srcP += MIN(clipLeft, opcodeOrLen);
- opcodeOrLen -= MIN(clipLeft, opcodeOrLen);
- clipLeft -= MIN(clipLeft, opcodeOrLen);
- width -= opcodeOrLen;
-
-
- // Handle any right edge clipping (if run length covers entire width)
- if (width < pObj->rightClip) {
- remainder += (pObj->rightClip - width);
- opcodeOrLen -= (pObj->rightClip - width);
+ if (applyClipping) {
+ pObj->height -= pObj->botClip;
+ topClip = pObj->topClip;
+ }
+
+ if (packingType == 3) {
+ // Variable colours
+ numColours = *srcP++;
+ colourTable = srcP;
+ srcP += numColours;
+ }
+
+ for (int y = 0; y < pObj->height; ++y) {
+ // Get the position to start writing out from
+ uint8 *tempP = !horizFlipped ? destP :
+ destP + (pObj->width - pObj->leftClip - pObj->rightClip) - 1;
+ int leftClip = applyClipping ? pObj->leftClip : 0;
+ int rightClip = applyClipping ? pObj->rightClip : 0;
+ if (horizFlipped)
+ SWAP(leftClip, rightClip);
+ bool eolFlag = false;
+
+ // Get offset for first pixels in next line
+ xOffset = *srcP++;
+
+ int x = 0;
+ while (x < pObj->width) {
+ // Get next run size and colour to use
+ for (;;) {
+ if (xOffset > 0) {
+ x += xOffset;
+
+ // Reduce offset amount by any remaining left clipping
+ v = MIN(xOffset, leftClip);
+ xOffset -= v;
+ leftClip -= v;
+
+ if (horizFlipped) tempP -= xOffset; else tempP += xOffset;
+ xOffset = 0;
}
-
- if (opcodeOrLen > 0)
- Common::copy(srcP, srcP + opcodeOrLen, destP);
- } else {
- // Output a run length number of bytes
- // Get data for byte value and run length
- opcodeOrLen = -opcodeOrLen;
- int runLength = opcodeOrLen & 0xff;
- uint8 colourVal = (opcodeOrLen >> 8) & 0xff;
-
- // Make adjustments for past the clipping width
- runLength -= MIN(clipLeft, runLength);
- clipLeft -= MIN(clipLeft, runLength);
- width -= runLength;
-
- // Handle any right edge clipping (if run length covers entire width)
- if (width < pObj->rightClip)
- runLength -= (pObj->rightClip - width);
-
- if (runLength > 0) {
- // Displayable part starts partway through the slice
- if (colourVal != 0)
- Common::set_to(destP, destP + runLength, colourVal);
- destP += runLength;
+ v = *srcP++;
+ numBytes = v & 0xf; // No. bytes 1-15
+ if (packingType == 3)
+ colour = colourTable[v >> 4];
+ else
+ colour = pObj->baseCol + (v >> 4);
+
+ if (numBytes != 0)
+ break;
+
+ numBytes = *srcP++;
+ if (numBytes >= 16)
+ break;
+
+ xOffset = numBytes + v;
+ if (xOffset == 0) {
+ // End of line encountered
+ eolFlag = true;
+ break;
}
}
- if (width < pObj->rightClip)
- width = 0;
- }
-
- // Handling for the visible part of the line
- int endWidth = applyClipping ? pObj->rightClip : 0;
- while (width > endWidth) {
- int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
- srcP += sizeof(uint32);
+ if (eolFlag)
+ break;
- if (opcodeOrLen >= 0) {
- // Copy the specified number of bytes
- int remainder = 4 - (opcodeOrLen % 4);
+ // Apply clipping on byte sequence
+ v = MIN(numBytes, leftClip);
+ leftClip -= v;
+ numBytes -= v;
+ x += v;
- if (width < endWidth) {
- // Shorten run length by right clipping
- remainder += (pObj->rightClip - width);
- opcodeOrLen -= (pObj->rightClip - width);
+ while (numBytes-- > 0) {
+ if ((topClip == 0) && (x < (pObj->width - rightClip))) {
+ *tempP = colour;
+ if (horizFlipped) --tempP; else ++tempP;
}
-
- Common::copy(srcP, srcP + opcodeOrLen, destP);
- srcP += opcodeOrLen + remainder;
- destP += opcodeOrLen;
- width -= opcodeOrLen;
-
- } else {
- // Handle a given run length
- opcodeOrLen = -opcodeOrLen;
- int runLength = opcodeOrLen & 0xff;
- uint8 colourVal = (opcodeOrLen >> 8) & 0xff;
-
- if (width < endWidth)
- // Shorten run length by right clipping
- runLength -= (pObj->rightClip - width);
-
- // Only set pixels if colourVal non-zero (0 signifies transparency)
- if (colourVal != 0)
- // Fill out a run length of a specified colour
- Common::set_to(destP, destP + runLength, colourVal);
-
- destP += runLength;
- width -= runLength;
+ ++x;
}
}
+ assert(x <= pObj->width);
- // If right edge clipping is being applied, then width may still be non-zero - in
- // that case all remaining line data until the end of the line must be ignored
- while (width > 0) {
- int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
- srcP += sizeof(uint32);
-
- if (opcodeOrLen >= 0) {
- // Dump the data
- srcP += ((opcodeOrLen + 3) / 4) * 4;
- width -= opcodeOrLen;
- } else {
- // Dump the run-length opcode
- width -= -opcodeOrLen;
- }
+ if (!eolFlag) {
+ // Assert that the next bytes signal a line end
+ assert((*srcP++ & 0xf) == 0);
+ assert(*srcP++ == 0);
}
- --pObj->height;
- destP += pObj->lineoffset;
+ if (topClip > 0)
+ --topClip;
+ else
+ destP += SCREEN_WIDTH;
}
}
-#endif
//----------------- MAIN FUNCTIONS ---------------------
@@ -380,8 +430,10 @@ void ClearScreen() {
* Updates the screen surface within the following rectangle
*/
void UpdateScreenRect(const Common::Rect &pClip) {
- byte *pDest = (byte *)_vm->screen().getBasePtr(pClip.left, pClip.top);
- g_system->copyRectToScreen(pDest, _vm->screen().pitch, pClip.left, pClip.top, pClip.width(), pClip.height());
+ int yOffset = (g_system->getHeight() - SCREEN_HEIGHT) / 2;
+ byte *pSrc = (byte *)_vm->screen().getBasePtr(pClip.left, pClip.top);
+ g_system->copyRectToScreen(pSrc, _vm->screen().pitch, pClip.left, pClip.top + yOffset,
+ pClip.width(), pClip.height());
g_system->updateScreen();
}
@@ -398,11 +450,17 @@ void DrawObject(DRAWOBJECT *pObj) {
// If writing constant data, don't bother locking the data pointer and reading src details
if ((pObj->flags & DMA_CONST) == 0) {
- byte *p = (byte *)LockMem(pObj->hBits & 0xFF800000);
-
- srcPtr = p + (pObj->hBits & 0x7FFFFF);
- pObj->charBase = (char *)p + READ_LE_UINT32(p + 0x10);
- pObj->transOffset = READ_LE_UINT32(p + 0x14);
+ if (TinselV2) {
+ srcPtr = (byte *)LockMem(pObj->hBits);
+ pObj->charBase = NULL;
+ pObj->transOffset = 0;
+ } else {
+ byte *p = (byte *)LockMem(pObj->hBits & HANDLEMASK);
+
+ srcPtr = p + (pObj->hBits & OFFSETMASK);
+ pObj->charBase = (char *)p + READ_LE_UINT32(p + 0x10);
+ pObj->transOffset = READ_LE_UINT32(p + 0x14);
+ }
}
// Get destination starting point
@@ -410,30 +468,83 @@ void DrawObject(DRAWOBJECT *pObj) {
// Handle various draw types
uint8 typeId = pObj->flags & 0xff;
- switch (typeId) {
- case 0x01:
- case 0x08:
- case 0x41:
- case 0x48:
- WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40);
- break;
-
- case 0x04:
- case 0x44:
- // ClpWrtConst with/without clipping
- WrtConst(pObj,destPtr, typeId == 0x44);
- break;
-
- case 0x84:
- case 0xC4:
- // WrtTrans with/without clipping
- WrtTrans(pObj, destPtr, typeId == 0xC4);
- break;
-
- default:
- // NoOp
- error("Unknown drawing type %d", typeId);
- break;
+
+ if (TinselV2) {
+ // Tinsel v2 decoders
+ // Initial switch statement for the different bit packing types
+ int packType = pObj->flags >> 14;
+
+ if (packType == 0) {
+ // No colour packing
+ switch (typeId) {
+ case 0x01:
+ case 0x11:
+ case 0x41:
+ case 0x51:
+ case 0x81:
+ case 0xC1:
+ t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, (typeId & 0x10) != 0);
+ break;
+ case 0x02:
+ case 0x42:
+ // This renderer called 'RlWrtAll', but is the same as t2WrtNonZero
+ t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, false);
+ break;
+ case 0x04:
+ case 0x44:
+ // WrtConst with/without clipping
+ WrtConst(pObj, destPtr, typeId == 0x44);
+ break;
+ case 0x08:
+ case 0x48:
+ WrtAll(pObj, srcPtr, destPtr, typeId >= 0x40);
+ break;
+ case 0x84:
+ case 0xC4:
+ // WrtTrans with/without clipping
+ WrtTrans(pObj, destPtr, typeId == 0xC4);
+ break;
+ default:
+ error("Unknown drawing type %d", typeId);
+ }
+ } else {
+ // 1 = 16 from 240
+ // 2 = 16 from 224
+ // 3 = variable colour
+ if (packType == 1) pObj->baseCol = 0xF0;
+ else if (packType == 2) pObj->baseCol = 0xE0;
+
+ PackedWrtNonZero(pObj, srcPtr, destPtr, (pObj->flags & DMA_CLIP) != 0,
+ (pObj->flags & DMA_FLIPH), packType);
+ }
+
+ } else {
+ // Tinsel v1 decoders
+ switch (typeId) {
+ case 0x01:
+ case 0x08:
+ case 0x41:
+ case 0x48:
+ WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40);
+ break;
+
+ case 0x04:
+ case 0x44:
+ // WrtConst with/without clipping
+ WrtConst(pObj,destPtr, typeId == 0x44);
+ break;
+
+ case 0x84:
+ case 0xC4:
+ // WrtTrans with/without clipping
+ WrtTrans(pObj, destPtr, typeId == 0xC4);
+ break;
+
+ default:
+ // NoOp
+ error("Unknown drawing type %d", typeId);
+ break;
+ }
}
}
diff --git a/engines/tinsel/graphics.h b/engines/tinsel/graphics.h
index 85299d4873..9a05dbe785 100644
--- a/engines/tinsel/graphics.h
+++ b/engines/tinsel/graphics.h
@@ -37,12 +37,6 @@ namespace Tinsel {
struct PALQ;
-
-#define SCREEN_WIDTH 320 // PC screen dimensions
-#define SCREEN_HEIGHT 200
-#define SCRN_CENTRE_X ((SCREEN_WIDTH - 1) / 2) // screen centre x
-#define SCRN_CENTRE_Y ((SCREEN_HEIGHT - 1) / 2) // screen centre y
-
/** draw object structure - only used when drawing objects */
struct DRAWOBJECT {
char *charBase; // character set base address
@@ -58,8 +52,9 @@ struct DRAWOBJECT {
int rightClip; // amount to clip off object right
int topClip; // amount to clip off object top
int botClip; // amount to clip off object bottom
- short xPos; // x position of object
- short yPos; // y position of object
+ short xPos; // x position of object
+ short yPos; // y position of object
+ uint32 baseCol; // For 4-bit stuff
};
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index 11623516ec..41774a9c88 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -28,6 +28,7 @@
#include "common/file.h"
+#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/scn.h" // name of "index" file
#include "tinsel/handle.h"
@@ -41,6 +42,8 @@
#include "tinsel/object.h"
#include "tinsel/palette.h"
#include "tinsel/text.h"
+#include "tinsel/timers.h"
+#include "tinsel/tinsel.h"
#include "tinsel/scene.h"
namespace Tinsel {
@@ -58,6 +61,7 @@ struct MEMHANDLE {
char szName[12]; //!< 00 - file name of graphics file
int32 filesize; //!< 12 - file size and flags
MEM_NODE *pNode; //!< 16 - memory node for the graphics
+ uint32 flags2;
};
@@ -72,7 +76,6 @@ enum {
};
#define FSIZE_MASK 0x00FFFFFFL //!< mask to isolate the filesize
#define MALLOC_MASK 0xFF000000L //!< mask to isolate the memory allocation flags
-#define OFFSETMASK 0x007fffffL //!< get offset of address
//#define HANDLEMASK 0xFF800000L //!< get handle of address
//----------------- LOCAL GLOBAL DATA --------------------
@@ -83,6 +86,13 @@ static MEMHANDLE *handleTable = 0;
// number of handles in the handle table
static uint numHandles = 0;
+static uint32 cdPlayHandle = (uint32)-1;
+
+static int cdPlayFileNum, cdPlaySceneNum;
+static SCNHANDLE cdBaseHandle = 0, cdTopHandle = 0;
+static Common::File cdGraphStream;
+
+static char szCdPlayFile[100];
//----------------- FORWARD REFERENCES --------------------
@@ -94,7 +104,8 @@ static void LoadFile(MEMHANDLE *pH, bool bWarn); // load a memory block as a fil
* permanent graphics etc.
*/
void SetupHandleTable(void) {
- enum { RECORD_SIZE = 20 };
+ bool t2Flag = (TinselVersion == TINSEL_V2);
+ int RECORD_SIZE = t2Flag ? 24 : 20;
int len;
uint i;
@@ -108,7 +119,7 @@ void SetupHandleTable(void) {
if (len > 0) {
if ((len % RECORD_SIZE) != 0) {
// index file is corrupt
- error("File %s is corrupt", INDEX_FILENAME);
+ error(FILE_IS_CORRUPT, INDEX_FILENAME);
}
// calc number of handles
@@ -128,31 +139,36 @@ void SetupHandleTable(void) {
// need to read that from the file.
handleTable[i].pNode = NULL;
f.seek(4, SEEK_CUR);
+ // For Discworld 2, read in the flags2 field
+ handleTable[i].flags2 = t2Flag ? f.readUint32LE() : 0;
}
if (f.ioFailed()) {
// index file is corrupt
- error("File %s is corrupt", INDEX_FILENAME);
+ error(FILE_IS_CORRUPT, INDEX_FILENAME);
}
// close the file
f.close();
} else { // index file is corrupt
- error("File %s is corrupt", INDEX_FILENAME);
+ error(FILE_IS_CORRUPT, INDEX_FILENAME);
}
} else { // cannot find the index file
- error("Cannot find file %s", INDEX_FILENAME);
+ error(CANNOT_FIND_FILE, INDEX_FILENAME);
}
// allocate memory nodes and load all permanent graphics
for (i = 0, pH = handleTable; i < numHandles; i++, pH++) {
if (pH->filesize & fPreload) {
// allocate a fixed memory node for permanent files
- pH->pNode = MemoryAlloc(DWM_FIXED, pH->filesize & FSIZE_MASK);
+ pH->pNode = MemoryAlloc(DWM_FIXED, sizeof(MEM_NODE) + (pH->filesize & FSIZE_MASK));
// make sure memory allocated
assert(pH->pNode);
+ // Initialise the MEM_NODE structure
+ memset(pH->pNode, 0, sizeof(MEM_NODE));
+
// load the data
LoadFile(pH, true);
}
@@ -178,9 +194,102 @@ void FreeHandleTable(void) {
free(handleTable);
handleTable = NULL;
}
+ if (cdGraphStream.isOpen())
+ cdGraphStream.close();
+}
+
+/**
+ * Loads a memory block as a file.
+ */
+void OpenCDGraphFile(void) {
+ if (cdGraphStream.isOpen())
+ cdGraphStream.close();
+
+ // As the theory goes, the right CD will be in there!
+
+ cdGraphStream.clearIOFailed();
+ cdGraphStream.open(szCdPlayFile);
+ if (cdGraphStream.ioFailed())
+ error(CANNOT_FIND_FILE, szCdPlayFile);
+}
+
+void LoadCDGraphData(MEMHANDLE *pH) {
+ // read the data
+ uint bytes;
+ byte *addr;
+ int retries = 0;
+
+ assert(!(pH->filesize & fCompressed));
+
+ // Can't be preloaded
+ assert(!(pH->filesize & fPreload));
+
+ // discardable - lock the memory
+ addr = (byte *)MemoryLock(pH->pNode);
+
+ // make sure address is valid
+ assert(addr);
+
+ // Move to correct place in file and load the required data
+ cdGraphStream.seek(cdBaseHandle & OFFSETMASK, SEEK_SET);
+ bytes = cdGraphStream.read(addr, (cdTopHandle - cdBaseHandle) & OFFSETMASK);
+
+ // New code to try and handle CD read failures 24/2/97
+ while (bytes != ((cdTopHandle - cdBaseHandle) & OFFSETMASK) && retries++ < MAX_READ_RETRIES) {
+ // Try again
+ cdGraphStream.seek(cdBaseHandle & OFFSETMASK, SEEK_SET);
+ bytes = cdGraphStream.read(addr, (cdTopHandle - cdBaseHandle) & OFFSETMASK);
+ }
+
+ // discardable - unlock the memory
+ MemoryUnlock(pH->pNode);
+
+ // set the loaded flag
+ pH->filesize |= fLoaded;
+
+ // clear the loading flag
+// pH->filesize &= ~fLoading;
+
+ if (bytes != ((cdTopHandle-cdBaseHandle) & OFFSETMASK))
+ // file is corrupt
+ error(FILE_READ_ERROR, "CD play file");
}
/**
+ * Called immediatly preceding a CDplay().
+ * Prepares the ground so that when LockMem() is called, the
+ * appropriate section of the extra scene file is loaded.
+ * @param start Handle of start of range
+ * @param next Handle of end of range + 1
+ */
+void LoadExtraGraphData(SCNHANDLE start, SCNHANDLE next) {
+ if (cdPlayFileNum == cdPlaySceneNum && start == cdBaseHandle)
+ return;
+
+ OpenCDGraphFile();
+
+ if ((handleTable + cdPlayHandle)->pNode->pBaseAddr != NULL)
+ MemoryDiscard((handleTable + cdPlayHandle)->pNode); // Free it
+
+ // It must always be the same
+ assert(cdPlayHandle == (start >> SCNHANDLE_SHIFT));
+ assert(cdPlayHandle == (next >> SCNHANDLE_SHIFT));
+
+ cdBaseHandle = start;
+ cdTopHandle = next;
+}
+
+void SetCdPlaySceneDetails(int fileNum, const char *fileName) {
+ cdPlaySceneNum = fileNum;
+ strcpy(szCdPlayFile, fileName);
+}
+
+void SetCdPlayHandle(int fileNum) {
+ cdPlayHandle = fileNum;
+}
+
+
+/**
* Loads a memory block as a file.
* @param pH Memory block pointer
* @param bWarn If set, treat warnings as errors
@@ -204,7 +313,7 @@ void LoadFile(MEMHANDLE *pH, bool bWarn) {
if (pH->filesize & fPreload)
// preload - no need to lock the memory
- addr = (uint8 *)pH->pNode;
+ addr = (uint8 *)pH->pNode + sizeof(MEM_NODE);
else {
// discardable - lock the memory
addr = (uint8 *)MemoryLock(pH->pNode);
@@ -243,12 +352,12 @@ void LoadFile(MEMHANDLE *pH, bool bWarn) {
if (bWarn)
// file is corrupt
- error("File %s is corrupt", szFilename);
+ error(FILE_IS_CORRUPT, szFilename);
}
if (bWarn)
// cannot find file
- error("Cannot find file %s", szFilename);
+ error(CANNOT_FIND_FILE, szFilename);
}
/**
@@ -265,8 +374,39 @@ uint8 *LockMem(SCNHANDLE offset) {
pH = handleTable + handle;
if (pH->filesize & fPreload) {
+ if (TinselV2)
+ // update the LRU time (new in this file)
+ pH->pNode->lruTime = DwGetCurrentTime();
+
// permanent files are already loaded
- return (uint8 *)pH->pNode + (offset & OFFSETMASK);
+ return (uint8 *)pH->pNode + sizeof(MEM_NODE) + (offset & OFFSETMASK);
+ } else if (handle == cdPlayHandle) {
+ // Must be in currently loaded/loadable range
+ if(offset < cdBaseHandle || offset >= cdTopHandle)
+ error("Overlapping (in time) CD-plays!");
+
+ if (pH->pNode->pBaseAddr && (pH->filesize & fLoaded))
+ // already allocated and loaded
+ return pH->pNode->pBaseAddr + ((offset - cdBaseHandle) & OFFSETMASK);
+
+ if (pH->pNode->pBaseAddr == NULL)
+ // must have been discarded - reallocate the memory
+ MemoryReAlloc(pH->pNode, cdTopHandle-cdBaseHandle,
+ DWM_MOVEABLE | DWM_DISCARDABLE);
+
+ if(pH->pNode->pBaseAddr == NULL)
+ error("Out of memory");
+
+ LoadCDGraphData(pH);
+
+ // make sure address is valid
+ assert(pH->pNode->pBaseAddr);
+
+ // update the LRU time (new in this file)
+ pH->pNode->lruTime = DwGetCurrentTime();
+
+ return pH->pNode->pBaseAddr + ((offset - cdBaseHandle) & OFFSETMASK);
+
} else {
if (pH->pNode->pBaseAddr && (pH->filesize & fLoaded))
// already allocated and loaded
@@ -312,7 +452,10 @@ void LockScene(SCNHANDLE offset) {
if ((pH->filesize & fPreload) == 0) {
// change the flags for the node
- MemoryReAlloc(pH->pNode, pH->filesize & FSIZE_MASK, DWM_MOVEABLE);
+ // WORKAROUND: The original didn't include the DWM_LOCKED flag. It's being
+ // included because the method is 'LockScene' so it's presumed that the
+ // point of this was that the scene's memory block be locked
+ MemoryReAlloc(pH->pNode, pH->filesize & FSIZE_MASK, DWM_MOVEABLE | DWM_LOCKED);
#ifdef DEBUG
bLockedScene = true;
#endif
@@ -363,4 +506,50 @@ bool ValidHandle(SCNHANDLE offset) {
}
#endif
+/**
+ * TouchMem
+ * @param offset Handle and offset to data
+ */
+void TouchMem(SCNHANDLE offset) {
+ MEMHANDLE *pH; // points to table entry
+ uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
+
+ if (offset != 0) {
+ pH = handleTable + handle;
+
+ // update the LRU time whether its loaded or not!
+ pH->pNode->lruTime = DwGetCurrentTime();
+ }
+}
+
+/**
+ * Returns true if the given handle is into the cd graph data
+ * @param offset Handle and offset to data
+ */
+bool IsCdPlayHandle(SCNHANDLE offset) {
+ uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
+
+ // range check the memory handle
+ assert(handle < numHandles);
+
+ return (handle == cdPlayHandle);
+}
+
+/**
+ * Returns the CD for a given scene handle
+ */
+int CdNumber(SCNHANDLE offset) {
+ uint handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
+
+ // range check the memory handle
+ assert(handle < numHandles);
+
+ MEMHANDLE *pH = handleTable + handle;
+
+ if (!TinselV2)
+ return 1;
+
+ return GetCD(pH->flags2 & fAllCds);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/handle.h b/engines/tinsel/handle.h
index 2cb1638d9d..3f29ca712a 100644
--- a/engines/tinsel/handle.h
+++ b/engines/tinsel/handle.h
@@ -48,6 +48,23 @@ void LockScene( // Called to make the current scene non-discardable
void UnlockScene( // Called to make the current scene discardable again
SCNHANDLE offset); // handle and offset to data
+bool IsCdPlayHandle(SCNHANDLE offset);
+
+void TouchMem(SCNHANDLE offset);
+
+void SetCdPlaySceneDetails( // Called at scene startup
+ int sceneNum,
+ const char *fileName);
+
+void SetCdPlayHandle( // Called at game startup
+ int fileNum);
+
+void LoadExtraGraphData(
+ SCNHANDLE start, // Handle of start of range
+ SCNHANDLE next); // Handle of end of range + 1
+
+int CdNumber(SCNHANDLE offset);
+
} // end of namespace Tinsel
#endif // TINSEL_HANDLE_H
diff --git a/engines/tinsel/heapmem.cpp b/engines/tinsel/heapmem.cpp
index aff085d003..78c020eb74 100644
--- a/engines/tinsel/heapmem.cpp
+++ b/engines/tinsel/heapmem.cpp
@@ -26,11 +26,13 @@
#include "tinsel/heapmem.h"
#include "tinsel/timers.h" // For DwGetCurrentTime
+#include "tinsel/tinsel.h"
namespace Tinsel {
-// minimum memory required for MS-DOS version of game
-#define MIN_MEM 2506752L
+// Specifies the total amount of memory required for DW1 demo, DW1, or DW2 respectively.
+// Currently this is set at 10Mb for all three - this could probably be reduced somewhat
+uint32 MemoryPoolSize[3] = {10 * 1024 * 1024, 10 * 1024 * 1024, 10 * 1024 * 1024};
// list of all memory nodes
MEM_NODE mnodeList[NUM_MNODES];
@@ -73,8 +75,10 @@ void MemoryInit(void) {
// null the last mnode
mnodeList[NUM_MNODES - 1].pNext = NULL;
- // allocatea big chunk of memory
- const uint32 size = 2*MIN_MEM+655360L;
+ // allocates a big chunk of memory
+ uint32 size = MemoryPoolSize[0];
+ if (TinselVersion == TINSEL_V1) size = MemoryPoolSize[1];
+ else if (TinselVersion == TINSEL_V2) size = MemoryPoolSize[2];
uint8 *mem = (uint8 *)malloc(size);
assert(mem);
@@ -274,8 +278,9 @@ MEM_NODE *MemoryAlloc(int flags, long size) {
bool bCompacted = true; // set when heap has been compacted
// compact the heap if we are allocating fixed memory
- if (flags & DWM_FIXED)
+ if (flags & DWM_FIXED) {
HeapCompact(MAX_INT, false);
+ }
while ((flags & DWM_NOALLOC) == 0 && bCompacted) {
// search the heap for a free block
diff --git a/engines/tinsel/mareels.cpp b/engines/tinsel/mareels.cpp
index 4c64eaf091..8c09787690 100644
--- a/engines/tinsel/mareels.cpp
+++ b/engines/tinsel/mareels.cpp
@@ -24,6 +24,7 @@
* Functions to set up moving actors' reels.
*/
+#include "tinsel/handle.h"
#include "tinsel/pcode.h" // For D_UP, D_DOWN
#include "tinsel/rince.h"
@@ -34,7 +35,7 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
enum {
- NUM_INTERVALS = NUM_MAINSCALES - 1,
+ NUM_INTERVALS = REQ_MAIN_SCALES - 1,
// 2 for up and down, 3 allow enough entries for 3 fully subscribed moving actors' worth
MAX_SCRENTRIES = NUM_INTERVALS*2*3
@@ -52,35 +53,79 @@ static SCIdataStruct SCIdata[MAX_SCRENTRIES];
static int scrEntries = 0;
/**
+ * Sets an actor's walk reels
+ */
+
+void SetWalkReels(PMOVER pMover, int scale,
+ SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
+ assert(scale > 0 && scale <= TOTAL_SCALES);
+
+ pMover->walkReels[scale-1][LEFTREEL] = al;
+ pMover->walkReels[scale-1][RIGHTREEL] = ar;
+ pMover->walkReels[scale-1][FORWARD] = af;
+ pMover->walkReels[scale-1][AWAY] = aa;
+}
+
+
+/**
+ * Sets an actor's stand reels
+ */
+
+void SetStandReels(PMOVER pMover, int scale,
+ SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
+ assert(scale > 0 && scale <= TOTAL_SCALES);
+
+ pMover->standReels[scale-1][LEFTREEL] = al;
+ pMover->standReels[scale-1][RIGHTREEL] = ar;
+ pMover->standReels[scale-1][FORWARD] = af;
+ pMover->standReels[scale-1][AWAY] = aa;
+}
+
+
+/**
+ * Sets an actor's talk reels
+ */
+
+void SetTalkReels(PMOVER pMover, int scale,
+ SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
+ assert(scale > 0 && scale <= TOTAL_SCALES);
+
+ pMover->talkReels[scale-1][LEFTREEL] = al;
+ pMover->talkReels[scale-1][RIGHTREEL] = ar;
+ pMover->talkReels[scale-1][FORWARD] = af;
+ pMover->talkReels[scale-1][AWAY] = aa;
+}
+
+/**
* Return handle to actor's talk reel at present scale and direction.
*/
-SCNHANDLE GetMactorTalkReel(PMACTOR pActor, TFTYPE dirn) {
+SCNHANDLE GetMoverTalkReel(PMOVER pActor, TFTYPE dirn) {
assert(1 <= pActor->scale && pActor->scale <= TOTAL_SCALES);
switch (dirn) {
case TF_NONE:
- return pActor->TalkReels[pActor->scale-1][pActor->dirn];
+ return pActor->talkReels[pActor->scale-1][pActor->direction];
case TF_UP:
- return pActor->TalkReels[pActor->scale-1][AWAY];
+ return pActor->talkReels[pActor->scale-1][AWAY];
case TF_DOWN:
- return pActor->TalkReels[pActor->scale-1][FORWARD];
+ return pActor->talkReels[pActor->scale-1][FORWARD];
case TF_LEFT:
- return pActor->TalkReels[pActor->scale-1][LEFTREEL];
+ return pActor->talkReels[pActor->scale-1][LEFTREEL];
case TF_RIGHT:
- return pActor->TalkReels[pActor->scale-1][RIGHTREEL];
+ return pActor->talkReels[pActor->scale-1][RIGHTREEL];
default:
- error("GetMactorTalkReel() - illegal direction!");
+ error("GetMoverTalkReel() - illegal direction!");
}
}
/**
* scalingreels
*/
-void setscalingreels(int actor, int scale, int direction,
+void SetScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
assert(scale >= 1 && scale <= NUM_MAINSCALES); // invalid scale
assert(!(scale == 1 && direction == D_UP) &&
@@ -101,7 +146,7 @@ void setscalingreels(int actor, int scale, int direction,
/**
* ScalingReel
*/
-SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRREEL reel) {
+SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel) {
int d; // Direction
// The smaller the number, the bigger the scale
@@ -129,4 +174,20 @@ void RebootScalingReels(void) {
memset(SCIdata, 0, sizeof(SCIdata));
}
+/**
+ * Discourage them from being ditched.
+ */
+void TouchMoverReels(void) {
+ PMOVER pMover;
+ int scale;
+
+ pMover = NextMover(NULL);
+
+ do {
+ for (scale = 0; scale < TOTAL_SCALES; scale++) {
+ TouchMem(pMover->walkReels[scale][LEFTREEL]);
+ }
+ } while ((pMover = NextMover(pMover)) != NULL);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/mareels.h b/engines/tinsel/mareels.h
new file mode 100644
index 0000000000..cca406a04b
--- /dev/null
+++ b/engines/tinsel/mareels.h
@@ -0,0 +1,56 @@
+/* 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$
+ *
+ */
+
+#ifndef TINSEL_MAREELS_H // prevent multiple includes
+#define TINSEL_MAREELS_H
+
+#include "tinsel/dw.h" // for SCNHANDLE
+#include "tinsel/rince.h"
+
+namespace Tinsel {
+
+void SetWalkReels(PMOVER pMover, int scale,
+ SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
+
+void SetStandReels(PMOVER pMover, int scale,
+ SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
+
+void SetTalkReels(PMOVER pMover, int scale,
+ SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
+
+SCNHANDLE GetMoverTalkReel(PMOVER pActor, TFTYPE dirn);
+
+void SetScalingReels(int actor, int scale, int direction,
+ SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away);
+
+SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel);
+
+void RebootScalingReels(void);
+
+void TouchMoverReels(void);
+
+} // end of namespace Tinsel
+
+#endif
diff --git a/engines/tinsel/module.mk b/engines/tinsel/module.mk
index b00afcddbc..dc89731842 100644
--- a/engines/tinsel/module.mk
+++ b/engines/tinsel/module.mk
@@ -5,11 +5,14 @@ MODULE_OBJS = \
anim.o \
background.o \
bg.o \
+ bmv.o \
cliprect.o \
config.o \
cursor.o \
debugger.o \
detection.o \
+ dialogs.o \
+ drives.o \
effect.o \
events.o \
faders.o \
@@ -17,7 +20,6 @@ MODULE_OBJS = \
graphics.o \
handle.o \
heapmem.o \
- inventory.o \
mareels.o \
move.o \
multiobj.o \
@@ -37,6 +39,7 @@ MODULE_OBJS = \
scroll.o \
sound.o \
strres.o \
+ sysvar.o \
text.o \
timers.o \
tinlib.o \
diff --git a/engines/tinsel/move.cpp b/engines/tinsel/move.cpp
index 803bc5fd7b..20e4175e01 100644
--- a/engines/tinsel/move.cpp
+++ b/engines/tinsel/move.cpp
@@ -38,7 +38,8 @@
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/scroll.h"
-#include "tinsel/tinlib.h" // For stand()
+#include "tinsel/tinlib.h" // For Stand()
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -48,21 +49,16 @@ namespace Tinsel {
//----------------- EXTERNAL FUNCTIONS ---------------------
-// in BG.C
-extern int BackgroundWidth(void);
-extern int BackgroundHeight(void);
-
-
// in POLYGONS.C
// Deliberatley defined here, and not in polygons.h
-HPOLYGON InitExtraBlock(PMACTOR ca, PMACTOR ta);
+HPOLYGON InitExtraBlock(PMOVER ca, PMOVER ta);
//----------------- LOCAL DEFINES --------------------
-#define XMDIST 4
-#define XHMDIST 2
-#define YMDIST 2
-#define YHMDIST 2
+#define XMDIST (TinselV2 ? 6 : 4)
+#define XHMDIST (TinselV2 ? 3 : 2)
+#define YMDIST (TinselV2 ? 3 : 2)
+#define YHMDIST (TinselV2 ? 3 : 2)
#define XTHERE 1
#define XRESTRICT 2
@@ -77,6 +73,7 @@ HPOLYGON InitExtraBlock(PMACTOR ca, PMACTOR ta);
#define ALL_SORTED 1
#define NOT_SORTED 0
+#define STEPS_MAX (TinselV2 ? 12 : 6)
//----------------- LOCAL GLOBAL DATA --------------------
@@ -86,6 +83,8 @@ static int BogusVar = 0; // For slowing down walking, for testing
#endif
static int32 DefaultRefer = 0;
+static int lastLeadXdest = 0, lastLeadYdest = 0;
+
static int hSlowVar = 0; // used by MoveActor()
@@ -94,7 +93,7 @@ static int hSlowVar = 0; // used by MoveActor()
static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
int *newx, int *newy, int *s1, int *s2, HPOLYGON *hS2p,
bool bOver, bool bBodge,
- PMACTOR pActor, PMACTOR *collisionActor = 0);
+ PMOVER pActor, PMOVER *collisionActor = 0);
#if SLOW_RINCE_DOWN
@@ -126,7 +125,7 @@ static int ClickedOnPath(int clickX, int clickY, int *ptgtX, int *ptgtY) {
Clicked within a path,
go to where requested unless blocked.
--------------------------------------*/
- if (InPolygon(clickX, clickY, BLOCKING) == NOPOLY) {
+ if (InPolygon(clickX, clickY, BLOCK) == NOPOLY) {
// Not in a blocking polygon - go to where requested.
*ptgtX = clickX;
*ptgtY = clickY;
@@ -143,7 +142,7 @@ static int ClickedOnPath(int clickX, int clickY, int *ptgtX, int *ptgtY) {
i = SCREEN_HEIGHT;
break;
}
- if (InPolygon(clickX, i, BLOCKING) == NOPOLY) {
+ if (InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
@@ -156,7 +155,7 @@ static int ClickedOnPath(int clickX, int clickY, int *ptgtX, int *ptgtY) {
i = -1;
break;
}
- if (InPolygon(clickX, i, BLOCKING) == NOPOLY) {
+ if (InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
@@ -187,15 +186,15 @@ static int ClickedOnRefer(HPOLYGON hRefpoly, int clickX, int clickY, int *ptgtX,
switch (PolySubtype(hRefpoly)) {
case REF_POINT: // Go to specified node
- getPolyNode(hRefpoly, ptgtX, ptgtY);
+ GetPolyNode(hRefpoly, ptgtX, ptgtY);
assert(InPolygon(*ptgtX, *ptgtY, PATH) != NOPOLY); // POINT Referral to illegal point
break;
case REF_DOWN: // Search downwards
- end = BackgroundHeight();
+ end = BgHeight();
for (i = clickY+1; i < end; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY
- && InPolygon(clickX, i, BLOCKING) == NOPOLY) {
+ && InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
@@ -205,7 +204,7 @@ static int ClickedOnRefer(HPOLYGON hRefpoly, int clickX, int clickY, int *ptgtX,
case REF_UP: // Search upwards
for (i = clickY-1; i >= 0; i--)
if (InPolygon(clickX, i, PATH) != NOPOLY
- && InPolygon(clickX, i, BLOCKING) == NOPOLY) {
+ && InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
@@ -213,10 +212,10 @@ static int ClickedOnRefer(HPOLYGON hRefpoly, int clickX, int clickY, int *ptgtX,
break;
case REF_RIGHT: // Search to the right
- end = BackgroundWidth();
+ end = BgWidth();
for (i = clickX+1; i < end; i++)
if (InPolygon(i, clickY, PATH) != NOPOLY
- && InPolygon(i, clickY, BLOCKING) == NOPOLY) {
+ && InPolygon(i, clickY, BLOCK) == NOPOLY) {
*ptgtX = i;
*ptgtY = clickY;
break;
@@ -226,7 +225,7 @@ static int ClickedOnRefer(HPOLYGON hRefpoly, int clickX, int clickY, int *ptgtX,
case REF_LEFT: // Search to the left
for (i = clickX-1; i >= 0; i--)
if (InPolygon(i, clickY, PATH) != NOPOLY
- && InPolygon(i, clickY, BLOCKING) == NOPOLY) {
+ && InPolygon(i, clickY, BLOCK) == NOPOLY) {
*ptgtX = i;
*ptgtY = clickY;
break;
@@ -265,7 +264,7 @@ static int ClickedOnNothing(int clickX, int clickY, int *ptgtX, int *ptgtY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
// Try searching down and up (offscreen).
- end = BackgroundHeight();
+ end = BgHeight();
for (i = clickY+1; i < end; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
@@ -284,7 +283,7 @@ static int ClickedOnNothing(int clickX, int clickY, int *ptgtX, int *ptgtY) {
break;
case REF_DOWN:
- end = BackgroundHeight();
+ end = BgHeight();
for (i = clickY+1; i < end; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
@@ -299,7 +298,7 @@ static int ClickedOnNothing(int clickX, int clickY, int *ptgtX, int *ptgtY) {
break;
case REF_RIGHT:
- end = BackgroundWidth();
+ end = BgWidth();
for (i = clickX + 1; i < end; i++)
if (InPolygon(i, clickY, PATH) != NOPOLY) {
return ClickedOnPath(i, clickY, ptgtX, ptgtY);
@@ -339,18 +338,19 @@ static int WorkOutDestination(int clickX, int clickY, int *ptgtX, int *ptgtY) {
/**
* Work out which reel to adopt for a section of movement.
*/
-static DIRREEL GetDirectionReel(int fromx, int fromy, int tox, int toy, DIRREEL lastreel, HPOLYGON hPath) {
+DIRECTION GetDirection(int fromx, int fromy, int tox, int toy, DIRECTION lastreel,
+ HPOLYGON hPath, YBIAS yBias) {
int xchange = 0, ychange = 0;
enum {X_NONE, X_LEFT, X_RIGHT, X_NO} xdir;
enum {Y_NONE, Y_UP, Y_DOWN, Y_NO} ydir;
- DIRREEL reel = lastreel; // Leave alone if can't decide
+ DIRECTION reel = lastreel; // Leave alone if can't decide
/*
* Determine size and direction of X movement.
* i.e. left, right, none or not allowed.
*/
- if (getPolyReelType(hPath) == REEL_VERT)
+ if (hPath != NOPOLY && (GetPolyReelType(hPath) == REEL_VERT))
xdir = X_NO;
else if (tox == -1)
xdir = X_NONE;
@@ -369,7 +369,7 @@ static DIRREEL GetDirectionReel(int fromx, int fromy, int tox, int toy, DIRREEL
* Determine size and direction of Y movement.
* i.e. up, down, none or not allowed.
*/
- if (getPolyReelType(hPath) == REEL_HORIZ)
+ if (hPath != NOPOLY && (GetPolyReelType(hPath) == REEL_HORIZ))
ydir = Y_NO;
else if (toy == -1)
ydir = Y_NONE;
@@ -387,7 +387,15 @@ static DIRREEL GetDirectionReel(int fromx, int fromy, int tox, int toy, DIRREEL
/*
* Some adjustment to allow for different x and y pixell sizes.
*/
- ychange += ychange; // Double y distance to cover
+ switch (yBias) {
+ case YB_X2:
+ ychange += ychange; // Double y distance to cover
+ break;
+
+ case YB_X1_5:
+ ychange += ychange / 2; // Double y distance to cover
+ break;
+ }
/*
* Determine which reel to use.
@@ -462,37 +470,60 @@ static DIRREEL GetDirectionReel(int fromx, int fromy, int tox, int toy, DIRREEL
/**
* Haven't moved, look towards the cursor.
*/
-static void GotThereWithoutMoving(PMACTOR pActor) {
+static void GotThereWithoutMoving(PMOVER pActor) {
int curX, curY;
- DIRREEL reel;
+ DIRECTION reel;
- if (!pActor->TagReelRunning) {
+ if (!pActor->bSpecReel) {
GetCursorXYNoWait(&curX, &curY, true);
- reel = GetDirectionReel(pActor->objx, pActor->objy, curX, curY, pActor->dirn, pActor->hCpath);
+ reel = GetDirection(pActor->objX, pActor->objY, curX, curY, pActor->direction, pActor->hCpath);
- if (reel != pActor->dirn)
- SetMActorWalkReel(pActor, reel, pActor->scale, false);
+ if (reel != pActor->direction)
+ SetMoverWalkReel(pActor, reel, pActor->scale, false);
}
}
/**
* Arrived at final destination.
*/
-static void GotThere(PMACTOR pActor) {
- pActor->targetX = pActor->targetY = -1; // 4/1/95
- pActor->ItargetX = pActor->ItargetY = -1;
- pActor->UtargetX = pActor->UtargetY = -1;
+static void GotThere(PMOVER pMover) {
+ pMover->targetX = pMover->targetY = -1; // 4/1/95
+ pMover->ItargetX = pMover->ItargetY = -1;
+ pMover->UtargetX = pMover->UtargetY = -1;
// Perhaps we have'nt moved.
- if (pActor->objx == (int)pActor->fromx && pActor->objy == (int)pActor->fromy)
- GotThereWithoutMoving(pActor);
+ if (pMover->objX == (int)pMover->walkedFromX && pMover->objY == (int)pMover->walkedFromY) {
+ // Got there without moving
+ if (!TinselV2)
+ GotThereWithoutMoving(pMover);
+ else if (!pMover->bSpecReel) {
+ // No tag reel, look at cursor
+ int curX, curY;
+ DIRECTION direction;
+
+ GetCursorXY(&curX, &curY, true);
+ direction = GetDirection(pMover->objX, pMover->objY,
+ curX, curY,
+ pMover->direction,
+ pMover->hCpath,
+ YB_X2);
+
+ if (direction != pMover->direction)
+ SetMoverWalkReel(pMover, direction, pMover->scale, false);
+ }
+ }
- ReTagActor(pActor->actorID); // Tag allowed while stationary
+ if (!TinselV2)
+ ReTagActor(pMover->actorID); // Tag allowed while stationary
- SetMActorStanding(pActor);
+ SetMoverStanding(pMover);
+ pMover->bMoving = false;
- pActor->bMoving = false;
+ if (TinselV2 && pMover->bIgPath && pMover->zOverride != -1
+ && InPolygon(pMover->objX, pMover->objY, PATH) == NOPOLY)
+ // New feature for end-of-scene walk-outs
+ SetMoverZ(pMover, pMover->objY, pMover->zOverride);
}
enum cgt { GT_NOTL, GT_NOTB, GT_NOT2, GT_OK, GT_MAY };
@@ -500,15 +531,15 @@ enum cgt { GT_NOTL, GT_NOTB, GT_NOT2, GT_OK, GT_MAY };
/**
* Can we get straight there?
*/
-static cgt CanGetThere(PMACTOR pActor, int tx, int ty) {
+static cgt CanGetThere(PMOVER pActor, int tx, int ty) {
int s1, s2; // s2 not used here!
HPOLYGON hS2p; // nor is s2p!
int nextx, nexty;
int targetX = tx;
int targetY = ty; // Ultimate destination
- int x = pActor->objx;
- int y = pActor->objy; // Present position
+ int x = pActor->objX;
+ int y = pActor->objY; // Present position
while (targetX != -1 || targetY != -1) {
NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
@@ -536,7 +567,7 @@ static cgt CanGetThere(PMACTOR pActor, int tx, int ty) {
/**
* Set final destination.
*/
-static void SetMoverUltDest(PMACTOR pActor, int x, int y) {
+static void SetMoverUltDest(PMOVER pActor, int x, int y) {
pActor->UtargetX = x;
pActor->UtargetY = y;
pActor->hUpath = InPolygon(x, y, PATH);
@@ -554,89 +585,108 @@ static void SetMoverUltDest(PMACTOR pActor, int x, int y) {
* Otherwise, head towards the pseudo-centre or end node of the first
* en-route path.
*/
-static void SetMoverIntDest(PMACTOR pActor, int x, int y) {
+static void SetMoverIntDest(PMOVER pMover, int x, int y) {
HPOLYGON hIpath, hTpath;
int node;
hTpath = InPolygon(x, y, PATH); // Target path
#ifdef DEBUG
- if (!pActor->bIgPath)
+ if (!pMover->bIgPath)
assert(hTpath != NOPOLY); // SetMoverIntDest() - target not in path
#endif
- if (pActor->hCpath == hTpath || pActor->bIgPath
- || IsInPolygon(pActor->objx, pActor->objy, hTpath)) {
+ if (pMover->hCpath == hTpath || pMover->bIgPath
+ || IsInPolygon(pMover->objX, pMover->objY, hTpath)) {
// In destination path - head straight for the target.
- pActor->ItargetX = x;
- pActor->ItargetY = y;
- pActor->hIpath = hTpath;
- } else if (IsAdjacentPath(pActor->hCpath, hTpath)) {
+ pMover->ItargetX = x;
+ pMover->ItargetY = y;
+ // make damn sure that Itarget is in hIpath
+ pMover->hIpath = !TinselV2 ? hTpath : InPolygon(x, y, PATH);
+ } else if (IsAdjacentPath(pMover->hCpath, hTpath)) {
// In path adjacent to target
if (PolySubtype(hTpath) != NODE) {
// Target path is normal - head for target.
// Added 26/01/95, innroom
- if (CanGetThere(pActor, x, y) == GT_NOTL) {
- NearestCorner(&x, &y, pActor->hCpath, hTpath);
+ if (CanGetThere(pMover, x, y) == GT_NOTL) {
+ NearestCorner(&x, &y, pMover->hCpath, hTpath);
}
- pActor->ItargetX = x;
- pActor->ItargetY = y;
+ pMover->ItargetX = x;
+ pMover->ItargetY = y;
+ if (TinselV2)
+ // make damn sure that Itarget is in hIpath
+ pMover->hIpath = InPolygon(x, y, PATH);
} else {
// Target path is node - head for end node.
- node = NearestEndNode(hTpath, pActor->objx, pActor->objy);
- getNpathNode(hTpath, node, &pActor->ItargetX, &pActor->ItargetY);
-
+ node = NearestEndNode(hTpath, pMover->objX, pMover->objY);
+ getNpathNode(hTpath, node, &pMover->ItargetX, &pMover->ItargetY);
+ if (TinselV2)
+ // make damn sure that Itarget is in hIpath
+ pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
}
- pActor->hIpath = hTpath;
+ if (!TinselV2)
+ pMover->hIpath = hTpath;
} else {
assert(hTpath != NOPOLY); // Error 701
- hIpath = getPathOnTheWay(pActor->hCpath, hTpath);
+ hIpath = GetPathOnTheWay(pMover->hCpath, hTpath);
- if (hIpath != NOPOLY) {
+ if (TinselV2 && (hIpath == NOPOLY)) {
+ pMover->hIpath = NOPOLY;
+ } else if (hIpath != NOPOLY) {
/* Head for an en-route path */
if (PolySubtype(hIpath) != NODE) {
/* En-route path is normal - head for pseudo centre. */
- if (CanGetThere(pActor, x, y) == GT_OK) {
- pActor->ItargetX = x;
- pActor->ItargetY = y;
+ if (CanGetThere(pMover, x, y) == GT_OK) {
+ pMover->ItargetX = x;
+ pMover->ItargetY = y;
+ if (TinselV2)
+ // make damn sure that Itarget is in hIpath
+ pMover->hIpath = InPolygon(x, y, PATH);
} else {
- pActor->ItargetX = PolyCentreX(hIpath);
- pActor->ItargetY = PolyCentreY(hIpath);
+ pMover->ItargetX = PolyCentreX(hIpath);
+ pMover->ItargetY = PolyCentreY(hIpath);
+ if (TinselV2)
+ // make damn sure that Itarget is in hIpath
+ pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
}
} else {
/* En-route path is node - head for end node. */
- node = NearestEndNode(hIpath, pActor->objx, pActor->objy);
- getNpathNode(hIpath, node, &pActor->ItargetX, &pActor->ItargetY);
+ node = NearestEndNode(hIpath, pMover->objX, pMover->objY);
+ getNpathNode(hIpath, node, &pMover->ItargetX, &pMover->ItargetY);
+ if (TinselV2)
+ // make damn sure that Itarget is in hIpath
+ pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
}
- pActor->hIpath = hIpath;
+ if (!TinselV2)
+ pMover->hIpath = hIpath;
}
}
- pActor->InDifficulty = NO_PROB;
+ pMover->InDifficulty = NO_PROB;
}
/**
* Set short-term destination and adopt the appropriate reel.
*/
-static void SetMoverDest(PMACTOR pActor, int x, int y) {
+static void SetMoverDest(PMOVER pActor, int x, int y) {
int scale;
- DIRREEL reel;
+ DIRECTION reel;
// Set the co-ordinates requested.
pActor->targetX = x;
pActor->targetY = y;
pActor->InDifficulty = NO_PROB;
- reel = GetDirectionReel(pActor->objx, pActor->objy, x, y, pActor->dirn, pActor->hCpath);
- scale = GetScale(pActor->hCpath, pActor->objy);
- if (scale != pActor->scale || reel != pActor->dirn) {
- SetMActorWalkReel(pActor, reel, scale, false);
+ reel = GetDirection(pActor->objX, pActor->objY, x, y, pActor->direction, pActor->hCpath);
+ scale = GetScale(pActor->hCpath, pActor->objY);
+ if (scale != pActor->scale || reel != pActor->direction) {
+ SetMoverWalkReel(pActor, reel, scale, false);
}
}
/**
* SetNextDest
*/
-static void SetNextDest(PMACTOR pActor) {
+static void SetNextDest(PMOVER pMover) {
int targetX, targetY; // Ultimate destination
int x, y; // Present position
int nextx, nexty;
@@ -653,7 +703,7 @@ static void SetNextDest(PMACTOR pActor) {
int ss1, ss2;
HPOLYGON shS2p;
- PMACTOR collisionActor;
+ PMOVER collisionActor;
#if 1
int sTargetX, sTargetY;
#endif
@@ -661,30 +711,30 @@ static void SetNextDest(PMACTOR pActor) {
/*
* Desired destination (Itarget) is already set
*/
- x = pActor->objx; // Current position
- y = pActor->objy;
- targetX = pActor->ItargetX; // Desired position
- targetY = pActor->ItargetY;
+ x = pMover->objX; // Current position
+ y = pMover->objY;
+ targetX = pMover->ItargetX; // Desired position
+ targetY = pMover->ItargetY;
/*
* If we're where we're headed, end it all (the moving).
*/
// if (x == targetX && y == targetY)
if (ABS(x - targetX) < XMDIST && ABS(y - targetY) < YMDIST) {
- if (targetX == pActor->UtargetX && targetY == pActor->UtargetY) {
+ if (targetX == pMover->UtargetX && targetY == pMover->UtargetY) {
// Desired position
- GotThere(pActor);
+ GotThere(pMover);
return;
} else {
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5001
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5001
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
}
}
- if (pActor->bNoPath || pActor->bIgPath) {
+ if (pMover->bNoPath || pMover->bIgPath) {
/* Can get there directly. */
- SetMoverDest(pActor, targetX, targetY);
- pActor->over = false;
+ SetMoverDest(pMover, targetX, targetY);
+ pMover->over = false;
return;
}
@@ -692,8 +742,8 @@ static void SetNextDest(PMACTOR pActor) {
| Some work to do here if we're in a follow nodes polygon - basically
| head for the next node.
----------------------------------------------------------------------*/
- hNpoly = pActor->hFnpath; // The node path we're in (if any)
- switch (pActor->npstatus) {
+ hNpoly = pMover->hFnpath; // The node path we're in (if any)
+ switch (pMover->npstatus) {
case NOT_IN:
break;
@@ -701,99 +751,99 @@ static void SetNextDest(PMACTOR pActor) {
znode = NearestEndNode(hNpoly, x, y);
/* Hang on, we're probably here already! */
if (znode) {
- pActor->npstatus = GOING_DOWN;
- pActor->line = znode-1;
+ pMover->npstatus = GOING_DOWN;
+ pMover->line = znode-1;
getNpathNode(hNpoly, znode - 1, &nx, &ny);
} else {
- pActor->npstatus = GOING_UP;
- pActor->line = znode;
+ pMover->npstatus = GOING_UP;
+ pMover->line = znode;
getNpathNode(hNpoly, 1, &nx, &ny);
}
- SetMoverDest(pActor, nx, ny);
+ SetMoverDest(pMover, nx, ny);
// Test for pseudo-one-node npaths
if (numNodes(hNpoly) == 2 &&
- ABS(pActor->objx - pActor->targetX) < XMDIST &&
- ABS(pActor->objy - pActor->targetY) < YMDIST) {
+ ABS(pMover->objX - pMover->targetX) < XMDIST &&
+ ABS(pMover->objY - pMover->targetY) < YMDIST) {
// That's enough, we're leaving
- pActor->npstatus = LEAVING;
+ pMover->npstatus = LEAVING;
} else {
// Normal situation
- pActor->over = true;
+ pMover->over = true;
return;
}
// Fall through for LEAVING
case LEAVING:
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5002
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
- targetX = pActor->ItargetX; // Desired position
- targetY = pActor->ItargetY;
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5002
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ targetX = pMover->ItargetX; // Desired position
+ targetY = pMover->ItargetY;
break;
case GOING_UP:
- i = pActor->line; // The line we're on
+ i = pMover->line; // The line we're on
// Is this the final target line?
- if (i+1 == pActor->Tline && hNpoly == pActor->hUpath) {
+ if (i+1 == pMover->Tline && hNpoly == pMover->hUpath) {
// The final leg of the journey
- pActor->line = i+1;
- SetMoverDest(pActor, pActor->UtargetX, pActor->UtargetY);
- pActor->over = false;
+ pMover->line = i+1;
+ SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ pMover->over = false;
return;
} else {
// Go to the next node unless we're at the last one
i++; // The node we're at
if (++i < numNodes(hNpoly)) {
getNpathNode(hNpoly, i, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->line = i-1;
- if (ABS(pActor->UtargetX - pActor->targetX) < XMDIST &&
- ABS(pActor->UtargetY - pActor->targetY) < YMDIST)
- pActor->over = false;
+ SetMoverDest(pMover, nx, ny);
+ pMover->line = i-1;
+ if (ABS(pMover->UtargetX - pMover->targetX) < XMDIST &&
+ ABS(pMover->UtargetY - pMover->targetY) < YMDIST)
+ pMover->over = false;
else
- pActor->over = true;
+ pMover->over = true;
return;
} else {
// Last node - we're off
- pActor->npstatus = LEAVING;
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5003
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
- targetX = pActor->ItargetX; // Desired position
- targetY = pActor->ItargetY;
+ pMover->npstatus = LEAVING;
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5003
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ targetX = pMover->ItargetX; // Desired position
+ targetY = pMover->ItargetY;
break;
}
}
case GOING_DOWN:
- i = pActor->line; // The line we're on and the node we're at
+ i = pMover->line; // The line we're on and the node we're at
// Is this the final target line?
- if (i - 1 == pActor->Tline && hNpoly == pActor->hUpath) {
+ if (i - 1 == pMover->Tline && hNpoly == pMover->hUpath) {
// The final leg of the journey
- SetMoverDest(pActor, pActor->UtargetX, pActor->UtargetY);
- pActor->line = i-1;
- pActor->over = false;
+ SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ pMover->line = i-1;
+ pMover->over = false;
return;
} else {
// Go to the next node unless we're at the last one
if (--i >= 0) {
getNpathNode(hNpoly, i, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->line--; /* The next node to head for */
- if (ABS(pActor->UtargetX - pActor->targetX) < XMDIST &&
- ABS(pActor->UtargetY - pActor->targetY) < YMDIST)
- pActor->over = false;
+ SetMoverDest(pMover, nx, ny);
+ pMover->line--; /* The next node to head for */
+ if (ABS(pMover->UtargetX - pMover->targetX) < XMDIST &&
+ ABS(pMover->UtargetY - pMover->targetY) < YMDIST)
+ pMover->over = false;
else
- pActor->over = true;
+ pMover->over = true;
return;
} else {
// Last node - we're off
- pActor->npstatus = LEAVING;
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5004
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
- targetX = pActor->ItargetX; // Desired position
- targetY = pActor->ItargetY;
+ pMover->npstatus = LEAVING;
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5004
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ targetX = pMover->ItargetX; // Desired position
+ targetY = pMover->ItargetY;
break;
}
}
@@ -814,7 +864,7 @@ static void SetNextDest(PMACTOR pActor) {
sTargetY = targetY;
#endif
NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
- &s1, &s2, &hS2p, pActor->over, false, pActor, &collisionActor);
+ &s1, &s2, &hS2p, pMover->over, false, pMover, &collisionActor);
if (s1 != (XTHERE | YTHERE) && x == nextx && y == nexty) {
ss1 = s1;
@@ -829,7 +879,7 @@ static void SetNextDest(PMACTOR pActor) {
// nobbled by that last call to NewCoOrdinates()
// Re-instating them (can) leads to oscillation
NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
- &s1, &s2, &hS2p, pActor->over, true, pActor, &collisionActor);
+ &s1, &s2, &hS2p, pMover->over, true, pMover, &collisionActor);
if (x == nextx && y == nexty) {
s1 = ss1;
@@ -840,8 +890,8 @@ static void SetNextDest(PMACTOR pActor) {
if (s1 == (XTHERE | YTHERE)) {
/* Can get there directly. */
- SetMoverDest(pActor, nextx, nexty);
- pActor->over = false;
+ SetMoverDest(pMover, nextx, nexty);
+ pMover->over = false;
break;
} else if ((s1 & STUCK) || s1 == (XRESTRICT + YRESTRICT)
|| s1 == (XTHERE | YRESTRICT) || s1 == (YTHERE | XRESTRICT)) {
@@ -851,61 +901,61 @@ static void SetNextDest(PMACTOR pActor) {
something about it. |
-------------------------------------------------*/
if (s2 & ENTERING_BLOCK) {
- x = pActor->objx; // Current position
- y = pActor->objy;
+ x = pMover->objX; // Current position
+ y = pMover->objY;
// Go to the nearest corner of the blocking polygon concerned
- BlockingCorner(hS2p, &x, &y, pActor->ItargetX, pActor->ItargetY);
- SetMoverDest(pActor, x, y);
- pActor->over = false;
+ BlockingCorner(hS2p, &x, &y, pMover->ItargetX, pMover->ItargetY);
+ SetMoverDest(pMover, x, y);
+ pMover->over = false;
} else if (s2 & ENTERING_MBLOCK) {
- if (InMActorBlock(pActor, pActor->UtargetX, pActor->UtargetY)) {
+ if (InMoverBlock(pMover, pMover->UtargetX, pMover->UtargetY)) {
// The best we're going to achieve
- SetMoverUltDest(pActor, x, y);
- SetMoverDest(pActor, x, y);
+ SetMoverUltDest(pMover, x, y);
+ SetMoverDest(pMover, x, y);
} else {
- sx = pActor->objx;
- sy = pActor->objy;
-// pActor->objx = x;
-// pActor->objy = y;
-
- hEb = InitExtraBlock(pActor, collisionActor);
- x = pActor->objx;
- y = pActor->objy;
- BlockingCorner(hEb, &x, &y, pActor->ItargetX, pActor->ItargetY);
-
- pActor->objx = sx;
- pActor->objy = sy;
- SetMoverDest(pActor, x, y);
- pActor->over = false;
+ sx = pMover->objX;
+ sy = pMover->objY;
+// pMover->objX = x;
+// pMover->objY = y;
+
+ hEb = InitExtraBlock(pMover, collisionActor);
+ x = pMover->objX;
+ y = pMover->objY;
+ BlockingCorner(hEb, &x, &y, pMover->ItargetX, pMover->ItargetY);
+
+ pMover->objX = sx;
+ pMover->objY = sy;
+ SetMoverDest(pMover, x, y);
+ pMover->over = false;
}
} else {
/*----------------------------------------
Currently, this is as far as we can go. |
Definitely room for improvement here! |
----------------------------------------*/
- hPath = InPolygon(pActor->ItargetX, pActor->ItargetY, PATH);
- if (hPath != pActor->hIpath) {
- if (IsInPolygon(pActor->ItargetX, pActor->ItargetY, pActor->hIpath))
- hPath = pActor->hIpath;
+ hPath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
+ if (hPath != pMover->hIpath) {
+ if (IsInPolygon(pMover->ItargetX, pMover->ItargetY, pMover->hIpath))
+ hPath = pMover->hIpath;
}
- assert(hPath == pActor->hIpath);
+ assert(hPath == pMover->hIpath);
- if (pActor->InDifficulty == NO_PROB) {
+ if (pMover->InDifficulty == NO_PROB) {
x = PolyCentreX(hPath);
y = PolyCentreY(hPath);
- SetMoverDest(pActor, x, y);
- pActor->InDifficulty = TRY_CENTRE;
- pActor->over = false;
- } else if (pActor->InDifficulty == TRY_CENTRE) {
- NearestCorner(&x, &y, pActor->hCpath, pActor->hIpath);
- SetMoverDest(pActor, x, y);
- pActor->InDifficulty = TRY_CORNER;
- pActor->over = false;
- } else if (pActor->InDifficulty == TRY_CORNER) {
- NearestCorner(&x, &y, pActor->hCpath, pActor->hIpath);
- SetMoverDest(pActor, x, y);
- pActor->InDifficulty = TRY_NEXTCORNER;
- pActor->over = false;
+ SetMoverDest(pMover, x, y);
+ pMover->InDifficulty = TRY_CENTRE;
+ pMover->over = false;
+ } else if (pMover->InDifficulty == TRY_CENTRE) {
+ NearestCorner(&x, &y, pMover->hCpath, pMover->hIpath);
+ SetMoverDest(pMover, x, y);
+ pMover->InDifficulty = TRY_CORNER;
+ pMover->over = false;
+ } else if (pMover->InDifficulty == TRY_CORNER) {
+ NearestCorner(&x, &y, pMover->hCpath, pMover->hIpath);
+ SetMoverDest(pMover, x, y);
+ pMover->InDifficulty = TRY_NEXTCORNER;
+ pMover->over = false;
}
}
break;
@@ -916,41 +966,43 @@ static void SetNextDest(PMACTOR pActor) {
A restriction in a direction has been removed. |
Use this as an intermediate destination. |
-----------------------------------------------*/
- SetMoverDest(pActor, nextx, nexty);
- pActor->over = false;
+ SetMoverDest(pMover, nextx, nexty);
+ pMover->over = false;
break;
}
x = nextx;
y = nexty;
- /*-------------------------
- Change of path polygon? |
- -------------------------*/
- hPath = InPolygon(x, y, PATH);
- if (pActor->hCpath != hPath &&
- !IsInPolygon(x, y, pActor->hCpath) &&
- !IsAdjacentPath(pActor->hCpath, pActor->hIpath)) {
- /*----------------------------------------------------------
- If just entering a follow nodes polygon, go to first node.|
- Else if just going to pass through, go to pseudo-centre. |
- ----------------------------------------------------------*/
- if (PolySubtype(hPath) == NODE && pActor->hFnpath != hPath && pActor->npstatus != LEAVING) {
- int node = NearestEndNode(hPath, x, y);
- getNpathNode(hPath, node, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->over = true;
- } else if (!IsInPolygon(pActor->ItargetX, pActor->ItargetY, hPath) &&
- !IsInPolygon(pActor->ItargetX, pActor->ItargetY, pActor->hCpath)) {
- SetMoverDest(pActor, PolyCentreX(hPath), PolyCentreY(hPath));
- pActor->over = true;
- } else {
- SetMoverDest(pActor, pActor->ItargetX, pActor->ItargetY);
+ if (!TinselV2) {
+ /*-------------------------
+ Change of path polygon? |
+ -------------------------*/
+ hPath = InPolygon(x, y, PATH);
+ if (pMover->hCpath != hPath &&
+ !IsInPolygon(x, y, pMover->hCpath) &&
+ !IsAdjacentPath(pMover->hCpath, pMover->hIpath)) {
+ /*----------------------------------------------------------
+ If just entering a follow nodes polygon, go to first node.|
+ Else if just going to pass through, go to pseudo-centre. |
+ ----------------------------------------------------------*/
+ if (PolySubtype(hPath) == NODE && pMover->hFnpath != hPath && pMover->npstatus != LEAVING) {
+ int node = NearestEndNode(hPath, x, y);
+ getNpathNode(hPath, node, &nx, &ny);
+ SetMoverDest(pMover, nx, ny);
+ pMover->over = true;
+ } else if (!IsInPolygon(pMover->ItargetX, pMover->ItargetY, hPath) &&
+ !IsInPolygon(pMover->ItargetX, pMover->ItargetY, pMover->hCpath)) {
+ SetMoverDest(pMover, PolyCentreX(hPath), PolyCentreY(hPath));
+ pMover->over = true;
+ } else {
+ SetMoverDest(pMover, pMover->ItargetX, pMover->ItargetY);
+ }
+ break;
}
- break;
- }
- lstatus = s1;
+ lstatus = s1;
+ }
}
}
@@ -961,19 +1013,19 @@ static void SetNextDest(PMACTOR pActor) {
static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
int *newx, int *newy, int *s1, int *s2,
HPOLYGON *hS2p, bool bOver, bool bBodge,
- PMACTOR pActor, PMACTOR *collisionActor) {
+ PMOVER pMover, PMOVER *collisionActor) {
HPOLYGON hPoly;
int sidem, depthm;
int sidesteps, depthsteps;
- PMACTOR ma;
+ PMOVER ma;
*s1 = *s2 = 0;
/*------------------------------------------------
Don't overrun if this is the final destination. |
------------------------------------------------*/
- if (*targetX == pActor->UtargetX && (*targetY == -1 || *targetY == pActor->UtargetY) ||
- *targetY == pActor->UtargetY && (*targetX == -1 || *targetX == pActor->UtargetX))
+ if (*targetX == pMover->UtargetX && (*targetY == -1 || *targetY == pMover->UtargetY) ||
+ *targetY == pMover->UtargetY && (*targetX == -1 || *targetX == pMover->UtargetX))
bOver = false;
/*----------------------------------------------------
@@ -1087,7 +1139,7 @@ static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
Must now check if it's in a legal spot.
------------------------------------------------------*/
- if (!pActor->bNoPath && !pActor->bIgPath) {
+ if (!pMover->bNoPath && !pMover->bIgPath) {
/*------------------------------
Must stay in a path polygon.
-------------------------------*/
@@ -1095,10 +1147,10 @@ static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
if (hPoly == NOPOLY) {
*s2 = LEAVING_PATH; // Trying to leave the path polygons
- if (*newx != fromx && InPolygon(*newx, fromy, PATH) != NOPOLY && InPolygon(*newx, fromy, BLOCKING) == NOPOLY) {
+ if (*newx != fromx && InPolygon(*newx, fromy, PATH) != NOPOLY && InPolygon(*newx, fromy, BLOCK) == NOPOLY) {
*newy = fromy;
*s1 |= YRESTRICT;
- } else if (*newy != fromy && InPolygon(fromx, *newy, PATH) != NOPOLY && InPolygon(fromx, *newy, BLOCKING) == NOPOLY) {
+ } else if (*newy != fromy && InPolygon(fromx, *newy, PATH) != NOPOLY && InPolygon(fromx, *newy, BLOCK) == NOPOLY) {
*newx = fromx;
*s1 |= XRESTRICT;
} else {
@@ -1115,15 +1167,15 @@ static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
/*--------------------------------------
Must stay out of blocking polygons.
--------------------------------------*/
- hPoly = InPolygon(*newx, *newy, BLOCKING);
+ hPoly = InPolygon(*newx, *newy, BLOCK);
if (hPoly != NOPOLY) {
*s2 = ENTERING_BLOCK; // Trying to enter a blocking poly
*hS2p = hPoly;
- if (*newx != fromx && InPolygon(*newx, fromy, BLOCKING) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
+ if (*newx != fromx && InPolygon(*newx, fromy, BLOCK) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
*newy = fromy;
*s1 |= YRESTRICT;
- } else if (*newy != fromy && InPolygon(fromx, *newy, BLOCKING) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
+ } else if (*newy != fromy && InPolygon(fromx, *newy, BLOCK) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
*newx = fromx;
*s1 |= XRESTRICT;
} else {
@@ -1138,22 +1190,22 @@ static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
/*------------------------------------------------------
Must stay out of moving actors' blocking polygons.
------------------------------------------------------*/
- ma = InMActorBlock(pActor, *newx, *newy);
+ ma = InMoverBlock(pMover, *newx, *newy);
if (ma != NULL) {
// Ignore if already in it (it may have just appeared)
- if (!InMActorBlock(pActor, pActor->objx, pActor->objy)) {
+ if (!InMoverBlock(pMover, pMover->objX, pMover->objY)) {
*s2 = ENTERING_MBLOCK; // Trying to walk through an actor
*hS2p = -1;
if (collisionActor)
*collisionActor = ma;
- if (*newx != fromx && InMActorBlock(pActor, *newx, fromy) == NULL
- && InPolygon(*newx, fromy, BLOCKING) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
+ if (*newx != fromx && InMoverBlock(pMover, *newx, fromy) == NULL
+ && InPolygon(*newx, fromy, BLOCK) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
*newy = fromy;
*s1 |= YRESTRICT;
- } else if (*newy != fromy && InMActorBlock(pActor, fromx, *newy) == NULL
- && InPolygon(fromx, *newy, BLOCKING) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
+ } else if (*newy != fromy && InMoverBlock(pMover, fromx, *newy) == NULL
+ && InPolygon(fromx, *newy, BLOCK) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
*newx = fromx;
*s1 |= XRESTRICT;
} else {
@@ -1172,7 +1224,7 @@ static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
/**
* SetOffWithinNodePath
*/
-static void SetOffWithinNodePath(PMACTOR pActor, HPOLYGON StartPath, HPOLYGON DestPath,
+static void SetOffWithinNodePath(PMOVER pMover, HPOLYGON StartPath, HPOLYGON DestPath,
int targetX, int targetY) {
int endnode;
HPOLYGON hIpath;
@@ -1180,17 +1232,17 @@ static void SetOffWithinNodePath(PMACTOR pActor, HPOLYGON StartPath, HPOLYGON De
int x, y;
if (StartPath == DestPath) {
- if (pActor->line == pActor->Tline) {
- SetMoverDest(pActor, pActor->UtargetX, pActor->UtargetY);
- pActor->over = false;
- } else if (pActor->line < pActor->Tline) {
- getNpathNode(StartPath, pActor->line+1, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->npstatus = GOING_UP;
- } else if (pActor->line > pActor->Tline) {
- getNpathNode(StartPath, pActor->line, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->npstatus = GOING_DOWN;
+ if (pMover->line == pMover->Tline) {
+ SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ pMover->over = false;
+ } else if (pMover->line < pMover->Tline) {
+ getNpathNode(StartPath, pMover->line+1, &nx, &ny);
+ SetMoverDest(pMover, nx, ny);
+ pMover->npstatus = GOING_UP;
+ } else if (pMover->line > pMover->Tline) {
+ getNpathNode(StartPath, pMover->line, &nx, &ny);
+ SetMoverDest(pMover, nx, ny);
+ pMover->npstatus = GOING_DOWN;
}
} else {
/*
@@ -1198,7 +1250,7 @@ static void SetOffWithinNodePath(PMACTOR pActor, HPOLYGON StartPath, HPOLYGON De
* which end of this path to head for.
*/
assert(DestPath != NOPOLY); // Error 702
- if ((hIpath = getPathOnTheWay(StartPath, DestPath)) == NOPOLY) {
+ if ((hIpath = GetPathOnTheWay(StartPath, DestPath)) == NOPOLY) {
// This should never happen!
// It's the old code that didn't always work.
endnode = NearestEndNode(StartPath, targetX, targetY);
@@ -1213,20 +1265,31 @@ static void SetOffWithinNodePath(PMACTOR pActor, HPOLYGON StartPath, HPOLYGON De
}
#if 1
- if ((pActor->npstatus == LEAVING) &&
- endnode == NearestEndNode(StartPath, pActor->objx, pActor->objy)) {
+ if ((pMover->npstatus == LEAVING) &&
+ endnode == NearestEndNode(StartPath, pMover->objX, pMover->objY)) {
// Leave it be
+
+ if (TinselV2) {
+ // Yeah, but we need a destination
+ // It's release night and there's this problem in the bar...
+ if (hIpath) // must be, but...
+ {
+ // could go for its end node if it's an NPATH
+ // but we probably will when we hit it anyway!
+ SetMoverDest(pMover, PolyCentreX(hIpath), PolyCentreY(hIpath));
+ }
+ }
} else
#endif
{
if (endnode) {
- getNpathNode(StartPath, pActor->line+1, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->npstatus = GOING_UP;
+ getNpathNode(StartPath, pMover->line+1, &nx, &ny);
+ SetMoverDest(pMover, nx, ny);
+ pMover->npstatus = GOING_UP;
} else {
- getNpathNode(StartPath, pActor->line, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
- pActor->npstatus = GOING_DOWN;
+ getNpathNode(StartPath, pMover->line, &nx, &ny);
+ SetMoverDest(pMover, nx, ny);
+ pMover->npstatus = GOING_DOWN;
}
}
}
@@ -1235,52 +1298,79 @@ static void SetOffWithinNodePath(PMACTOR pActor, HPOLYGON StartPath, HPOLYGON De
/**
* Restore a movement, called from restoreMovement() in ACTORS.CPP
*/
-void SSetActorDest(PMACTOR pActor) {
+void SSetActorDest(PMOVER pActor) {
if (pActor->UtargetX != -1 && pActor->UtargetY != -1) {
- stand(pActor->actorID, pActor->objx, pActor->objy, 0);
+ Stand(nullContext, pActor->actorID, pActor->objX, pActor->objY, 0);
if (pActor->UtargetX != -1 && pActor->UtargetY != -1) {
SetActorDest(pActor, pActor->UtargetX, pActor->UtargetY,
pActor->bIgPath, 0);
}
} else {
- stand(pActor->actorID, pActor->objx, pActor->objy, 0);
+ Stand(nullContext, pActor->actorID, pActor->objX, pActor->objY, 0);
}
}
/**
* Initiate a movement, called from WalkTo_Event()
*/
-void SetActorDest(PMACTOR pActor, int clickX, int clickY, bool igPath, SCNHANDLE film) {
+int SetActorDest(PMOVER pMover, int clickX, int clickY, bool igPath, SCNHANDLE hFilm) {
HPOLYGON StartPath, DestPath = 0;
int targetX, targetY;
- if (pActor->actorID == LeadId()) // Now only for lead actor
- UnTagActor(pActor->actorID); // Tag not allowed while moving
- pActor->ticket++;
- pActor->stop = false;
- pActor->over = false;
- pActor->fromx = pActor->objx;
- pActor->fromy = pActor->objy;
- pActor->bMoving = true;
- pActor->bIgPath = igPath;
-
- // Use the supplied reel or restore the normal actor.
- if (film != 0)
- AlterMActor(pActor, film, AR_WALKREEL);
- else
- AlterMActor(pActor, 0, AR_NORMAL);
+ if (TinselV2) {
+ // No need to synchronise if not moving!
+ // Hopefully will stop luggage flip in shades.
+ if (!MoverMoving(pMover))
+ pMover->stepCount = 0;
+
+ // Fix interrupted-walking-to-wardrobe bug in mortuary
+ StopMover(pMover);
+ } else {
+ if (pMover->actorID == GetLeadId()) // Now only for lead actor
+ UnTagActor(pMover->actorID); // Tag not allowed while moving
+ }
+
+ pMover->walkNumber++;
+ pMover->bStop = false;
+ pMover->over = false;
+ pMover->walkedFromX = pMover->objX;
+ pMover->walkedFromY = pMover->objY;
+ pMover->bMoving = true;
+ pMover->bIgPath = igPath;
+ pMover->zOverride = -1;
+ pMover->hRpath = NOPOLY;
+
+ if (!TinselV2) {
+ // Use the supplied reel or restore the normal actor.
+ if (hFilm != 0)
+ AlterMover(pMover, hFilm, AR_WALKREEL);
+ else
+ AlterMover(pMover, 0, AR_NORMAL);
+ }
if (igPath) {
targetX = clickX;
targetY = clickY;
+
+ if (pMover->actorID == GetLeadId()) {
+ lastLeadXdest = targetX;
+ lastLeadYdest = targetY;
+ }
} else {
- if (WorkOutDestination(clickX, clickY, &targetX, &targetY) == ALL_SORTED) {
- GotThere(pActor);
- return;
+ int wodResult = WorkOutDestination(clickX, clickY, &targetX, &targetY);
+
+ if (pMover->actorID == GetLeadId()) {
+ lastLeadXdest = targetX;
+ lastLeadYdest = targetY;
+ }
+
+ if (wodResult == ALL_SORTED) {
+ GotThere(pMover);
+ return 0;
}
assert(InPolygon(targetX, targetY, PATH) != NOPOLY); // illegal destination!
- assert(InPolygon(targetX, targetY, BLOCKING) == NOPOLY); // illegal destination!
+ assert(InPolygon(targetX, targetY, BLOCK) == NOPOLY); // illegal destination!
}
@@ -1289,9 +1379,9 @@ void SetActorDest(PMACTOR pActor, int clickX, int clickY, bool igPath, SCNHANDLE
/*----------------------------------
| Don't move if it's not worth it.
----------------------------------*/
- if (ABS(targetX - pActor->objx) < XMDIST && ABS(targetY - pActor->objy) < YMDIST) {
- GotThere(pActor);
- return;
+ if (ABS(targetX - pMover->objX) < XMDIST && ABS(targetY - pMover->objY) < YMDIST) {
+ GotThere(pMover);
+ return 0;
}
/*------------------------------------------------------
@@ -1302,41 +1392,57 @@ void SetActorDest(PMACTOR pActor, int clickX, int clickY, bool igPath, SCNHANDLE
DestPath = InPolygon(targetX, targetY, PATH);
if (PolySubtype(DestPath) == NODE) {
// Find the nearest point on a line, or nearest node
- FindBestPoint(DestPath, &targetX, &targetY, &pActor->Tline);
+ FindBestPoint(DestPath, &targetX, &targetY, &pMover->Tline);
}
}
- assert(pActor->bIgPath || InPolygon(targetX, targetY, PATH) != NOPOLY); // Error 5005
- SetMoverUltDest(pActor, targetX, targetY);
- SetMoverIntDest(pActor, targetX, targetY);
+ assert(pMover->bIgPath || InPolygon(targetX, targetY, PATH) != NOPOLY); // Error 5005
+ SetMoverUltDest(pMover, targetX, targetY);
+ SetMoverIntDest(pMover, targetX, targetY);
+
+ if (TinselV2) {
+ // No movement for unconnected paths
+ if (pMover->hIpath == NOPOLY && !igPath) {
+ GotThere(pMover);
+ return 0;
+ }
+
+ // Use the supplied reel or restore the normal actor.
+ if (hFilm != 0)
+ AlterMover(pMover, hFilm, AR_WALKREEL);
+ else
+ AlterMover(pMover, 0, AR_NORMAL);
+ }
/*-------------------------------------------------------------------
| If in a follow nodes path, need to set off in the right direction! |
-------------------------------------------------------------------*/
- if ((StartPath = pActor->hFnpath) != NOPOLY && !igPath) {
- SetOffWithinNodePath(pActor, StartPath, DestPath, targetX, targetY);
+ if ((StartPath = pMover->hFnpath) != NOPOLY && !igPath) {
+ SetOffWithinNodePath(pMover, StartPath, DestPath, targetX, targetY);
} else {
// Set off!
- SetNextDest(pActor);
+ SetNextDest(pMover);
}
+
+ return pMover->walkNumber;
}
/**
* Change scale if appropriate.
*/
-static void CheckScale(PMACTOR pActor, HPOLYGON hPath, int ypos) {
+static void CheckScale(PMOVER pActor, HPOLYGON hPath, int ypos) {
int scale;
scale = GetScale(hPath, ypos);
if (scale != pActor->scale) {
- SetMActorWalkReel(pActor, pActor->dirn, scale, false);
+ SetMoverWalkReel(pActor, pActor->direction, scale, false);
}
}
/**
* Not going anywhere - Kick off again if not at final destination.
*/
-static void NotMoving(PMACTOR pActor, int x, int y) {
+static void NotMoving(PMOVER pActor, int x, int y) {
pActor->targetX = pActor->targetY = -1;
// if (x == pActor->UtargetX && y == pActor->UtargetY)
@@ -1357,19 +1463,21 @@ static void NotMoving(PMACTOR pActor, int x, int y) {
/**
* Does the necessary business when entering a different path polygon.
*/
-static void EnteringNewPath(PMACTOR pActor, HPOLYGON hPath, int x, int y) {
+static void EnteringNewPath(PMOVER pMover, HPOLYGON hPath, int x, int y) {
int firstnode; // First node to go to
int lastnode; // Last node to go to
HPOLYGON hIpath;
+ HPOLYGON hLpath; // one we're leaving
int nx, ny;
int nxl, nyl;
- pActor->hCpath = hPath; // current path
+ hLpath = pMover->hCpath;
+ pMover->hCpath = hPath; // current path
if (hPath == NOPOLY) {
// Not proved this ever happens, but just in case
- pActor->hFnpath = NOPOLY;
- pActor->npstatus = NOT_IN;
+ pMover->hFnpath = NOPOLY;
+ pMover->npstatus = NOT_IN;
return;
}
@@ -1381,12 +1489,12 @@ static void EnteringNewPath(PMACTOR pActor, HPOLYGON hPath, int x, int y) {
// If this is not the destination path,
// find which end nodfe we wish to leave via
- if (hPath != pActor->hUpath) {
- if (pActor->bIgPath) {
- lastnode = NearestEndNode(hPath, pActor->UtargetX, pActor->UtargetY);
+ if (hPath != pMover->hUpath) {
+ if (pMover->bIgPath) {
+ lastnode = NearestEndNode(hPath, pMover->UtargetX, pMover->UtargetY);
} else {
- assert(pActor->hUpath != NOPOLY); // Error 703
- hIpath = getPathOnTheWay(hPath, pActor->hUpath);
+ assert(pMover->hUpath != NOPOLY); // Error 703
+ hIpath = GetPathOnTheWay(hPath, pMover->hUpath);
assert(hIpath != NOPOLY); // No path on the way
if (PolySubtype(hIpath) != NODE) {
@@ -1406,68 +1514,76 @@ static void EnteringNewPath(PMACTOR pActor, HPOLYGON hPath, int x, int y) {
// If leaving by same node as entering, don't bother.
if (lastnode == firstnode) {
- pActor->hFnpath = NOPOLY;
- pActor->npstatus = NOT_IN;
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5007
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
- SetNextDest(pActor);
+ pMover->hFnpath = NOPOLY;
+ pMover->npstatus = NOT_IN;
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5007
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ SetNextDest(pMover);
} else {
// Head for first node
- pActor->over = true;
- pActor->npstatus = ENTERING;
- pActor->hFnpath = hPath;
- pActor->line = firstnode ? firstnode - 1 : firstnode;
- if (pActor->line == pActor->Tline && hPath == pActor->hUpath) {
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5008
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
- SetMoverDest(pActor, pActor->UtargetX, pActor->UtargetY);
+ pMover->over = true;
+ pMover->npstatus = ENTERING;
+ pMover->hFnpath = hPath;
+ pMover->line = firstnode ? firstnode - 1 : firstnode;
+ if (pMover->line == pMover->Tline && hPath == pMover->hUpath) {
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5008
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
} else {
// This doesn't seem right
getNpathNode(hPath, firstnode, &nx, &ny);
- if (ABS(pActor->objx - nx) < XMDIST
- && ABS(pActor->objy - ny) < YMDIST) {
- pActor->npstatus = ENTERING;
- pActor->hFnpath = hPath;
- SetNextDest(pActor);
+ if (ABS(pMover->objX - nx) < XMDIST
+ && ABS(pMover->objY - ny) < YMDIST) {
+ pMover->npstatus = ENTERING;
+ pMover->hFnpath = hPath;
+ SetNextDest(pMover);
} else {
getNpathNode(hPath, firstnode, &nx, &ny);
- SetMoverDest(pActor, nx, ny);
+ SetMoverDest(pMover, nx, ny);
}
}
}
return;
} else {
- pActor->hFnpath = NOPOLY;
- pActor->npstatus = NOT_IN;
- assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5009
-// Added 26/01/95
- if (IsPolyCorner(hPath, pActor->ItargetX, pActor->ItargetY))
+ pMover->hFnpath = NOPOLY;
+ pMover->npstatus = NOT_IN;
+ assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5009
+
+ // Added 26/01/95
+ if (IsPolyCorner(hPath, pMover->ItargetX, pMover->ItargetY))
return;
- SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
- SetNextDest(pActor);
+ // Added 23/10/96
+ if (TinselV2 && (pMover->hRpath == hPath))
+ return;
+
+ if (TinselV2)
+ pMover->hRpath = hLpath;
+ SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
+ SetNextDest(pMover);
}
}
/**
* Move
*/
-void Move(PMACTOR pActor, int newx, int newy, HPOLYGON hPath) {
- MultiSetAniXY(pActor->actorObj, newx, newy);
- MAsetZPos(pActor, newy, getPolyZfactor(hPath));
- if (StepAnimScript(&pActor->actorAnim) == ScriptFinished) {
+void Move(PMOVER pMover, int newx, int newy, HPOLYGON hPath) {
+ pMover->objX = newx;
+ pMover->objY = newy;
+
+ MultiSetAniXY(pMover->actorObj, newx, newy);
+ SetMoverZ(pMover, newy, GetPolyZfactor(hPath));
+ if (StepAnimScript(&pMover->actorAnim) == ScriptFinished) {
// The end of a scale-change reel
// Revert to normal walking reel
- pActor->walkReel = false;
- pActor->scount = 0;
- SetMActorWalkReel(pActor, pActor->dirn, pActor->scale, true);
+ pMover->bWalkReel = false;
+ pMover->stepCount = 0;
+ SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true);
}
- pActor->objx = newx;
- pActor->objy = newy;
// Synchronised walking reels
- if (++pActor->scount >= 6)
- pActor->scount = 0;
+ if (++pMover->stepCount >= STEPS_MAX)
+ pMover->stepCount = 0;
}
/**
@@ -1475,24 +1591,25 @@ void Move(PMACTOR pActor, int newx, int newy, HPOLYGON hPath) {
*
* Moves the actor as appropriate.
*/
-void MoveActor(PMACTOR pActor) {
+void MoveActor(PMOVER pMover) {
int newx, newy;
HPOLYGON hPath;
int status, s2; // s2 not used here!
HPOLYGON hS2p; // nor is s2p!
HPOLYGON hEb;
- PMACTOR ma;
+ PMOVER ma;
int sTargetX, sTargetY;
bool bNewPath = false;
// Only do anything if the actor needs to move!
- if (pActor->targetX == -1 && pActor->targetY == -1)
+ if (pMover->targetX == -1 && pMover->targetY == -1)
return;
- if (pActor->stop) {
- GotThere(pActor);
- pActor->stop = false;
- SetMActorStanding(pActor);
+ if (pMover->bStop) {
+ GotThere(pMover);
+ pMover->bStop = false;
+ pMover->walkNumber++;
+ SetMoverStanding(pMover);
return;
}
@@ -1502,29 +1619,31 @@ void MoveActor(PMACTOR pActor) {
/**/ BogusVar = 0; //
#endif
- // During swalk()s, movement while hidden may be slowed down.
- if (pActor->aHidden) {
- if (++hSlowVar < pActor->SlowFactor)
- return;
- hSlowVar = 0;
+ if (!TinselV2) {
+ // During swalk()s, movement while hidden may be slowed down.
+ if (pMover->bHidden) {
+ if (++hSlowVar < pMover->SlowFactor)
+ return;
+ hSlowVar = 0;
+ }
}
// 'push' the target
- sTargetX = pActor->targetX;
- sTargetY = pActor->targetY;
+ sTargetX = pMover->targetX;
+ sTargetY = pMover->targetY;
- NewCoOrdinates(pActor->objx, pActor->objy, &pActor->targetX, &pActor->targetY,
- &newx, &newy, &status, &s2, &hS2p, pActor->over, false, pActor);
+ NewCoOrdinates(pMover->objX, pMover->objY, &pMover->targetX, &pMover->targetY,
+ &newx, &newy, &status, &s2, &hS2p, pMover->over, false, pMover);
- if (newx == pActor->objx && newy == pActor->objy) {
+ if (newx == pMover->objX && newy == pMover->objY) {
// 'pop' the target
- pActor->targetX = sTargetX;
- pActor->targetY = sTargetY;
+ pMover->targetX = sTargetX;
+ pMover->targetY = sTargetY;
- NewCoOrdinates(pActor->objx, pActor->objy, &pActor->targetX, &pActor->targetY, &newx, &newy,
- &status, &s2, &hS2p, pActor->over, true, pActor);
- if (newx == pActor->objx && newy == pActor->objy) {
- NotMoving(pActor, newx, newy);
+ NewCoOrdinates(pMover->objX, pMover->objY, &pMover->targetX, &pMover->targetY, &newx, &newy,
+ &status, &s2, &hS2p, pMover->over, true, pMover);
+ if (newx == pMover->objX && newy == pMover->objY) {
+ NotMoving(pMover, newx, newy);
return;
}
}
@@ -1532,43 +1651,43 @@ void MoveActor(PMACTOR pActor) {
// Find out which path we're in now
hPath = InPolygon(newx, newy, PATH);
if (hPath == NOPOLY) {
- if (pActor->bNoPath) {
- Move(pActor, newx, newy, pActor->hCpath);
+ if (pMover->bNoPath) {
+ Move(pMover, newx, newy, pMover->hCpath);
return;
} else {
// May be marginally outside!
// OR bIgPath may be set.
- hPath = pActor->hCpath;
+ hPath = pMover->hCpath;
}
- } else if (pActor->bNoPath) {
- pActor->bNoPath = false;
+ } else if (pMover->bNoPath) {
+ pMover->bNoPath = false;
bNewPath = true;
- } else if (hPath != pActor->hCpath) {
- if (IsInPolygon(newx, newy, pActor->hCpath))
- hPath = pActor->hCpath;
+ } else if (hPath != pMover->hCpath) {
+ if (IsInPolygon(newx, newy, pMover->hCpath))
+ hPath = pMover->hCpath;
}
- CheckScale(pActor, hPath, newy);
+ CheckScale(pMover, hPath, newy);
/*
* Must stay out of moving actors' blocking polygons.
*/
- ma = InMActorBlock(pActor, newx, newy);
+ ma = InMoverBlock(pMover, newx, newy);
if (ma != NULL) {
// Stop if there's no chance of arriving
- if (InMActorBlock(pActor, pActor->UtargetX, pActor->UtargetY)) {
- GotThere(pActor);
+ if (InMoverBlock(pMover, pMover->UtargetX, pMover->UtargetY)) {
+ GotThere(pMover);
return;
}
- if (InMActorBlock(pActor, pActor->objx, pActor->objy))
+ if (InMoverBlock(pMover, pMover->objX, pMover->objY))
;
else {
- hEb = InitExtraBlock(pActor, ma);
- newx = pActor->objx;
- newy = pActor->objy;
- BlockingCorner(hEb, &newx, &newy, pActor->ItargetX, pActor->ItargetY);
- SetMoverDest(pActor, newx, newy);
+ hEb = InitExtraBlock(pMover, ma);
+ newx = pMover->objX;
+ newy = pMover->objY;
+ BlockingCorner(hEb, &newx, &newy, pMover->ItargetX, pMover->ItargetY);
+ SetMoverDest(pMover, newx, newy);
return;
}
}
@@ -1576,11 +1695,11 @@ void MoveActor(PMACTOR pActor) {
/*--------------------------------------
This is where it actually gets moved.
--------------------------------------*/
- Move(pActor, newx, newy, hPath);
+ Move(pMover, newx, newy, hPath);
// Entering a new path polygon?
- if (hPath != pActor->hCpath || bNewPath)
- EnteringNewPath(pActor, hPath, newx, newy);
+ if (hPath != pMover->hCpath || bNewPath)
+ EnteringNewPath(pMover, hPath, newx, newy);
}
/**
@@ -1590,25 +1709,36 @@ void SetDefaultRefer(int32 defRefer) {
DefaultRefer = defRefer;
}
+int GetLastLeadXdest(void) {
+ return lastLeadXdest;
+}
+
+int GetLastLeadYdest(void) {
+ return lastLeadYdest;
+}
+
+
+
+
/**
* DoMoveActor
*/
-void DoMoveActor(PMACTOR pActor) {
+void DoMoveActor(PMOVER pActor) {
int wasx, wasy;
int i;
#define NUMBER 1
- wasx = pActor->objx;
- wasy = pActor->objy;
+ wasx = pActor->objX;
+ wasy = pActor->objY;
MoveActor(pActor);
if ((pActor->targetX != -1 || pActor->targetY != -1)
- && (wasx == pActor->objx && wasy == pActor->objy)) {
+ && (wasx == pActor->objX && wasy == pActor->objY)) {
for (i=0; i < NUMBER; i++) {
MoveActor(pActor);
- if (wasx != pActor->objx || wasy != pActor->objy)
+ if (wasx != pActor->objX || wasy != pActor->objY)
break;
}
// assert(i<NUMBER);
diff --git a/engines/tinsel/move.h b/engines/tinsel/move.h
index 2c5f2cfe73..9ab703839f 100644
--- a/engines/tinsel/move.h
+++ b/engines/tinsel/move.h
@@ -27,17 +27,24 @@
#define TINSEL_MOVE_H
#include "tinsel/dw.h" // for SCNHANDLE
+#include "tinsel/rince.h"
namespace Tinsel {
-struct MACTOR;
+struct MOVER;
-void SetActorDest(MACTOR *pActor, int x, int y, bool igPath, SCNHANDLE film);
-void SSetActorDest(MACTOR *pActor);
-void DoMoveActor(MACTOR *pActor);
+typedef enum { YB_X2, YB_X1_5 } YBIAS;
+
+int SetActorDest(MOVER *pMover, int x, int y, bool igPath, SCNHANDLE film);
+void SSetActorDest(MOVER *pActor);
+void DoMoveActor(MOVER *pMover);
void SetDefaultRefer(int32 defRefer);
+int GetLastLeadXdest(void);
+int GetLastLeadYdest(void);
+DIRECTION GetDirection(int fromx, int fromy, int tox, int toy, DIRECTION lastreel,
+ HPOLYGON hPath, YBIAS yBias = YB_X2);
} // end of namespace Tinsel
#endif /* TINSEL_MOVE_H */
diff --git a/engines/tinsel/multiobj.cpp b/engines/tinsel/multiobj.cpp
index c60592069f..34f7d3ec00 100644
--- a/engines/tinsel/multiobj.cpp
+++ b/engines/tinsel/multiobj.cpp
@@ -27,6 +27,7 @@
#include "tinsel/multiobj.h"
#include "tinsel/handle.h"
#include "tinsel/object.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -204,14 +205,17 @@ void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
if (deltaX == 0 && deltaY == 0)
return; // ignore no change
- if (pMultiObj->flags & DMA_FLIPH) {
- // image is flipped horizontally - flip the x direction
- deltaX = -deltaX;
- }
+ if (!TinselV2) {
+ // *** This may be wrong!!!
+ if (pMultiObj->flags & DMA_FLIPH) {
+ // image is flipped horizontally - flip the x direction
+ deltaX = -deltaX;
+ }
- if (pMultiObj->flags & DMA_FLIPV) {
- // image is flipped vertically - flip the y direction
- deltaY = -deltaY;
+ if (pMultiObj->flags & DMA_FLIPV) {
+ // image is flipped vertically - flip the y direction
+ deltaY = -deltaY;
+ }
}
// for all the objects that make up this multi-part
@@ -530,4 +534,32 @@ int MultiLowest(OBJECT *pMulti) {
return lowest - 1;
}
+/**
+ * Returns TRUE if the object currently has an image.
+ * @param pMulti Multi-part object
+ */
+
+bool MultiHasShape(POBJECT pMulti) {
+ return (pMulti->hShape != 0);
+}
+
+/**
+ * Bodge for text on movies. Makes sure it appears for it's lifetime.
+ * @param pMultiObj Multi-part object to be adjusted
+ */
+
+void MultiForceRedraw(POBJECT pMultiObj) {
+ // validate object pointer
+ assert(pMultiObj >= objectList && pMultiObj <= objectList + NUM_OBJECTS - 1);
+
+ // for all the objects that make up this multi-part
+ do {
+ // signal a change in the object
+ pMultiObj->flags |= DMA_CHANGED;
+
+ // next obj in list
+ pMultiObj = pMultiObj->pSlave;
+ } while (pMultiObj != NULL);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/multiobj.h b/engines/tinsel/multiobj.h
index 6d25600ea2..9bf1579363 100644
--- a/engines/tinsel/multiobj.h
+++ b/engines/tinsel/multiobj.h
@@ -28,6 +28,7 @@
#define TINSEL_MULTIOBJ_H
#include "tinsel/dw.h"
+#include "tinsel/object.h"
namespace Tinsel {
@@ -45,7 +46,9 @@ struct MULTI_INIT {
int32 mulX; //!< multi-objects initial x ani position
int32 mulY; //!< multi-objects initial y ani position
int32 mulZ; //!< multi-objects initial z position
+ uint32 otherFlags; //!< multi-objects Tinsel 2 - other flags
} PACKED_STRUCT;
+typedef MULTI_INIT *PMULTI_INIT;
#include "common/pack-end.h" // END STRUCT PACKING
@@ -119,6 +122,12 @@ int MultiHighest( // Returns the highest point of a multi-part object
int MultiLowest( // Returns the lowest point of a multi-part object
OBJECT *pMulti); // multi-part object
+bool MultiHasShape( // Returns TRUE if the object currently has an image
+ POBJECT pMulti); // multi-part object
+
+void MultiForceRedraw(
+ POBJECT pMultiObj); // multi-part object to be forced
+
} // end of namespace Tinsel
#endif // TINSEL_MULTIOBJ_H
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index d165ba0a10..979fc61270 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -31,12 +31,21 @@
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "sound/audiocd.h"
+#include "sound/adpcm.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "tinsel/config.h"
#include "tinsel/sound.h"
#include "tinsel/music.h"
+#include "tinsel/handle.h"
+#include "tinsel/sysvar.h"
+
+#define MUSIC_JUMP (-1)
+#define MUSIC_END (-2)
+#define BLMAGIC (-3458)
+
+#define DIM_SPEED 8
namespace Tinsel {
@@ -153,8 +162,8 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
currentMidi = dwFileOffset;
currentLoop = bLoop;
- if (volMidi != 0) {
- SetMidiVolume(volMidi);
+ if (volMusic != 0) {
+ SetMidiVolume(volMusic);
// Support for compressed music from the music enhancement project
AudioCD.stop();
@@ -191,7 +200,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
- error("Cannot find file %s", MIDI_FILE);
+ error(CANNOT_FIND_FILE, MIDI_FILE);
// update index of last tune loaded
dwLastMidiIndex = dwMidiIndex;
@@ -206,22 +215,22 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
assert(dwSeqLen > 0 && dwSeqLen <= midiBuffer.size);
// stop any currently playing tune
- _vm->_music->stop();
+ _vm->_midiMusic->stop();
// read the sequence
if (midiStream.read(midiBuffer.pDat, dwSeqLen) != dwSeqLen)
- error("File %s is corrupt", MIDI_FILE);
+ error(FILE_IS_CORRUPT, MIDI_FILE);
midiStream.close();
- _vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
+ _vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
// Store the length
dwLastSeqLen = dwSeqLen;
} else {
// dwMidiIndex == dwLastMidiIndex
- _vm->_music->stop();
- _vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
+ _vm->_midiMusic->stop();
+ _vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
}
// allow another sequence to play
@@ -235,7 +244,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
*/
bool MidiPlaying(void) {
if (AudioCD.isPlaying()) return true;
- return _vm->_music->isPlaying();
+ return _vm->_midiMusic->isPlaying();
}
/**
@@ -246,7 +255,7 @@ bool StopMidi(void) {
currentLoop = false;
AudioCD.stop();
- _vm->_music->stop();
+ _vm->_midiMusic->stop();
return true;
}
@@ -255,7 +264,7 @@ bool StopMidi(void) {
* Gets the volume of the MIDI music.
*/
int GetMidiVolume() {
- return volMidi;
+ return volMusic;
}
/**
@@ -265,24 +274,24 @@ int GetMidiVolume() {
void SetMidiVolume(int vol) {
assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume);
- if (vol == 0 && volMidi == 0) {
+ if (vol == 0 && volMusic == 0) {
// Nothing to do
- } else if (vol == 0 && volMidi != 0) {
+ } else if (vol == 0 && volMusic != 0) {
// Stop current midi sequence
AudioCD.stop();
StopMidi();
- } else if (vol != 0 && volMidi == 0) {
+ } else if (vol != 0 && volMusic == 0) {
// Perhaps restart last midi sequence
if (currentLoop) {
PlayMidiSequence(currentMidi, true);
- _vm->_music->setVolume(vol);
+ _vm->_midiMusic->setVolume(vol);
}
- } else if (vol != 0 && volMidi != 0) {
+ } else if (vol != 0 && volMusic != 0) {
// Alter current volume
- _vm->_music->setVolume(vol);
+ _vm->_midiMusic->setVolume(vol);
}
- volMidi = vol;
+ volMusic = vol;
}
/**
@@ -292,7 +301,7 @@ void OpenMidiFiles(void) {
Common::File midiStream;
// Demo version has no midi file
- if (_vm->getFeatures() & GF_DEMO)
+ if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2))
return;
if (midiBuffer.pDat)
@@ -301,12 +310,12 @@ void OpenMidiFiles(void) {
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
- error("Cannot find file %s", MIDI_FILE);
+ error(CANNOT_FIND_FILE, MIDI_FILE);
// gen length of the largest sequence
midiBuffer.size = midiStream.readUint32LE();
if (midiStream.ioFailed())
- error("File %s is corrupt", MIDI_FILE);
+ error(FILE_IS_CORRUPT, MIDI_FILE);
if (midiBuffer.size) {
// allocate a buffer big enough for the largest MIDI sequence
@@ -327,14 +336,14 @@ void DeleteMidiBuffer() {
midiBuffer.pDat = NULL;
}
-MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false) {
+MidiMusicPlayer::MidiMusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
this->open();
_xmidiParser = MidiParser::createParser_XMIDI();
}
-MusicPlayer::~MusicPlayer() {
+MidiMusicPlayer::~MidiMusicPlayer() {
_driver->setTimerCallback(NULL, NULL);
stop();
this->close();
@@ -342,7 +351,7 @@ MusicPlayer::~MusicPlayer() {
delete _xmidiParser;
}
-void MusicPlayer::setVolume(int volume) {
+void MidiMusicPlayer::setVolume(int volume) {
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
if (_masterVolume == volume)
@@ -359,7 +368,7 @@ void MusicPlayer::setVolume(int volume) {
}
}
-int MusicPlayer::open() {
+int MidiMusicPlayer::open() {
// Don't ever call open without first setting the output driver!
if (!_driver)
return 255;
@@ -372,14 +381,14 @@ int MusicPlayer::open() {
return 0;
}
-void MusicPlayer::close() {
+void MidiMusicPlayer::close() {
stop();
if (_driver)
_driver->close();
_driver = 0;
}
-void MusicPlayer::send(uint32 b) {
+void MidiMusicPlayer::send(uint32 b) {
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
@@ -409,7 +418,7 @@ void MusicPlayer::send(uint32 b) {
}
}
-void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
+void MidiMusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
switch (type) {
case 0x2F: // End of Track
if (_looping)
@@ -423,15 +432,15 @@ void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
}
}
-void MusicPlayer::onTimer(void *refCon) {
- MusicPlayer *music = (MusicPlayer *)refCon;
+void MidiMusicPlayer::onTimer(void *refCon) {
+ MidiMusicPlayer *music = (MidiMusicPlayer *)refCon;
Common::StackLock lock(music->_mutex);
if (music->_isPlaying)
music->_parser->onTimer();
}
-void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
+void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
if (_isPlaying)
return;
@@ -463,7 +472,7 @@ void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
}
}
-void MusicPlayer::stop() {
+void MidiMusicPlayer::stop() {
Common::StackLock lock(_mutex);
_isPlaying = false;
@@ -473,16 +482,414 @@ void MusicPlayer::stop() {
}
}
-void MusicPlayer::pause() {
+void MidiMusicPlayer::pause() {
setVolume(-1);
_isPlaying = false;
}
-void MusicPlayer::resume() {
+void MidiMusicPlayer::resume() {
setVolume(GetMidiVolume());
_isPlaying = true;
}
+PCMMusicPlayer::PCMMusicPlayer() {
+ _silenceSamples = 0;
+
+ _curChunk = 0;
+ _fileName = 0;
+ _state = S_IDLE;
+ _mState = S_IDLE;
+ _scriptNum = -1;
+ _scriptIndex = 0;
+ _forcePlay = false;
+
+ _volume = 255;
+ _dimmed = false;
+ _dimmedTinsel = false;
+ _dimIteration = 0;
+
+ _fadeOutVolume = 0;
+ _fadeOutIteration = 0;
+
+ _end = true;
+
+ _vm->_mixer->playInputStream(Audio::Mixer::kMusicSoundType,
+ &_handle, this, -1, _volume, 0, false, true);
+}
+
+PCMMusicPlayer::~PCMMusicPlayer() {
+ _vm->_mixer->stopHandle(_handle);
+
+ delete[] _fileName;
+}
+
+void PCMMusicPlayer::startPlay(int id) {
+ if (!_fileName)
+ return;
+
+ debugC(DEBUG_DETAILED, kTinselDebugMusic, "Playing PCM music %s, index %d", _fileName, id);
+
+ Common::StackLock slock(_mutex);
+
+ stop();
+
+ _scriptNum = id;
+ _scriptIndex = 1;
+ _state = S_NEW;
+
+ play();
+}
+
+void PCMMusicPlayer::stopPlay() {
+ Common::StackLock slock(_mutex);
+
+ stop();
+}
+
+int PCMMusicPlayer::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock slock(_mutex);
+
+ if (!_curChunk && ((_state == S_IDLE) || (_state == S_STOP)))
+ return 0;
+
+ int samplesLeft = numSamples;
+
+ while (samplesLeft > 0) {
+ if (_silenceSamples > 0) {
+ int n = MIN(_silenceSamples, samplesLeft);
+
+ memset(buffer, 0, n);
+
+ buffer += n;
+ _silenceSamples -= n;
+ samplesLeft -= n;
+
+ } else if (_curChunk &&
+ ((_state == S_MID) || (_state == S_NEXT) || (_state == S_NEW))) {
+ int n = _curChunk->readBuffer(buffer, samplesLeft);
+
+ buffer += n;
+ samplesLeft -= n;
+
+ if (_curChunk->endOfData()) {
+ _state = S_END1;
+
+ delete _curChunk;
+ _curChunk = 0;
+ }
+ } else {
+
+ if (!getNextChunk())
+ break;
+ }
+ }
+
+ return (numSamples - samplesLeft);
+}
+
+bool PCMMusicPlayer::isPlaying() const {
+ return ((_state != S_IDLE) && (_state != S_STOP));
+}
+
+bool PCMMusicPlayer::isDimmed() const {
+ return _dimmed;
+}
+
+void PCMMusicPlayer::getTunePlaying(void *voidPtr, int length) {
+ Common::StackLock lock(_mutex);
+
+ debugC(DEBUG_DETAILED, kTinselDebugMusic, "getTunePlaying");
+
+ assert(length == (3 * sizeof(int32)));
+
+ int32 *p = (int32 *) voidPtr;
+
+ _mState = _state;
+
+ p[0] = (int32) _mState;
+ p[1] = _scriptNum;
+ p[2] = _scriptIndex;
+}
+
+void PCMMusicPlayer::restoreThatTune(void *voidPtr) {
+ Common::StackLock lock(_mutex);
+
+ debugC(DEBUG_DETAILED, kTinselDebugMusic, "restoreThatTune");
+
+ int32 *p = (int32 *) voidPtr;
+
+ _mState = (State) p[0];
+ _scriptNum = p[1];
+ _scriptIndex = p[2];
+
+ if (_mState != S_IDLE)
+ _state = S_NEW;
+
+ delete _curChunk;
+ _curChunk = 0;
+
+ _end = false;
+}
+
+void PCMMusicPlayer::setMusicSceneDetails(SCNHANDLE hScript,
+ SCNHANDLE hSegment, const char *fileName) {
+
+ Common::StackLock lock(_mutex);
+
+ stop();
+
+ debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Setting music scene details: %s", fileName);
+
+ _hScript = hScript;
+ _hSegment = hSegment;
+ _fileName = new char[strlen(fileName) + 1];
+ strcpy(_fileName, fileName);
+
+ // Start scene with music not dimmed
+ _dimmed = false;
+ _dimmedTinsel = false;
+ _dimIteration = 0;
+ setVol(255);
+}
+
+void PCMMusicPlayer::setVolume(int volume) {
+ assert((volume >= 0) && (volume <= 100));
+
+ _dimmed = false;
+ setVol((volume * 255) / 100);
+}
+
+void PCMMusicPlayer::setVol(uint8 volume) {
+ _volume = volume;
+
+ _vm->_mixer->setChannelVolume(_handle, _volume);
+}
+
+bool PCMMusicPlayer::getMusicTinselDimmed() const {
+ return _dimmedTinsel;
+}
+
+void PCMMusicPlayer::dim(bool bTinselDim) {
+ if (_dimmed || (_volume == 0) ||
+ (_state == S_IDLE) || !_curChunk || (SysVar(SV_MUSICDIMFACTOR) == 0))
+ return;
+
+ _dimmed = true;
+ if (bTinselDim)
+ _dimmedTinsel = true;
+
+ _dimmedVolume = _volume - (_volume / SysVar(SV_MUSICDIMFACTOR));
+
+ // Iterate down, negative iteration
+ if (!_dimIteration)
+ _dimPosition = _volume;
+ _dimIteration = (_dimmedVolume - _volume)/DIM_SPEED;
+
+ debugC(DEBUG_DETAILED, kTinselDebugMusic, "Dimming music from %d to %d, steps %d", _dimPosition, _dimmedVolume, _dimIteration);
+
+ // And SFX
+ if (SysVar(SYS_SceneFxDimFactor))
+ _vm->_sound->setSFXVolumes(255 - 255/SysVar(SYS_SceneFxDimFactor));
+}
+
+void PCMMusicPlayer::unDim(bool bTinselUnDim) {
+ if (!_dimmed || (_dimmedTinsel && !bTinselUnDim))
+ return; // not dimmed
+
+ _dimmed = _dimmedTinsel = false;
+
+ if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
+ return;
+
+ // Iterate up, positive iteration
+ if (!_dimIteration)
+ _dimPosition = _dimmedVolume;
+ _dimIteration = (_volume - _dimmedVolume)/DIM_SPEED;
+
+ debugC(DEBUG_DETAILED, kTinselDebugMusic, "UnDimming music from %d to %d, steps %d", _dimPosition, _volume, _dimIteration);
+
+ // And SFX
+ _vm->_sound->setSFXVolumes(255);
+}
+
+void PCMMusicPlayer::dimIteration() {
+ if (_dimIteration != 0)
+ {
+ _dimPosition += _dimIteration;
+ if (_dimPosition >= _volume)
+ {
+ _dimPosition = _volume;
+ _dimIteration = 0;
+ }
+ else if (_dimPosition <= _dimmedVolume)
+ {
+ _dimPosition = _dimmedVolume;
+ _dimIteration = 0;
+ }
+
+ _vm->_mixer->setChannelVolume(_handle, _dimPosition);
+ }
+}
+
+void PCMMusicPlayer::startFadeOut(int ticks) {
+ if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
+ return;
+
+ debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Fading out music...");
+
+ if (_dimmed) {
+ // Start from dimmed volume and go from there
+ _dimmed = false;
+ _fadeOutVolume = _volume - _volume/SysVar(SV_MUSICDIMFACTOR);
+ } else
+ _fadeOutVolume = _volume;
+
+ assert(ticks != 0);
+ _fadeOutIteration = _fadeOutVolume / ticks;
+
+ fadeOutIteration();
+}
+
+void PCMMusicPlayer::fadeOutIteration() {
+ if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
+ return;
+
+ _fadeOutVolume = CLIP<int>(_fadeOutVolume -= _fadeOutIteration, 0, 255);
+
+ _vm->_mixer->setChannelVolume(_handle, _fadeOutVolume);
+}
+
+bool PCMMusicPlayer::getNextChunk() {
+ MusicSegment *musicSegments;
+ int32 *script, *scriptBuffer;
+ int id;
+ int snum;
+ uint32 sampleOffset, sampleLength, sampleCLength;
+ Common::File f;
+ byte *buffer;
+ Common::MemoryReadStream *sampleStream;
+
+ switch (_state) {
+ case S_NEW:
+ case S_NEXT:
+ _forcePlay = false;
+
+ script = scriptBuffer = (int32 *) LockMem(_hScript);
+
+ // Set parameters for this chunk of music
+ id = _scriptNum;
+ while(id--)
+ script = scriptBuffer + FROM_LE_32(*script);
+ snum = FROM_LE_32(script[_scriptIndex++]);
+
+ if (snum == MUSIC_JUMP || snum == MUSIC_END)
+ {
+ // Let usual code sort it out!
+ _scriptIndex--; // Undo increment
+ _forcePlay = true; // Force a Play
+ _state = S_END1; // 'Goto' S_END1
+ break;
+ }
+
+ musicSegments = (MusicSegment *) LockMem(_hSegment);
+
+ assert(FROM_LE_32(musicSegments[snum].numChannels) == 1);
+ assert(FROM_LE_32(musicSegments[snum].bitsPerSample) == 16);
+
+ sampleOffset = FROM_LE_32(musicSegments[snum].sampleOffset);
+ sampleLength = FROM_LE_32(musicSegments[snum].sampleLength);
+ sampleCLength = (((sampleLength + 63) & ~63)*33)/64;
+
+ if (!f.open(_fileName))
+ error(CANNOT_FIND_FILE, _fileName);
+
+ f.seek(sampleOffset);
+ if (f.ioFailed() || (uint32)f.pos() != sampleOffset)
+ error(FILE_IS_CORRUPT, _fileName);
+
+ buffer = (byte *) malloc(sampleCLength);
+ assert(buffer);
+
+ // read all of the sample
+ if (f.read(buffer, sampleCLength) != sampleCLength)
+ error(FILE_IS_CORRUPT, _fileName);
+
+ sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, true);
+
+ delete _curChunk;
+ _curChunk = makeADPCMStream(sampleStream, true, sampleCLength,
+ Audio::kADPCMTinsel8, 22050, 1, 32);
+
+ _state = S_MID;
+ return true;
+
+ case S_END1:
+ script = scriptBuffer = (int32 *) LockMem(_hScript);
+
+ id = _scriptNum;
+ while(id--)
+ script = scriptBuffer + FROM_LE_32(*script);
+
+ switch (script[_scriptIndex]) {
+
+ case MUSIC_END:
+ _state = S_END2;
+ break;
+
+ case MUSIC_JUMP:
+ _scriptIndex = script[++_scriptIndex];
+ // Fall through
+ default:
+ if (_forcePlay)
+ _state = S_NEW;
+ else
+ _state = S_NEXT;
+ _forcePlay = false;
+ break;
+ }
+
+ return true;
+
+ case S_END2:
+ _silenceSamples = 11025; // Half a second of silence
+ return true;
+
+ case S_END3:
+ stop();
+ _state = S_IDLE;
+ return false;
+
+ case S_IDLE:
+ return false;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void PCMMusicPlayer::play() {
+ if (_curChunk)
+ return;
+ if (_scriptNum == -1)
+ return;
+
+ _end = false;
+
+ getNextChunk();
+}
+
+void PCMMusicPlayer::stop() {
+ delete _curChunk;
+ _curChunk = 0;
+ _scriptNum = -1;
+ _state = S_IDLE;
+ _mState = S_IDLE;
+
+ _end = true;
+}
+
void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop) {
*pMidi = currentMidi;
*pLoop = currentLoop;
@@ -495,9 +902,9 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) {
currentMidi = Midi;
currentLoop = Loop;
- if (volMidi != 0 && Loop) {
+ if (volMusic != 0 && Loop) {
PlayMidiSequence(currentMidi, true);
- SetMidiVolume(volMidi);
+ SetMidiVolume(volMusic);
}
}
@@ -546,4 +953,4 @@ void dumpMusic() {
}
#endif
-} // End of namespace Made
+} // End of namespace Tinsel
diff --git a/engines/tinsel/music.h b/engines/tinsel/music.h
index 3d647f95bf..1909430c32 100644
--- a/engines/tinsel/music.h
+++ b/engines/tinsel/music.h
@@ -30,6 +30,8 @@
#include "sound/mididrv.h"
#include "sound/midiparser.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
#include "common/mutex.h"
namespace Tinsel {
@@ -58,11 +60,10 @@ SCNHANDLE GetTrackOffset(int trackNumber);
void dumpMusic();
-
-class MusicPlayer : public MidiDriver {
+class MidiMusicPlayer : public MidiDriver {
public:
- MusicPlayer(MidiDriver *driver);
- ~MusicPlayer();
+ MidiMusicPlayer(MidiDriver *driver);
+ ~MidiMusicPlayer();
bool isPlaying() { return _isPlaying; }
void setPlaying(bool playing) { _isPlaying = playing; }
@@ -71,6 +72,7 @@ public:
int getVolume() { return _masterVolume; }
void playXMIDI(byte *midiData, uint32 size, bool loop);
+
void stop();
void pause();
void resume();
@@ -111,6 +113,93 @@ protected:
byte _masterVolume;
};
-} // End of namespace Made
+class PCMMusicPlayer : public Audio::AudioStream {
+public:
+ PCMMusicPlayer();
+ ~PCMMusicPlayer();
+
+ bool isPlaying() const;
+
+ bool isDimmed() const;
+
+ void getTunePlaying(void *voidPtr, int length);
+ void restoreThatTune(void *voidPtr);
+
+ void setMusicSceneDetails(SCNHANDLE hScript, SCNHANDLE hSegment, const char *fileName);
+
+ void setVolume(int volume);
+
+ void startPlay(int id);
+ void stopPlay();
+
+ bool getMusicTinselDimmed() const;
+ void dim(bool bTinselDim);
+ void unDim(bool bTinselUnDim);
+ void dimIteration();
+
+ void startFadeOut(int ticks);
+ void fadeOutIteration();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return false; }
+ bool endOfData() const { return _end; }
+ bool endOfStream() const { return false; }
+ int getRate() const { return 22050; }
+
+protected:
+ enum State {
+ S_IDLE,
+ S_NEW,
+ S_MID,
+ S_END1,
+ S_END2,
+ S_END3,
+ S_NEXT,
+ S_STOP
+ };
+
+ struct MusicSegment {
+ uint32 numChannels;
+ uint32 bitsPerSec;
+ uint32 bitsPerSample;
+ uint32 sampleLength;
+ uint32 sampleOffset;
+ };
+
+ Audio::SoundHandle _handle;
+ Audio::AudioStream *_curChunk;
+ Common::Mutex _mutex;
+
+ bool _end;
+
+ int _silenceSamples;
+
+ State _state, _mState;
+ bool _forcePlay;
+ int32 _scriptNum;
+ int32 _scriptIndex;
+ SCNHANDLE _hScript;
+ SCNHANDLE _hSegment;
+ char *_fileName;
+
+ uint8 _volume;
+
+ bool _dimmed;
+ bool _dimmedTinsel;
+ uint8 _dimmedVolume;
+ int _dimIteration;
+ int _dimPosition;
+
+ uint8 _fadeOutVolume;
+ int _fadeOutIteration;
+
+ void play();
+ void stop();
+ void setVol(uint8 volume);
+
+ bool getNextChunk();
+};
+
+} // End of namespace Tinsel
#endif
diff --git a/engines/tinsel/object.cpp b/engines/tinsel/object.cpp
index 709fa4fad9..c9766631f9 100644
--- a/engines/tinsel/object.cpp
+++ b/engines/tinsel/object.cpp
@@ -29,14 +29,13 @@
#include "tinsel/cliprect.h" // object clip rect defs
#include "tinsel/graphics.h" // low level interface
#include "tinsel/handle.h"
+#include "tinsel/text.h"
+#include "tinsel/tinsel.h"
#define OID_EFFECTS 0x2000 // generic special effects object id
namespace Tinsel {
-/** screen clipping rectangle */
-static const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
-
// list of all objects
OBJECT *objectList = 0;
@@ -194,6 +193,7 @@ void InsertObject(OBJECT *pObjList, OBJECT *pInsObj) {
*/
void DelObject(OBJECT *pObjList, OBJECT *pDelObj) {
OBJECT *pPrev, *pObj; // object list traversal pointers
+ const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// validate object pointer
assert(pDelObj >= objectList && pDelObj <= objectList + NUM_OBJECTS - 1);
@@ -232,7 +232,9 @@ void DelObject(OBJECT *pObjList, OBJECT *pDelObj) {
}
// if we get to here - object has not been found on the list
- error("DelObject(): formally 'assert(0)!'");
+ // This can be triggered in Act 3 in DW1 while talking to the guard,
+ // so this has been turned to a warning instead of an error
+ warning("DelObject(): formally 'assert(0)!'");
}
@@ -313,7 +315,7 @@ void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) {
// we are flipped vertically
// set ani Y = -ani Y + height - 1
- *pAniY = -*pAniY + FROM_LE_16(pImg->imgHeight) - 1;
+ *pAniY = -*pAniY + (FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK) - 1;
}
} else
// null image
@@ -365,21 +367,25 @@ OBJECT *InitObject(const OBJ_INIT *pInitTbl) {
// get pointer to image
if (pInitTbl->hObjImg) {
int aniX, aniY; // objects animation offsets
- PALQ *pPalQ; // palette queue pointer
+ PALQ *pPalQ = NULL; // palette queue pointer
const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image
- // allocate a palette for this object
- pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal));
+ if (pImg->hImgPal) {
+ // allocate a palette for this object
+ pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal));
- // make sure palette allocated
- assert(pPalQ != NULL);
+ // make sure palette allocated
+ assert(pPalQ != NULL);
+ }
// assign palette to object
pObj->pPal = pPalQ;
// set objects size
pObj->width = FROM_LE_16(pImg->imgWidth);
- pObj->height = FROM_LE_16(pImg->imgHeight);
+ pObj->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK;
+ pObj->flags &= ~C16_FLAG_MASK;
+ pObj->flags |= FROM_LE_16(pImg->imgHeight) & C16_FLAG_MASK;
// set objects bitmap definition
pObj->hBits = FROM_LE_32(pImg->hImgBits);
@@ -434,7 +440,9 @@ void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) {
// setup new shape
pAniObj->width = FROM_LE_16(pNewImg->imgWidth);
- pAniObj->height = FROM_LE_16(pNewImg->imgHeight);
+ pAniObj->height = FROM_LE_16(pNewImg->imgHeight) & ~C16_FLAG_MASK;
+ newflags &= ~C16_FLAG_MASK;
+ newflags |= pNewImg->imgHeight & C16_FLAG_MASK;
// set objects bitmap definition
pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits);
diff --git a/engines/tinsel/object.h b/engines/tinsel/object.h
index 8b61571a3e..f77c113482 100644
--- a/engines/tinsel/object.h
+++ b/engines/tinsel/object.h
@@ -51,21 +51,24 @@ enum {
DMA_ABS = 0x0100, //!< position of object is absolute
DMA_CHANGED = 0x0200, //!< object has changed in some way since the last frame
DMA_USERDEF = 0x0400, //!< user defined flags start here
+ DMA_GHOST = 0x0080,
+
/** flags that effect an objects appearance */
DMA_HARDFLAGS = (DMA_WNZ | DMA_CNZ | DMA_CONST | DMA_WA | DMA_FLIPH | DMA_FLIPV | DMA_TRANS)
};
/** structure for image */
+#include "common/pack-start.h" // START STRUCT PACKING
struct IMAGE {
short imgWidth; //!< image width
- short imgHeight; //!< image height
+ unsigned short imgHeight; //!< image height
short anioffX; //!< image x animation offset
short anioffY; //!< image y animation offset
SCNHANDLE hImgBits; //!< image bitmap handle
SCNHANDLE hImgPal; //!< image palette handle
-};
-
+} PACKED_STRUCT;
+#include "common/pack-end.h" // END STRUCT PACKING
/** a multi-object animation frame is a list of multi-image handles */
typedef uint32 FRAME;
@@ -93,6 +96,7 @@ struct OBJECT {
SCNHANDLE hMirror; //!< objects previous animation frame
int oid; //!< object identifier
};
+typedef OBJECT *POBJECT;
#include "common/pack-start.h" // START STRUCT PACKING
diff --git a/engines/tinsel/palette.cpp b/engines/tinsel/palette.cpp
index 3bc2b514b5..afbe6555fc 100644
--- a/engines/tinsel/palette.cpp
+++ b/engines/tinsel/palette.cpp
@@ -28,6 +28,7 @@
#include "tinsel/graphics.h"
#include "tinsel/handle.h" // LockMem definition
#include "tinsel/palette.h" // palette allocator structures etc.
+#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
#include "common/system.h"
@@ -72,6 +73,17 @@ static VIDEO_DAC_Q *pDAChead;
/** the translucent palette lookup table */
uint8 transPalette[MAX_COLOURS]; // used in graphics.cpp
+uint8 ghostPalette[MAX_COLOURS];
+
+static int translucentIndex = 228;
+
+static int talkIndex = 233;
+
+static COLORREF talkColRef;
+
+static COLORREF tagColRef;
+
+
#ifdef DEBUG
// diagnostic palette counters
static int numPals = 0;
@@ -243,6 +255,10 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
p->hPal = hNewPal; // set hardware palette data
p->numColours = FROM_LE_32(pNewPal->numColours); // set number of colours in palette
+ if (TinselV2)
+ // Copy all the colours
+ memcpy(p->palRGB, pNewPal->palRGB, pNewPal->numColours * sizeof(COLORREF));
+
#ifdef DEBUG
// one more palette in use
if (++numPals > maxPals)
@@ -250,7 +266,10 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
#endif
// Q the change to the video DAC
- UpdateDACqueueHandle(p->posInDAC, p->numColours, p->hPal);
+ if (TinselV2)
+ UpdateDACqueue(p->posInDAC, pNewPal->numColours, p->palRGB);
+ else
+ UpdateDACqueueHandle(p->posInDAC, p->numColours, p->hPal);
// move all palettes after this one down (if necessary)
for (pPrev = p, pNxtPal = pPrev + 1; pNxtPal < palAllocData + NUM_PALETTES; pNxtPal++) {
@@ -265,9 +284,14 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
+ pPrev->numColours | PALETTE_MOVED;
// Q the palette change in position to the video DAC
- UpdateDACqueueHandle(pNxtPal->posInDAC,
- pNxtPal->numColours,
- pNxtPal->hPal);
+ if (!TinselV2)
+ UpdateDACqueueHandle(pNxtPal->posInDAC,
+ pNxtPal->numColours,
+ pNxtPal->hPal);
+ else if (!pNxtPal->bFading)
+ UpdateDACqueue(pNxtPal->posInDAC,
+ pNxtPal->numColours,
+ pNxtPal->palRGB);
// update previous palette to current palette
pPrev = pNxtPal;
@@ -347,10 +371,22 @@ void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) {
// install new palette
pPalQ->hPal = hNewPal;
- // Q the change to the video DAC
- UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), hNewPal);
+ if (TinselV2) {
+ pPalQ->numColours = pNewPal->numColours;
+
+ // Copy all the colours
+ memcpy(pPalQ->palRGB, pNewPal->palRGB, pNewPal->numColours * sizeof(COLORREF));
+
+ if (!pPalQ->bFading)
+ // Q the change to the video DAC
+ UpdateDACqueue(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), pPalQ->palRGB);
+ } else {
+ // Q the change to the video DAC
+ UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), hNewPal);
+ }
} else {
// # colours are different - will have to update all following palette entries
+ assert(!TinselV2); // Fatal error for Tinsel 2
PALQ *pNxtPalQ; // next palette queue position
@@ -411,6 +447,33 @@ void SetBgndColour(COLORREF colour) {
}
/**
+ * Note whether a palette is being faded.
+ * @param pPalQ Palette queue position
+ * @param bFading Whether it is fading
+ */
+void FadingPalette(PPALQ pPalQ, bool bFading) {
+ // validate palette Q pointer
+ assert(pPalQ >= palAllocData && pPalQ <= palAllocData + NUM_PALETTES - 1);
+
+ // validate that this is a change
+ assert(pPalQ->bFading != bFading);
+
+ pPalQ->bFading = bFading;
+}
+
+/**
+ * All fading processes have just been killed, so none of the
+ * palettes are fading.
+ */
+void NoFadingPalettes(void) {
+ PPALQ pPalQ;
+
+ for (pPalQ = palAllocData; pPalQ <= palAllocData + NUM_PALETTES - 1; pPalQ++) {
+ pPalQ->bFading = false;
+ }
+}
+
+/**
* Builds the translucent palette from the current backgrounds palette.
* @param hPalette Handle to current background palette
*/
@@ -433,8 +496,134 @@ void CreateTranslucentPalette(SCNHANDLE hPalette) {
// map the Value field to one of the 4 colours reserved for the translucent palette
val /= 63;
- transPalette[i + 1] = (uint8)((val == 0) ? 0 : val + COL_HILIGHT - 1);
+ transPalette[i + 1] = (uint8)((val == 0) ? 0 : val +
+ (TinselV2 ? TranslucentColour() : COL_HILIGHT) - 1);
+ }
+}
+
+/**
+ * Creates the ghost palette
+ */
+void CreateGhostPalette(SCNHANDLE hPalette) {
+ // get a pointer to the palette
+ PALETTE *pPal = (PALETTE *)LockMem(hPalette);
+ int i;
+
+ // leave background colour alone
+ ghostPalette[0] = 0;
+
+ for (i = 0; i < pPal->numColours; i++) {
+ // get the RGB colour model values
+ uint8 red = GetRValue(pPal->palRGB[i]);
+ uint8 green = GetGValue(pPal->palRGB[i]);
+ uint8 blue = GetBValue(pPal->palRGB[i]);
+
+ // calculate the Value field of the HSV colour model
+ unsigned val = (red > green) ? red : green;
+ val = (val > blue) ? val : blue;
+
+ // map the Value field to one of the 4 colours reserved for the translucent palette
+ val /= 64;
+ assert(/*val >= 0 &&*/ val <= 3);
+ ghostPalette[i + 1] = (uint8)(val + SysVar(ISV_GHOST_BASE));
+ }
+}
+
+
+/**
+ * Returns an adjusted colour RGB
+ * @param colour Colour to scale
+ */
+static COLORREF DimColour(COLORREF colour, int factor) {
+ uint32 red, green, blue;
+
+ if (factor == 10) {
+ // No change
+ return colour;
+ } else if (factor == 0) {
+ // No brightness
+ return 0;
+ } else {
+ // apply multiplier to RGB components
+ red = GetRValue(colour) * factor / 10;
+ green = GetGValue(colour) * factor / 10;
+ blue = GetBValue(colour) * factor / 10;
+
+ // return new colour
+ return RGB(red, green, blue);
+ }
+}
+
+/**
+ * DimPartPalette
+ */
+void DimPartPalette(SCNHANDLE hDimPal, int startColour, int length, int brightness) {
+ PALQ *pPalQ;
+ PALETTE *pDimPal;
+ int iColour;
+
+ pPalQ = FindPalette(hDimPal);
+ assert(pPalQ);
+
+ // get pointer to dim palette
+ pDimPal = (PALETTE *)LockMem(hDimPal);
+
+ // Adjust for the fact that palettes don't contain colour 0
+ startColour -= 1;
+
+ // Check some other things
+ if (startColour + length > pPalQ->numColours)
+ error("DimPartPalette(): colour overrun");
+
+ for (iColour = startColour ; iColour < startColour + length; iColour++) {
+ pPalQ->palRGB[iColour] = DimColour(pDimPal->palRGB[iColour], brightness);
+ }
+
+ if (!pPalQ->bFading) {
+ // Q the change to the video DAC
+ UpdateDACqueue(pPalQ->posInDAC + startColour, length, &pPalQ->palRGB[startColour]);
}
}
+int TranslucentColour(void) {
+ return translucentIndex;
+}
+
+int HighlightColour(void) {
+ static COLORREF cRef;
+
+ cRef = (COLORREF)SysVar(SYS_HighlightRGB);
+ UpdateDACqueue(talkIndex, 1, &cRef);
+
+ return talkIndex;
+}
+
+int TalkColour(void) {
+ return TinselV2 ? talkIndex : TALKFONT_COL;
+}
+
+void SetTalkColourRef(COLORREF colRef) {
+ talkColRef = colRef;
+}
+
+COLORREF GetTalkColourRef(void) {
+ return talkColRef;
+}
+
+void SetTagColorRef(COLORREF colRef) {
+ tagColRef = colRef;
+}
+
+COLORREF GetTagColorRef(void) {
+ return tagColRef;
+}
+
+void SetTranslucencyOffset(int offset) {
+ translucentIndex = offset;
+}
+
+void SetTalkTextOffset(int offset) {
+ talkIndex = offset;
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/palette.h b/engines/tinsel/palette.h
index fdc4826dbd..1d43e6d31f 100644
--- a/engines/tinsel/palette.h
+++ b/engines/tinsel/palette.h
@@ -43,7 +43,7 @@ enum {
MAX_COLOURS = 256, //!< maximum number of colours - for VGA 256
BITS_PER_PIXEL = 8, //!< number of bits per pixel for VGA 256
MAX_INTENSITY = 255, //!< the biggest value R, G or B can have
- NUM_PALETTES = 3, //!< number of palettes
+ NUM_PALETTES = 32, //!< number of palettes
// Discworld has some fixed apportioned bits in the palette.
BGND_DAC_INDEX = 0, //!< index of background colour in Video DAC
@@ -84,8 +84,11 @@ struct PALQ {
int objCount; //!< number of objects using this palette
int posInDAC; //!< palette position in the video DAC
int numColours; //!< number of colours in the palette
+ // Discworld 2 fields
+ bool bFading; // Whether or not fading
+ COLORREF palRGB[MAX_COLOURS]; // actual palette colours
};
-
+typedef PALQ *PPALQ;
#define PALETTE_MOVED 0x8000 // when this bit is set in the "posInDAC"
// field - the palette entry has moved
@@ -137,8 +140,41 @@ COLORREF GetBgndColour(void); // returns current background colour
void SetBgndColour( // sets current background colour
COLORREF colour); // colour to set the background to
+void FadingPalette(PPALQ pPalQ, bool bFading);
+
void CreateTranslucentPalette(SCNHANDLE BackPal);
+void CreateGhostPalette(SCNHANDLE hPalette);
+
+void NoFadingPalettes(void); // All fading processes have just been killed
+
+void DimPartPalette(
+ SCNHANDLE hPal,
+ int startColour,
+ int length,
+ int brightness); // 0 = black, 10 == 100%
+
+
+int TranslucentColour(void);
+
+#define BoxColour TranslucentColour
+
+int HighlightColour(void);
+
+int TalkColour(void);
+
+void SetTalkColourRef(COLORREF colRef);
+
+COLORREF GetTalkColourRef(void);
+
+void SetTagColorRef(COLORREF colRef);
+
+COLORREF GetTagColorRef(void);
+
+void SetTalkTextOffset(int offset);
+
+void SetTranslucencyOffset(int offset);
+
} // end of namespace Tinsel
#endif // TINSEL_PALETTE_H
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index 023417fe3c..815ce2cd2f 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -25,13 +25,16 @@
*/
#include "tinsel/dw.h"
+#include "tinsel/drives.h"
#include "tinsel/events.h" // 'POINTED' etc.
#include "tinsel/handle.h" // LockMem()
-#include "tinsel/inventory.h" // for inventory id's
+#include "tinsel/dialogs.h" // for inventory id's
#include "tinsel/pcode.h" // opcodes etc.
#include "tinsel/scn.h" // FindChunk()
#include "tinsel/serializer.h"
+#include "tinsel/timers.h"
#include "tinsel/tinlib.h" // Library routines
+#include "tinsel/tinsel.h"
#include "common/util.h"
@@ -97,7 +100,7 @@ enum OPCODE {
#define OPMASK 0x3F //!< mask to isolate the opcode
-
+bool bNoPause = false;
//----------------- LOCAL GLOBAL DATA --------------------
@@ -107,13 +110,19 @@ static int numGlobals = 0; // How many global variables to save/restore
static INT_CONTEXT *icList = 0;
+static uint32 hMasterScript;
+
/**
* Keeps the code array pointer up to date.
*/
void LockCode(INT_CONTEXT *ic) {
- if (ic->GSort == GS_MASTER)
- ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
- else
+ if (ic->GSort == GS_MASTER) {
+ if (TinselV2)
+ // Get the srcipt handle from a specific global chunk
+ ic->code = (byte *)LockMem(hMasterScript);
+ else
+ ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
+ } else
ic->code = (byte *)LockMem(ic->hCode);
}
@@ -124,7 +133,7 @@ static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
INT_CONTEXT *pic;
int i;
- for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) {
+ for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if (pic->GSort == GS_NONE) {
pic->pProc = g_scheduler->getCurrentProcess();
pic->GSort = gsort;
@@ -141,11 +150,41 @@ static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
error("Out of interpret contexts");
}
+static void FreeWaitCheck(PINT_CONTEXT pic, bool bVoluntary) {
+ int i;
+
+ // Is this waiting for something?
+ if (pic->waitNumber1) {
+ for (i = 0; i < NUM_INTERPRET; i++) {
+ if ((icList + i)->waitNumber2 == pic->waitNumber1) {
+ (icList + i)->waitNumber2 = 0;
+ break;
+ }
+ }
+ }
+
+ // Is someone waiting for this?
+ if (pic->waitNumber2) {
+ for (i = 0; i < NUM_INTERPRET; i++) {
+ if ((icList + i)->waitNumber1 == pic->waitNumber2) {
+ (icList + i)->waitNumber1 = 0;
+ (icList + i)->resumeCode = bVoluntary ? RES_FINISHED : RES_CUTSHORT;
+ g_scheduler->reschedule((icList + i)->pProc);
+ break;
+ }
+ }
+ assert(i < NUM_INTERPRET);
+ }
+}
+
/**
* Normal release of an interpret context.
* Called from the end of Interpret().
*/
static void FreeInterpretContextPi(INT_CONTEXT *pic) {
+ FreeWaitCheck(pic, true);
+ if (TinselV2)
+ memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
}
@@ -158,8 +197,11 @@ void FreeInterpretContextPr(PROCESS *pProc) {
INT_CONTEXT *pic;
int i;
- for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) {
+ for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if (pic->GSort != GS_NONE && pic->pProc == pProc) {
+ FreeWaitCheck(pic, false);
+ if (TinselV2)
+ memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
break;
}
@@ -173,8 +215,9 @@ void FreeMostInterpretContexts(void) {
INT_CONTEXT *pic;
int i;
- for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) {
- if (pic->GSort != GS_MASTER) {
+ for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
+ if ((pic->GSort != GS_MASTER) && (pic->GSort != GS_GPROCESS)) {
+ memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
}
}
@@ -187,8 +230,9 @@ void FreeMasterInterpretContext(void) {
INT_CONTEXT *pic;
int i;
- for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) {
- if (pic->GSort == GS_MASTER) {
+ for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
+ if ((pic->GSort == GS_MASTER) || (pic->GSort == GS_GPROCESS)) {
+ memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
return;
}
@@ -205,8 +249,8 @@ void FreeMasterInterpretContext(void) {
* @param actorId Associated actor (if any)
* @param pinvo Associated inventory object
*/
-INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, USER_EVENT event,
- HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo) {
+INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, TINSEL_EVENT event,
+ HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) {
INT_CONTEXT *ic;
ic = AllocateInterpretContext(gsort);
@@ -215,14 +259,14 @@ INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, USER_EVENT event
ic->hCode = hCode;
LockCode(ic);
ic->event = event;
- ic->hpoly = hpoly;
- ic->actorid = actorid;
+ ic->hPoly = hpoly;
+ ic->idActor = actorid;
ic->pinvo = pinvo;
// Previously local variables in Interpret()
ic->bHalt = false; // set to exit interpeter
- ic->escOn = false;
- ic->myescEvent = 0; // only initialised to prevent compiler warning!
+ ic->escOn = myEscape > 0;
+ ic->myEscape = myEscape;
ic->sp = 0;
ic->bp = ic->sp + 1;
ic->ip = 0; // start of code
@@ -256,6 +300,9 @@ void RegisterGlobals(int num) {
if (pGlobals == NULL) {
numGlobals = num;
+ hMasterScript = !TinselV2 ? 0 :
+ READ_LE_UINT32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT));
+
// Allocate RAM for pGlobals and make sure it's allocated
pGlobals = (int32 *)calloc(numGlobals, sizeof(int32));
if (pGlobals == NULL) {
@@ -263,18 +310,38 @@ void RegisterGlobals(int num) {
}
// Allocate RAM for interpret contexts and make sure it's allocated
- icList = (INT_CONTEXT *)calloc(MAX_INTERPRET, sizeof(INT_CONTEXT));
+ icList = (INT_CONTEXT *)calloc(NUM_INTERPRET, sizeof(INT_CONTEXT));
if (icList == NULL) {
error("Cannot allocate memory for interpret contexts");
}
-
g_scheduler->setResourceCallback(FreeInterpretContextPr);
} else {
// Check size is still the same
assert(numGlobals == num);
memset(pGlobals, 0, numGlobals * sizeof(int32));
- memset(icList, 0, MAX_INTERPRET * sizeof(INT_CONTEXT));
+ memset(icList, 0, NUM_INTERPRET * sizeof(INT_CONTEXT));
+ }
+
+ if (TinselV2) {
+ // read initial values
+ CdCD(nullContext);
+
+ Common::File f;
+ if (!f.open(GLOBALS_FILENAME))
+ error(CANNOT_FIND_FILE, GLOBALS_FILENAME);
+
+ int32 length = f.readSint32LE();
+ if (length != num)
+ error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
+
+ for (int i = 0; i < length; ++i)
+ pGlobals[i] = f.readSint32LE();
+
+ if (f.ioFailed())
+ error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
+
+ f.close();
}
}
@@ -309,8 +376,8 @@ void INT_CONTEXT::syncWithSerializer(Serializer &s) {
s.syncAsUint32LE(GSort);
s.syncAsUint32LE(hCode);
s.syncAsUint32LE(event);
- s.syncAsSint32LE(hpoly);
- s.syncAsSint32LE(actorid);
+ s.syncAsSint32LE(hPoly);
+ s.syncAsSint32LE(idActor);
for (int i = 0; i < PCODE_STACK_SIZE; ++i)
s.syncAsSint32LE(stack[i]);
@@ -320,14 +387,14 @@ void INT_CONTEXT::syncWithSerializer(Serializer &s) {
s.syncAsSint32LE(ip);
s.syncAsUint32LE(bHalt);
s.syncAsUint32LE(escOn);
- s.syncAsSint32LE(myescEvent);
+ s.syncAsSint32LE(myEscape);
}
/**
* Return pointer to and size of global data for save/restore game.
*/
void SaveInterpretContexts(INT_CONTEXT *sICInfo) {
- memcpy(sICInfo, icList, MAX_INTERPRET * sizeof(INT_CONTEXT));
+ memcpy(sICInfo, icList, NUM_INTERPRET * sizeof(INT_CONTEXT));
}
/**
@@ -451,6 +518,8 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
return;
ic->sp += tmp2;
LockCode(ic);
+ if (TinselV2 && (ic->resumeState == RES_1))
+ ic->resumeState = RES_NOT;
break;
case OP_RET: // procedure return
@@ -567,12 +636,14 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
break;
case OP_ESCON:
+ bNoPause = true;
ic->escOn = true;
- ic->myescEvent = GetEscEvents();
+ ic->myEscape = GetEscEvents();
break;
case OP_ESCOFF:
ic->escOn = false;
+ ic->myEscape = 0;
break;
default:
@@ -590,4 +661,128 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
FreeInterpretContextPi(ic);
}
+/**
+ * Associates an interpret context with the
+ * process that will run it.
+ */
+void AttachInterpret(INT_CONTEXT *pic, PROCESS *pProc) {
+ // Attach the process which is using this context
+ pic->pProc = pProc;
+}
+
+/**
+ * Generate a number that isn't being used.
+ */
+static uint32 UniqueWaitNumber(void) {
+ uint32 retval;
+ int i;
+
+ for (retval = DwGetCurrentTime(); 1; retval--) {
+ if (retval == 0)
+ retval = (uint32)-1;
+
+ for (i = 0; i < NUM_INTERPRET; i++) {
+ if ((icList+i)->waitNumber1 == retval
+ || (icList+i)->waitNumber2 == retval)
+ break;
+ }
+
+ if (i == NUM_INTERPRET)
+ return retval;
+ }
+}
+
+/**
+ * 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;
+}
+
+/**
+ * CheckOutWaiters
+ */
+void CheckOutWaiters(void) {
+ int i, j;
+
+ // Check all waited for have someone waiting
+ for (i = 0; i < NUM_INTERPRET; i++) {
+ // If someone is supposedly waiting for this one
+ if ((icList + i)->GSort != GS_NONE && (icList + i)->waitNumber2) {
+ // Someone really must be waiting for this one
+ for (j = 0; j < NUM_INTERPRET; j++) {
+ if ((icList + j)->GSort != GS_NONE
+ && (icList + j)->waitNumber1 == (icList + i)->waitNumber2) {
+ break;
+ }
+ }
+ assert(j < NUM_INTERPRET);
+ }
+ }
+
+ // Check waiting for someone to wait for
+ for (i = 0; i < NUM_INTERPRET; i++) {
+ // If someone is supposedly waiting for this one
+ if ((icList + i)->GSort != GS_NONE && (icList + i)->waitNumber1) {
+ // Someone really must be waiting for this one
+ for (j = 0; j < NUM_INTERPRET; j++) {
+ if ((icList + j)->GSort != GS_NONE
+ && (icList + j)->waitNumber2 == (icList + i)->waitNumber1) {
+ break;
+ }
+ }
+ assert(j < NUM_INTERPRET);
+ }
+ }
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/pcode.h b/engines/tinsel/pcode.h
index 1c7e0a942c..fef45d5e03 100644
--- a/engines/tinsel/pcode.h
+++ b/engines/tinsel/pcode.h
@@ -27,7 +27,7 @@
#ifndef TINSEL_PCODE_H // prevent multiple includes
#define TINSEL_PCODE_H
-#include "tinsel/events.h" // for USER_EVENT
+#include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/sched.h" // for PROCESS
namespace Tinsel {
@@ -45,9 +45,12 @@ enum {
};
enum GSORT {
- GS_NONE, GS_ACTOR, GS_MASTER, GS_POLYGON, GS_INVENTORY, GS_SCENE
+ GS_NONE, GS_ACTOR, GS_MASTER, GS_POLYGON, GS_INVENTORY, GS_SCENE,
+ GS_PROCESS, GS_GPROCESS
};
+enum RESCODE {RES_WAITING, RES_FINISHED, RES_CUTSHORT};
+
struct INT_CONTEXT {
// Elements for interpret context management
@@ -57,9 +60,9 @@ struct INT_CONTEXT {
// Previously parameters to Interpret()
SCNHANDLE hCode; //!< scene handle of the code to execute
byte *code; //!< pointer to the code to execute
- USER_EVENT event; //!< causal event
- HPOLYGON hpoly; //!< associated polygon (if any)
- int actorid; //!< associated actor (if any)
+ TINSEL_EVENT event; //!< causal event
+ HPOLYGON hPoly; //!< associated polygon (if any)
+ int idActor; //!< associated actor (if any)
INV_OBJECT *pinvo; //!< associated inventory object
// Previously local variables in Interpret()
@@ -69,27 +72,32 @@ struct INT_CONTEXT {
int ip; //!< instruction pointer
bool bHalt; //!< set to exit interpeter
bool escOn;
- int myescEvent; //!< only initialised to prevent compiler warning!
+ int myEscape; //!< only initialised to prevent compiler warning!
+ uint32 waitNumber1; // The waiting numbert
+ uint32 waitNumber2; // The wait for number
+ RESCODE resumeCode;
RESUME_STATE resumeState;
void syncWithSerializer(Serializer &s);
};
-
+typedef INT_CONTEXT *PINT_CONTEXT;
/*----------------------------------------------------------------------*\
|* Interpreter Function Prototypes *|
\*----------------------------------------------------------------------*/
-void Interpret(CORO_PARAM, INT_CONTEXT *ic); // Interprets the PCODE instructions in the code array
+// Interprets the PCODE instructions in the code array
+void Interpret(CORO_PARAM, INT_CONTEXT *ic);
INT_CONTEXT *InitInterpretContext(
GSORT gsort,
SCNHANDLE hCode, // code to execute
- USER_EVENT event, // causal event
+ TINSEL_EVENT event, // causal event
HPOLYGON hpoly, // associated polygon (if any)
int actorid, // associated actor (if any)
- INV_OBJECT *pinvo); // associated inventory object
+ INV_OBJECT *pinvo,
+ int myEscape = -1); // associated inventory object
INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric);
@@ -101,8 +109,12 @@ void SaveInterpretContexts(INT_CONTEXT *sICInfo);
void RegisterGlobals(int num);
void FreeGlobals(void);
+void AttachInterpret(INT_CONTEXT *pic, PROCESS *pProc);
-#define MAX_INTERPRET (NUM_PROCESS - 20)
+void WaitInterpret(CORO_PARAM, PPROCESS pWaitProc, bool *result);
+
+#define NUM_INTERPRET (NUM_PROCESS - 20)
+#define MAX_INTERPRET (MAX_PROCESSES - 20)
/*----------------------------------------------------------------------*\
|* Library Procedure and Function codes parameter enums *|
@@ -137,10 +149,6 @@ void FreeGlobals(void);
#define MIDI_DEF 0
#define MIDI_LOOP 1
-#define TRANS_DEF 0
-#define TRANS_CUT 1
-#define TRANS_FADE 2
-
#define FM_IN 0 //
#define FM_OUT 1 // fademidi()
diff --git a/engines/tinsel/pdisplay.cpp b/engines/tinsel/pdisplay.cpp
index b5488da3e8..8cb6fcde0e 100644
--- a/engines/tinsel/pdisplay.cpp
+++ b/engines/tinsel/pdisplay.cpp
@@ -28,6 +28,7 @@
#include "tinsel/actors.h"
#include "tinsel/background.h"
+#include "tinsel/coroutine.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
@@ -41,6 +42,7 @@
#include "tinsel/sched.h"
#include "tinsel/strres.h"
#include "tinsel/text.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -56,8 +58,8 @@ extern int newestString; // The overrun counter, in STRRES.C
//----------------- EXTERNAL FUNCTIONS ---------------------
// in BG.C
-extern int BackgroundWidth(void);
-extern int BackgroundHeight(void);
+extern int BgWidth(void);
+extern int BgHeight(void);
@@ -84,8 +86,11 @@ static bool bShowString = false;
static int TaggedActor = 0;
static HPOLYGON hTaggedPolygon = NOPOLY;
-static enum { TAGS_OFF, TAGS_ON } TagsActive = TAGS_ON;
+static bool bTagsActive = true;
+static bool bPointingActive = true;
+
+static char tagBuffer[64];
#ifdef DEBUG
/**
@@ -134,7 +139,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
char PositionString[64]; // sprintf() things into here
- PMACTOR pActor; // Lead actor
+ PMOVER pActor; // Lead actor
while (1) {
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
@@ -159,7 +164,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// New text objects
sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
_ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
- 0, CPOSX, POSY, hTagFontHandle(), TXT_CENTRE);
+ 0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
if (DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
if (hp == NOPOLY)
@@ -171,7 +176,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
_ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
- 0, 4, POSY+ 10, hTagFontHandle(), 0);
+ 0, 4, POSY+ 10, GetTagFontHandle(), 0);
}
// update previous position
@@ -191,7 +196,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
sprintf(PositionString, "%d", Overrun);
_ctx->opText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
- 0, OPOSX, POSY, hTagFontHandle(), TXT_CENTRE);
+ 0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous value
_ctx->prevOver = Overrun;
@@ -202,7 +207,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
| Lead actor's position. |
\*----------------------*/
pActor = GetMover(LEAD_ACTOR);
- if (pActor && pActor->MActorState == NORM_MACTOR) {
+ if (pActor && pActor->MActorState == NORM_MOVER) {
// get lead's animation position
GetActorPos(LEAD_ACTOR, &aniX, &aniY);
@@ -217,7 +222,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// create new text object list
sprintf(PositionString, "%d %d", aniX, aniY);
_ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
- 0, LPOSX, POSY, hTagFontHandle(), TXT_CENTRE);
+ 0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous position
_ctx->prevlX = aniX;
@@ -236,7 +241,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
sprintf(PositionString, "String: %d", newestString);
_ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
- 0, SPOSX, POSY+10, hTalkFontHandle(), TXT_CENTRE);
+ 0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTRE);
// update previous value
_ctx->prevString = newestString;
@@ -253,8 +258,51 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
#endif
/**
+ * While inventory/menu is open.
+ */
+void DisablePointing(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ HPOLYGON hPoly; // Polygon handle
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ bPointingActive = false;
+
+ for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
+ _ctx->hPoly = GetPolyHandle(_ctx->i);
+
+ if (_ctx->hPoly != NOPOLY && PolyType(_ctx->hPoly) == TAG && PolyIsPointedTo(_ctx->hPoly)) {
+ SetPolyPointedTo(_ctx->hPoly, false);
+ SetPolyTagWanted(_ctx->hPoly, false, false, 0);
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
+ }
+ }
+
+ // For each tagged actor
+ for (_ctx->i = 0; (_ctx->i = NextTaggedActor(_ctx->i)) != 0; ) {
+ if (ActorIsPointedTo(_ctx->i)) {
+ SetActorPointedTo(_ctx->i, false);
+ SetActorTagWanted(_ctx->i, false, false, 0);
+
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * EnablePointing()
+ */
+void EnablePointing(void) {
+ bPointingActive = true;
+}
+
+/**
* Tag process keeps us updated as to which tagged actor is currently tagged
- * (if one is). Tag process asks us for this information, as does User_Event().
+ * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
static void SaveTaggedActor(int ano) {
TaggedActor = ano;
@@ -262,7 +310,7 @@ static void SaveTaggedActor(int ano) {
/**
* Tag process keeps us updated as to which tagged actor is currently tagged
- * (if one is). Tag process asks us for this information, as does User_Event().
+ * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
int GetTaggedActor(void) {
return TaggedActor;
@@ -270,7 +318,7 @@ int GetTaggedActor(void) {
/**
* Tag process keeps us updated as to which polygon is currently tagged
- * (if one is). Tag process asks us for this information, as does User_Event().
+ * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
static void SaveTaggedPoly(HPOLYGON hp) {
hTaggedPolygon = hp;
@@ -341,12 +389,59 @@ static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
* the screen.
*/
static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
- static int Loffset = 0, Toffset = 0; // Values when tag was displayed
- int nLoff, nToff; // new values, to keep tag in place
+ static int tagX = 0, tagY = 0; // Values when tag was displayed
+ int newX, newY; // new values, to keep tag in place
int ano;
int xtext, ytext;
bool newActor;
+ if (TinselV2) {
+ // Tinsel 2 version
+ // Get the foremost pointed to actor
+ int actor = FrontTaggedActor();
+
+ if (actor == 0) {
+ SaveTaggedActor(0);
+ return false;
+ }
+
+ // If new actor
+ // or actor has suddenly decided it wants tagging...
+ if (actor != GetTaggedActor() || (ActorTagIsWanted(actor) && !*ppText)) {
+ // Put up actor tag
+ SaveTaggedActor(actor); // This actor tagged
+ SaveTaggedPoly(NOPOLY); // No tagged polygon
+
+ if (*ppText)
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
+
+ if (ActorTagIsWanted(actor)) {
+ GetActorTagPos(actor, &tagX, &tagY, false);
+ LoadStringRes(GetActorTagHandle(actor), tagBuffer, sizeof(tagBuffer));
+
+ // May have buggered cursor
+ EndCursorFollowed();
+ *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), tagBuffer,
+ 0, tagX, tagY, GetTagFontHandle(), TXT_CENTRE, 0);
+ assert(*ppText);
+ MultiSetZPosition(*ppText, Z_TAG_TEXT);
+ } else
+ *ppText = NULL;
+ } else if (*ppText) {
+ // Same actor, maintain tag position
+ GetActorTagPos(actor, &newX, &newY, false);
+
+ if (newX != tagX || newY != tagY) {
+ MultiMoveRelXY(*ppText, newX - tagX, newY - tagY);
+ tagX = newX;
+ tagY = newY;
+ }
+ }
+
+ return true;
+ }
+
+ // Tinsel 1 version
// For each actor with a tag....
FirstTaggedActor();
while ((ano = NextTaggedActor()) != 0) {
@@ -369,20 +464,20 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
SaveTaggedActor(ano); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
- PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- LoadStringRes(GetActorTag(ano), tBufferAddr(), TBUFSZ);
- *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
- 0, xtext - Loffset, ytext - Toffset, hTagFontHandle(), TXT_CENTRE);
+ PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
+ LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
+ *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ 0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else {
// Maintain actor tag's position
- PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
- if (nLoff != Loffset || nToff != Toffset) {
- MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
- Loffset = nLoff;
- Toffset = nToff;
+ PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
+ if (newX != tagX || newY != tagY) {
+ MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
+ tagX = newX;
+ tagY = newY;
}
}
return true;
@@ -406,6 +501,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
*/
static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
static int Loffset = 0, Toffset = 0; // Values when tag was displayed
+ static int curX = 0, curY = 0;
int nLoff, nToff; // new values, to keep tag in place
HPOLYGON hp;
bool newPoly;
@@ -418,8 +514,11 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
for (int i = 0; i < MAX_POLY; i++) {
hp = GetPolyHandle(i);
+ if (TinselV2 && (hp == NOPOLY))
+ continue;
+
// Added code for un-tagged tags
- if (hp != NOPOLY && PolyPointState(hp) == POINTING && PolyTagState(hp) != TAG_ON) {
+ if ((hp != NOPOLY) && (PolyPointState(hp) == PS_POINTING) && (PolyTagState(hp) != TAG_ON)) {
// This poly is entitled to be tagged
if (hp != GetTaggedPoly()) {
if (*ppText) {
@@ -431,58 +530,99 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
SaveTaggedPoly(hp); // This polygon tagged
}
return true;
- } else if (hp != NOPOLY && PolyTagState(hp) == TAG_ON) {
+ } else if ((TinselV2 && PolyTagIsWanted(hp)) ||
+ (!TinselV2 && hp != NOPOLY && PolyTagState(hp) == TAG_ON)) {
// Put up or maintain polygon tag
- if (*pTag != POLY_HOTSPOT_TAG)
- newPoly = true; // A new polygon (no current)
- else if (hp != GetTaggedPoly())
- newPoly = true; // Different polygon
- else
- newPoly = false; // Same polygon
+ newPoly = false;
+ if (TinselV2) {
+ if (hp != GetTaggedPoly())
+ newPoly = true; // Different polygon
+ } else {
+ if (*pTag != POLY_HOTSPOT_TAG)
+ newPoly = true; // A new polygon (no current)
+ else if (hp != GetTaggedPoly())
+ newPoly = true; // Different polygon
+ }
if (newPoly) {
if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
- *pTag = POLY_HOTSPOT_TAG;
+ if (!TinselV2)
+ *pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- getPolyTagInfo(hp, &hTagtext, &tagx, &tagy);
+ GetTagTag(hp, &hTagtext, &tagx, &tagy);
int strLen;
- if (PolyTagHandle(hp) != 0)
- strLen = LoadStringRes(PolyTagHandle(hp), tBufferAddr(), TBUFSZ);
+ if (GetPolyTagHandle(hp) != 0)
+ strLen = LoadStringRes(GetPolyTagHandle(hp), TextBufferAddr(), TBUFSZ);
else
- strLen = LoadStringRes(hTagtext, tBufferAddr(), TBUFSZ);
+ strLen = LoadStringRes(hTagtext, TextBufferAddr(), TBUFSZ);
if (strLen == 0)
// No valid string returned, so leave ppText as NULL
ppText = NULL;
- else {
+ else if (TinselV2 && !PolyTagFollowsCursor(hp)) {
+ // May have buggered cursor
+ EndCursorFollowed();
+
+ *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
+ GetTagFontHandle(), TXT_CENTRE, 0);
+ } else if (TinselV2) {
+ // Bugger cursor
+ const char *tagPtr = TextBufferAddr();
+ if (tagPtr[0] < ' ' && tagPtr[1] == EOS_CHAR)
+ StartCursorFollowed();
+
+ GetCursorXYNoWait(&curX, &curY, false);
+ *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ 0, curX, curY, GetTagFontHandle(), TXT_CENTRE, 0);
+ } else {
// Handle displaying the tag text on-screen
- *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
+ *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
- hTagFontHandle(), TXT_CENTRE);
+ GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Polygon tag string produced NULL text
+ }
+
+ // DW1 has some tags without text, e.g. the "equals" button when talking to the guard in act 3
+ if (ppText) {
MultiSetZPosition(*ppText, Z_TAG_TEXT);
-
/*
* New feature: Don't go off the side of the background
*/
shift = MultiRightmost(*ppText) + Loffset + 2;
- if (shift >= BackgroundWidth()) // Not off right
- MultiMoveRelXY(*ppText, BackgroundWidth() - shift, 0);
+ if (shift >= BgWidth()) // Not off right
+ MultiMoveRelXY(*ppText, BgWidth() - shift, 0);
shift = MultiLeftmost(*ppText) + Loffset - 1;
if (shift <= 0) // Not off left
MultiMoveRelXY(*ppText, -shift, 0);
shift = MultiLowest(*ppText) + Toffset;
- if (shift > BackgroundHeight()) // Not off bottom
- MultiMoveRelXY(*ppText, 0, BackgroundHeight() - shift);
+ if (shift > BgHeight()) // Not off bottom
+ MultiMoveRelXY(*ppText, 0, BgHeight() - shift);
}
- } else {
+ } else if (TinselV2 && (*ppText)) {
+ if (!PolyTagFollowsCursor(hp)) {
+ PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
+ if (nLoff != Loffset || nToff != Toffset) {
+ MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
+ Loffset = nLoff;
+ Toffset = nToff;
+ }
+ } else {
+ GetCursorXY(&tagx, &tagy, false);
+ if (tagx != curX || tagy != curY) {
+ MultiMoveRelXY(*ppText, tagx - curX, tagy - curY);
+ curX = tagx;
+ curY = tagy;
+ }
+ }
+ } else if (!TinselV2) {
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
@@ -495,7 +635,9 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
}
// No tagged polygon
- if (*pTag == POLY_HOTSPOT_TAG) {
+ if (TinselV2)
+ SaveTaggedPoly(NOPOLY);
+ else if (*pTag == POLY_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG;
SaveTaggedPoly(NOPOLY);
}
@@ -522,7 +664,7 @@ void TagProcess(CORO_PARAM, const void *) {
SaveTaggedPoly(NOPOLY); // No tagged polygon yet
while (1) {
- if (TagsActive == TAGS_ON) {
+ if (bTagsActive) {
int curX, curY; // cursor position
while (!GetCursorXYNoWait(&curX, &curY, true))
CORO_SLEEP(1);
@@ -533,6 +675,10 @@ void TagProcess(CORO_PARAM, const void *) {
if (_ctx->pText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = NULL;
+
+ if (TinselV2)
+ // May have buggered cursor
+ EndCursorFollowed();
}
}
} else {
@@ -557,22 +703,43 @@ void TagProcess(CORO_PARAM, const void *) {
/**
* Called from PointProcess() as appropriate.
*/
-static void enteringpoly(HPOLYGON hp) {
- SetPolyPointState(hp, POINTING);
+static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ SetPolyPointState(hp, PS_POINTING);
- RunPolyTinselCode(hp, POINTED, BE_NONE, false);
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, POINTED, 0, false, 0));
+ else
+ RunPolyTinselCode(hp, POINTED, PLR_NOEVENT, false);
+
+ CORO_END_CODE;
}
/**
* Called from PointProcess() as appropriate.
*/
-static void leavingpoly(HPOLYGON hp) {
- SetPolyPointState(hp, NOT_POINTING);
+static void leavingpoly(CORO_PARAM, HPOLYGON hp) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ SetPolyPointState(hp, PS_NOT_POINTING);
+
+ if (TinselV2) {
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, UNPOINT, 0, false, 0));
+ SetPolyTagWanted(hp, false, false, 0);
- if (PolyTagState(hp) == TAG_ON) {
+ } else if (PolyTagState(hp) == TAG_ON) {
// Delete this tag entry
SetPolyTagState(hp, TAG_OFF);
}
+
+ CORO_END_CODE;
}
/**
@@ -583,56 +750,95 @@ static void leavingpoly(HPOLYGON hp) {
void PointProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
+ HPOLYGON hPoly;
+ int i;
+ int curX, curY; // cursor/tagged actor position
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
+ if (TinselV2)
+ EnablePointing();
+
while (1) {
- int aniX, aniY; // cursor/tagged actor position
- while (!GetCursorXYNoWait(&aniX, &aniY, true))
+ while (!GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
CORO_SLEEP(1);
/*----------------------------------*\
| For polygons of type TAG and EXIT. |
\*----------------------------------*/
- for (int i = 0; i < MAX_POLY; i++) {
- HPOLYGON hp = GetPolyHandle(i);
-
- if (hp != NOPOLY && (PolyType(hp) == TAG || PolyType(hp) == EXIT)) {
- if (PolyPointState(hp) == NOT_POINTING) {
- if (IsInPolygon(aniX, aniY, hp)) {
- enteringpoly(hp);
+ for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
+ _ctx->hPoly = GetPolyHandle(_ctx->i);
+ if ((_ctx->hPoly == NOPOLY) || ((PolyType(_ctx->hPoly) != TAG) &&
+ (PolyType(_ctx->hPoly) != EXIT)))
+ continue;
+
+ if (!PolyIsPointedTo(_ctx->hPoly)) {
+ if (IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
+ if (TinselV2) {
+ SetPolyPointedTo(_ctx->hPoly, true);
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, POINTED, 0, false, 0));
+ } else {
+ CORO_INVOKE_1(enteringpoly, _ctx->hPoly);
}
- } else if (PolyPointState(hp) == POINTING) {
- if (!IsInPolygon(aniX, aniY, hp)) {
- leavingpoly(hp);
+ }
+ } else {
+ if (!IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
+ if (TinselV2) {
+ SetPolyPointedTo(_ctx->hPoly, false);
+ SetPolyTagWanted(_ctx->hPoly, false, false, 0);
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
+ } else {
+ CORO_INVOKE_1(leavingpoly, _ctx->hPoly);
}
}
}
}
- // allow re-scheduling
- CORO_SLEEP(1);
+ if (TinselV2) {
+ // For each tagged actor
+ for (_ctx->i = 0; (_ctx->i = NextTaggedActor(_ctx->i)) != 0; ) {
+ if (!ActorIsPointedTo(_ctx->i)) {
+ if (InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
+ SetActorPointedTo(_ctx->i, true);
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, POINTED, false, 0));
+ }
+ } else {
+ if (!InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
+ SetActorPointedTo(_ctx->i, false);
+ SetActorTagWanted(_ctx->i, false, false, 0);
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
+ }
+ }
+ }
+
+ // allow re-scheduling
+ do {
+ CORO_SLEEP(1);
+ } while (!bPointingActive);
+ } else {
+ // allow re-scheduling
+ CORO_SLEEP(1);
+ }
}
CORO_END_CODE;
}
void DisableTags(void) {
- TagsActive = TAGS_OFF;
+ bTagsActive = false;
}
void EnableTags(void) {
- TagsActive = TAGS_ON;
+ bTagsActive = true;
}
bool DisableTagsIfEnabled(void) {
- if (TagsActive == TAGS_OFF)
- return false;
- else {
- TagsActive = TAGS_OFF;
+ if (bTagsActive) {
+ DisableTags();
return true;
- }
+ } else
+ return false;
}
/**
diff --git a/engines/tinsel/pdisplay.h b/engines/tinsel/pdisplay.h
new file mode 100644
index 0000000000..7a67f3541c
--- /dev/null
+++ b/engines/tinsel/pdisplay.h
@@ -0,0 +1,37 @@
+/* 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$
+ *
+ * Tag related methods
+ */
+
+#ifndef TINSEL_PDISPLAY_H // prevent multiple includes
+#define TINSEL_PDISPLAY_H
+
+namespace Tinsel {
+
+void EnableTags(void);
+void DisableTags(void);
+
+} // end of namespace Tinsel
+
+#endif /* TINSEL_PDISPLAY_H */
diff --git a/engines/tinsel/pid.h b/engines/tinsel/pid.h
index c2af1a5fcb..c7872d6432 100644
--- a/engines/tinsel/pid.h
+++ b/engines/tinsel/pid.h
@@ -61,12 +61,20 @@ namespace Tinsel {
#define PID_MASTER_SCR 0x00C0 // tinsel master script process
-#define PID_MACTOR (0x00D0 | PID_DESTROY) // moving actor process
+#define PID_MOVER (0x00D0 | PID_DESTROY) // moving actor process
#define PID_REEL (0x00E0 | PID_DESTROY) // process for each film reel
#define PID_MIDI (0x00F0 | PID_DESTROY) // process to poll MIDI sound driver
+#define PID_BMV 0x0100 // Movie player process
+
+#define PID_BTN_CLICK 0x110 // process to handle mouse button clicks
+
+#define PID_PROCESS (0x0110 | PID_DESTROY) // Scene process base
+
+#define PID_GPROCESS 0x0120 // Global process base
+
} // end of namespace Tinsel
#endif // TINSEL_PID_H
diff --git a/engines/tinsel/play.cpp b/engines/tinsel/play.cpp
index e32fc88d3d..27b2ea1bf5 100644
--- a/engines/tinsel/play.cpp
+++ b/engines/tinsel/play.cpp
@@ -26,20 +26,50 @@
#include "tinsel/actors.h"
#include "tinsel/background.h"
+#include "tinsel/coroutine.h"
#include "tinsel/dw.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h"
#include "tinsel/object.h"
#include "tinsel/pid.h"
+#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/sched.h"
+#include "tinsel/scn.h"
+#include "tinsel/sound.h"
#include "tinsel/timers.h"
-#include "tinsel/tinlib.h" // stand()
+#include "tinsel/tinlib.h" // Stand()
namespace Tinsel {
+struct PPINIT {
+ SCNHANDLE hFilm; // The 'film'
+ int16 x; // } Co-ordinates from the play()
+ int16 y; // } - set to (-1, -1) if none.
+ int16 z; // normally 0, set if from restore
+ int16 speed; // Film speed
+ int16 actorid; // Set if called from an actor code block
+ bool splay; // Set if called from splay()
+ bool bTop; // Set if called from topplay()
+ bool bRestore;
+ int16 sf; // SlowFactor - only used for moving actors
+ int16 column; // Column number, first column = 0
+
+ uint8 escOn;
+ int32 myescEvent;
+};
+
+//----------------- LOCAL GLOBAL DATA --------------------
+
+static SOUNDREELS soundReels[MAX_SOUNDREELS];
+static int soundReelNumbers[MAX_SOUNDREELS];
+
+static int soundReelWait;
+
+//-------------------- METHODS ----------------------
+
/**
* Poke the background palette into an image.
*/
@@ -54,13 +84,30 @@ static void PokeInPalette(SCNHANDLE hMulFrame) {
// get pointer to image
pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image
- pim->hImgPal = TO_LE_32(BackPal());
+ pim->hImgPal = TO_LE_32(BgPal());
}
}
+/**
+ * Poke the background palette into an image.
+ */
+void PokeInPalette(const MULTI_INIT *pmi) {
+ FRAME *pFrame; // Pointer to frame
+ IMAGE *pim; // Pointer to image
+
+ // Could be an empty column
+ if (pmi->hMulFrame) {
+ pFrame = (FRAME *)LockMem(pmi->hMulFrame);
+
+ // get pointer to image
+ pim = (IMAGE *)LockMem(*pFrame); // handle to image
+
+ pim->hImgPal = BgPal();
+ }
+}
int32 NoNameFunc(int actorID, bool bNewMover) {
- PMACTOR pActor;
+ PMOVER pActor;
int32 retval;
pActor = GetMover(actorID);
@@ -68,9 +115,9 @@ int32 NoNameFunc(int actorID, bool bNewMover) {
if (pActor != NULL && !bNewMover) {
// If no path, just use first path in the scene
if (pActor->hCpath == NOPOLY)
- retval = getPolyZfactor(FirstPathPoly());
+ retval = GetPolyZfactor(FirstPathPoly());
else
- retval = getPolyZfactor(pActor->hCpath);
+ retval = GetPolyZfactor(pActor->hCpath);
} else {
switch (actorMaskType(actorID)) {
case ACT_DEFAULT:
@@ -91,22 +138,272 @@ int32 NoNameFunc(int actorID, bool bNewMover) {
return retval;
}
-struct PPINIT {
- SCNHANDLE hFilm; // The 'film'
- int16 x; // } Co-ordinates from the play()
- int16 y; // } - set to (-1, -1) if none.
- int16 z; // normally 0, set if from restore
- int16 speed; // Film speed
- int16 actorid; // Set if called from an actor code block
- uint8 splay; // Set if called from splay()
- uint8 bTop; // Set if called from topplay()
- int16 sf; // SlowFactor - only used for moving actors
- int16 column; // Column number, first column = 0
+static FREEL *GetReel(SCNHANDLE hFilm, int column) {
+ FILM *pFilm = (FILM *)LockMem(hFilm);
- uint8 escOn;
- int32 myescEvent;
-};
+ return &pFilm->reels[column];
+}
+
+static int RegisterSoundReel(SCNHANDLE hFilm, int column, int actorCol) {
+ int i;
+
+ for (i = 0; i < MAX_SOUNDREELS; i++) {
+ // Should assert this doesn't happen, but let's be tolerant
+ if (soundReels[i].hFilm == hFilm && soundReels[i].column == column)
+ break;
+ if (!soundReels[i].hFilm)
+ {
+ soundReels[i].hFilm = hFilm;
+ soundReels[i].column = column;
+ soundReels[i].actorCol = actorCol;
+ break;
+ }
+ }
+
+ soundReelNumbers[i]++;
+ return i;
+}
+
+void NoSoundReels(void) {
+ memset(soundReels, 0, sizeof(soundReels));
+ soundReelWait = 0;
+}
+
+static void DeRegisterSoundReel(SCNHANDLE hFilm, int column) {
+ for (int i = 0; i < MAX_SOUNDREELS; i++) {
+ // Should assert this doesn't happen, but let's be tolerant
+ if (soundReels[i].hFilm == hFilm && soundReels[i].column == column) {
+ soundReels[i].hFilm = 0;
+ break;
+ }
+ }
+}
+
+void SaveSoundReels(PSOUNDREELS psr) {
+ for (int i = 0; i < MAX_SOUNDREELS; i++)
+ {
+ if (IsCdPlayHandle(soundReels[i].hFilm))
+ soundReels[i].hFilm = 0;
+ }
+
+ memcpy(psr, soundReels, sizeof(soundReels));
+}
+
+void RestoreSoundReels(PSOUNDREELS psr) {
+ memcpy(soundReels, psr, sizeof(soundReels));
+}
+
+static uint32 GetZfactor(int actorID, PMOVER pMover, bool bNewMover) {
+ if (pMover != NULL && bNewMover == false) {
+ // If no path, just use first path in the scene
+ if (pMover->hCpath == NOPOLY)
+ return GetPolyZfactor(FirstPathPoly());
+ else
+ return GetPolyZfactor(pMover->hCpath);
+ } else {
+ return GetActorZfactor(actorID);
+ }
+}
+
+/**
+ * Handles reels with sound id.
+ * @param hFilm The 'film'
+ * @param column Column number, first column = 0
+ * @param speed Film speed
+ */
+static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed,
+ int myescEvent, int actorCol) {
+ FILM *pFilm;
+ FREEL *pReel;
+ ANI_SCRIPT *pAni;
+
+ short x, y;
+
+ CORO_BEGIN_CONTEXT;
+ int myId;
+ int myNum;
+ int frameNumber;
+ int speed;
+ int sampleNumber;
+ bool bFinished;
+ bool bLooped;
+ int reelActor;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (actorCol) {
+ PMULTI_INIT pmi; // MULTI_INIT structure
+
+ pReel = GetReel(hFilm, actorCol - 1);
+ pmi = (PMULTI_INIT) LockMem(pReel->mobj);
+ _ctx->reelActor = pmi->mulID;
+ } else
+ _ctx->reelActor = 0;
+
+ _ctx->frameNumber = 0;
+ _ctx->speed = speed;
+ _ctx->sampleNumber = 0;
+ _ctx->bFinished = false;
+ _ctx->bLooped = false;
+ _ctx->myId = RegisterSoundReel(hFilm, column, actorCol);
+ _ctx->myNum = soundReelNumbers[_ctx->myId];
+
+ do {
+ pFilm = (FILM *)LockMem(hFilm);
+ pReel = &pFilm->reels[column];
+
+ pAni = (ANI_SCRIPT *)LockMem(pReel->script);
+
+ if (_ctx->speed == -1) {
+ _ctx->speed = (ONE_SECOND/pFilm->frate);
+
+ // Restored reel
+ for (;;) {
+ if (pAni[_ctx->frameNumber].op == ANI_END)
+ break;
+ else if (pAni[_ctx->frameNumber].op == ANI_JUMP) {
+ _ctx->frameNumber++;
+ _ctx->frameNumber += pAni[_ctx->frameNumber].op;
+ break;
+ }
+ // Could check for the other stuff here
+ // but they really dont happen
+ // OH YES THEY DO
+ else if (pAni[_ctx->frameNumber].op == ANI_ADJUSTX
+ || pAni[_ctx->frameNumber].op == ANI_ADJUSTY) {
+ _ctx->frameNumber += 2;
+ } else if (pAni[_ctx->frameNumber].op == ANI_ADJUSTXY) {
+ _ctx->frameNumber += 3;
+ } else {
+ // ANI_STOP, ANI_HIDE, ANI_HFLIP,
+ // ANI_VFLIP, ANI_HVFLIP, default
+ _ctx->frameNumber++;
+ }
+ }
+ }
+
+ switch (pAni[_ctx->frameNumber].op) {
+ case ANI_END:
+ // Stop this sample if repeating
+ if (_ctx->sampleNumber && _ctx->bLooped)
+ _vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
+ _ctx->bFinished = true;
+ break;
+
+ case ANI_JUMP:
+ _ctx->frameNumber++;
+
+ assert(pAni[_ctx->frameNumber].op < 0);
+
+ _ctx->frameNumber += pAni[_ctx->frameNumber].op;
+
+ assert(_ctx->frameNumber >= 0);
+ continue;
+
+ case ANI_STOP:
+ // Stop this sample
+ if (_ctx->sampleNumber)
+ _vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
+ break;
+
+ case ANI_HIDE:
+ // No op
+ break;
+
+ case ANI_HFLIP:
+ case ANI_VFLIP:
+ case ANI_HVFLIP:
+ _ctx->frameNumber++;
+ continue;
+
+ case ANI_ADJUSTX:
+ case ANI_ADJUSTY:
+ _ctx->frameNumber += 2;
+ continue;
+
+ case ANI_ADJUSTXY:
+ _ctx->frameNumber += 3;
+ continue;
+
+ default:
+ // Stop this sample
+ if (_ctx->sampleNumber)
+ _vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
+
+ _ctx->sampleNumber = pAni[_ctx->frameNumber++].op;
+ if (_ctx->sampleNumber > 0)
+ _ctx->bLooped = false;
+ else {
+ _ctx->sampleNumber = ~_ctx->sampleNumber;
+ _ctx->bLooped = true;
+ }
+ x = (short)(pAni[_ctx->frameNumber].op >> 16);
+ y = (short)(pAni[_ctx->frameNumber].op & 0xffff);
+
+ if (x == 0)
+ x = -1;
+
+ _vm->_sound->playSample(_ctx->sampleNumber, 0, _ctx->bLooped, x, y, PRIORITY_SCRIPT,
+ Audio::Mixer::kSFXSoundType);
+
+ break;
+ }
+
+ CORO_SLEEP(_ctx->speed);
+ _ctx->frameNumber++;
+
+ if (_ctx->reelActor && GetActorPresFilm(_ctx->reelActor) != hFilm) {
+ // Stop this sample if repeating
+ if (_ctx->sampleNumber && _ctx->bLooped)
+ _vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
+
+ _ctx->bFinished = true;
+ }
+
+ if (myescEvent && myescEvent != GetEscEvents()) {
+ // Stop this sample
+ if (_ctx->sampleNumber)
+ _vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
+
+ _ctx->bFinished = true;
+ }
+ } while (!_ctx->bFinished && _ctx->myNum == soundReelNumbers[_ctx->myId]);
+
+ // De-register - if not been replaced
+ if (_ctx->myNum == soundReelNumbers[_ctx->myId])
+ DeRegisterSoundReel(hFilm, column);
+
+ CORO_END_CODE;
+}
+
+static void ResSoundReel(CORO_PARAM, const void *) {
+ // get the stuff copied to process when it was created
+ PPROCESS pProc = g_scheduler->getCurrentProcess();
+ int i = *(int *)pProc->param;
+
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, soundReels[i].hFilm, soundReels[i].column,
+ -1, 0, soundReels[i].actorCol));
+
+ CORO_KILL_SELF();
+ CORO_END_CODE;
+}
+
+static void SoundReelWaitCheck(void) {
+ if (--soundReelWait == 0) {
+ for (int i = 0; i < MAX_SOUNDREELS; i++) {
+ if (soundReels[i].hFilm) {
+ g_scheduler->createProcess(PID_REEL, ResSoundReel, &i, sizeof(i));
+ }
+ }
+ }
+}
/**
* - Don't bother if this reel is already playing for this actor.
@@ -121,7 +418,7 @@ struct PPINIT {
* - If called from an splay(), moving actor's co-ordinates are updated
* after the play, any walk still in progress will go on from there.
*/
-void PlayReel(CORO_PARAM, const PPINIT *ppi) {
+static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) {
CORO_BEGIN_CONTEXT;
OBJECT *pPlayObj; // Object
ANIM thisAnim; // Animation structure
@@ -134,6 +431,8 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
int stepCount;
int frameCount;
int reelActor;
+ PMOVER pActor;
+ int tmpX, tmpY;
CORO_END_CONTEXT(_ctx);
static int firstColZ = 0; // Z-position of column zero
@@ -142,14 +441,13 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
CORO_BEGIN_CODE(_ctx);
const MULTI_INIT *pmi; // MULTI_INIT structure
- PMACTOR pActor;
bool bNewMover; // Gets set if a moving actor that isn't in scene yet
const FILM *pfilm;
_ctx->lifeNoMatter = false;
_ctx->replaced = false;
- pActor = NULL;
+ _ctx->pActor = NULL;
bNewMover = false;
pfilm = (const FILM *)LockMem(ppi->hFilm);
@@ -166,26 +464,26 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
return;
/**** Delete a bit down there if this stays ****/
- updateActorEsc(_ctx->reelActor, ppi->escOn, ppi->myescEvent);
+ UpdateActorEsc(_ctx->reelActor, ppi->escOn, ppi->myescEvent);
// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
- if (ppi->hFilm != getActorLatestFilm(_ctx->reelActor)) {
+ if (ppi->hFilm != GetActorLatestFilm(_ctx->reelActor)) {
// This in not the last film scheduled for this actor
// It may be the last non-talk film though
- if (isActorTalking(_ctx->reelActor))
- setActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
+ if (ActorIsTalking(_ctx->reelActor))
+ SetActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
return;
}
- if (isActorTalking(_ctx->reelActor)) {
+ if (ActorIsTalking(_ctx->reelActor)) {
// Note: will delete this and there'll be no need to store the talk film!
- if (ppi->hFilm != getActorTalkFilm(_ctx->reelActor)) {
- setActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
+ if (ppi->hFilm != GetActorTalkFilm(_ctx->reelActor)) {
+ SetActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
return;
}
} else {
- setActorPlayFilm(_ctx->reelActor, ppi->hFilm);
+ SetActorPlayFilm(_ctx->reelActor, ppi->hFilm);
}
// If this reel is already playing for this actor, just forget it.
@@ -206,34 +504,33 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
// Otherwise, use actor's position if there are not embedded co-ords.
// Add this first test for nth columns with offsets
// in plays with (x,y)
- int tmpX, tmpY;
- tmpX = ppi->x;
- tmpY = ppi->y;
+ _ctx->tmpX = ppi->x;
+ _ctx->tmpY = ppi->y;
if (ppi->column != 0 && (pmi->mulX || pmi->mulY)) {
- } else if (tmpX != -1 || tmpY != -1) {
- MultiSetAniXY(_ctx->pPlayObj, tmpX, tmpY);
+ } else if (_ctx->tmpX != -1 || _ctx->tmpY != -1) {
+ MultiSetAniXY(_ctx->pPlayObj, _ctx->tmpX, _ctx->tmpY);
} else if (!pmi->mulX && !pmi->mulY) {
- GetActorPos(_ctx->reelActor, &tmpX, &tmpY);
- MultiSetAniXY(_ctx->pPlayObj, tmpX, tmpY);
+ GetActorPos(_ctx->reelActor, &_ctx->tmpX, &_ctx->tmpY);
+ MultiSetAniXY(_ctx->pPlayObj, _ctx->tmpX, _ctx->tmpY);
}
// If it's a moving actor, this hides the moving actor
// used to do this only if (actorid == 0) - I don't know why
_ctx->mActor = HideMovingActor(_ctx->reelActor, ppi->sf);
- // If it's a moving actor, get its MACTOR structure.
+ // If it's a moving actor, get its MOVER structure.
// If it isn't in the scene yet, get its task running - using
- // stand() - to prevent a glitch at the end of the play.
+ // Stand() - to prevent a glitch at the end of the play.
if (_ctx->mActor) {
- pActor = GetMover(_ctx->reelActor);
- if (getMActorState(pActor) == NO_MACTOR) {
- stand(_ctx->reelActor, MAGICX, MAGICY, 0);
+ _ctx->pActor = GetMover(_ctx->reelActor);
+ if (!getMActorState(_ctx->pActor)) {
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, _ctx->reelActor, MAGICX, MAGICY, 0));
bNewMover = true;
}
}
// Register the fact that we're playing this for this actor
- storeActorReel(_ctx->reelActor, _ctx->pfreel, ppi->hFilm, _ctx->pPlayObj, ppi->column, tmpX, tmpY);
+ storeActorReel(_ctx->reelActor, _ctx->pfreel, ppi->hFilm, _ctx->pPlayObj, ppi->column, _ctx->tmpX, _ctx->tmpY);
/**** Will get rid of this if the above is kept ****/
// We may be temporarily resuscitating a dead actor
@@ -247,23 +544,23 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
// N.B. It HAS been ensured that the first column gets here first
if (ppi->z != 0) {
MultiSetZPosition(_ctx->pPlayObj, ppi->z);
- storeActorZpos(_ctx->reelActor, ppi->z);
+ StoreActorZpos(_ctx->reelActor, ppi->z);
} else if (ppi->bTop) {
if (ppi->column == 0) {
firstColZ = Z_TOPPLAY + actorMaskType(_ctx->reelActor);
MultiSetZPosition(_ctx->pPlayObj, firstColZ);
- storeActorZpos(_ctx->reelActor, firstColZ);
+ StoreActorZpos(_ctx->reelActor, firstColZ);
} else {
MultiSetZPosition(_ctx->pPlayObj, firstColZ + ppi->column);
- storeActorZpos(_ctx->reelActor, firstColZ + ppi->column);
+ StoreActorZpos(_ctx->reelActor, firstColZ + ppi->column);
}
} else if (ppi->column == 0) {
if (_ctx->mActor && !bNewMover) {
// If no path, just use first path in the scene
- if (pActor->hCpath == NOPOLY)
- fColZfactor = getPolyZfactor(FirstPathPoly());
+ if (_ctx->pActor->hCpath == NOPOLY)
+ fColZfactor = GetPolyZfactor(FirstPathPoly());
else
- fColZfactor = getPolyZfactor(pActor->hCpath);
+ fColZfactor = GetPolyZfactor(_ctx->pActor->hCpath);
firstColZ = AsetZPos(_ctx->pPlayObj, MultiLowest(_ctx->pPlayObj), fColZfactor);
} else {
switch (actorMaskType(_ctx->reelActor)) {
@@ -293,14 +590,14 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
break;
}
}
- storeActorZpos(_ctx->reelActor, firstColZ);
+ StoreActorZpos(_ctx->reelActor, firstColZ);
} else {
if (NoNameFunc(_ctx->reelActor, bNewMover) > fColZfactor) {
fColZfactor = NoNameFunc(_ctx->reelActor, bNewMover);
firstColZ = fColZfactor << 10;
}
MultiSetZPosition(_ctx->pPlayObj, firstColZ + ppi->column);
- storeActorZpos(_ctx->reelActor, firstColZ + ppi->column);
+ StoreActorZpos(_ctx->reelActor, firstColZ + ppi->column);
}
/*
@@ -313,7 +610,7 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
do {
if (_ctx->stepCount++ == 0) {
_ctx->frameCount++;
- storeActorSteps(_ctx->reelActor, _ctx->frameCount);
+ StoreActorSteps(_ctx->reelActor, _ctx->frameCount);
}
if (_ctx->stepCount == ppi->speed)
_ctx->stepCount = 0;
@@ -323,7 +620,7 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
int x, y;
GetAniPosition(_ctx->pPlayObj, &x, &y);
- storeActorPos(_ctx->reelActor, x, y);
+ StoreActorPos(_ctx->reelActor, x, y);
CORO_SLEEP(1);
@@ -332,7 +629,7 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
break;
}
- if (actorEsc(_ctx->reelActor) && actorEev(_ctx->reelActor) != GetEscEvents())
+ if (ActorEsc(_ctx->reelActor) && ActorEev(_ctx->reelActor) != GetEscEvents())
break;
} while (_ctx->lifeNoMatter || actorAlive(_ctx->reelActor));
@@ -359,26 +656,308 @@ void PlayReel(CORO_PARAM, const PPINIT *ppi) {
}
/**
+ * - Don't bother if this reel is already playing for this actor.
+ * - If explicit co-ordinates, use these, If embedded co-ordinates,
+ * leave alone, otherwise use actor's current position.
+ * - Moving actors get hidden during this play, other actors get
+ * replaced by this play.
+ * - Column 0 of a film gets its appropriate Z-position, slave columns
+ * get slightly bigger Z-positions, in column order.
+ * - Play proceeds until the script finishes, another reel starts up for
+ * this actor, or the actor gets killed.
+ * - If called from an splay(), moving actor's co-ordinates are updated
+ * after the play, any walk still in progress will go on from there.
+ * @param x Co-ordinates from the play(), set to (-1, -1) if none
+ * @param y Co-ordinates from the play(), set to (-1, -1) if none
+ * @param bRestore Normally False, set if from restore
+ * @param speed Film speed
+ * @param hFilm The 'film'
+ * @param column Column number, first column = 0
+ */
+static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHANDLE hFilm,
+ int column, int myescEvent, bool bTop) {
+ CORO_BEGIN_CONTEXT;
+ bool bReplaced;
+ bool bGotHidden;
+ int stepCount;
+ int frameCount;
+ bool bEscapedAlready;
+ bool bPrinciple; // true if this is the first column in a film for one actor
+ bool bRelative; // true if relative specified in script
+
+ FREEL *pFreel;
+ MULTI_INIT *pmi; // MULTI_INIT structure
+ POBJECT pPlayObj; // Object
+ ANIM thisAnim; // Animation structure
+
+ int reelActor; // Which actor this reel belongs to
+ PMOVER pMover; // set if it's a moving actor
+ bool bNewMover; // Gets set if a moving actor that isn't in scene yet
+
+ int filmNumber;
+ int myZ; // Remember for hide/unhide
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bReplaced = false;
+ _ctx->bGotHidden = false;
+ _ctx->stepCount = 0;
+ _ctx->frameCount = 0;
+
+ _ctx->bEscapedAlready = false;
+
+ // Get the reel and MULTI_INIT structure
+ _ctx->pFreel = GetReel(hFilm, column);
+ _ctx->pmi = (MULTI_INIT *)LockMem(_ctx->pFreel->mobj);
+
+ if (_ctx->pmi->mulID == -2) {
+ CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, hFilm, column, speed, myescEvent,
+ _ctx->pmi->otherFlags & OTH_RELATEDACTOR));
+ return;
+ }
+
+ // Save actor's ID
+ _ctx->reelActor = _ctx->pmi->mulID;
+
+ UpdateActorEsc(_ctx->reelActor, myescEvent);
+
+ // To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
+ if (hFilm != GetActorLatestFilm(_ctx->reelActor)) {
+ // This in not the last film scheduled for this actor
+
+ // It may be the last non-talk film though
+ if (ActorIsTalking(_ctx->reelActor))
+ SetActorPlayFilm(_ctx->reelActor, hFilm); // Revert to this film after talk
+
+ return;
+ }
+ if (ActorIsTalking(_ctx->reelActor)) {
+ // Note: will delete this and there'll be no need to store the talk film!
+ if (hFilm != GetActorTalkFilm(_ctx->reelActor)) {
+ SetActorPlayFilm(_ctx->reelActor, hFilm); // Revert to this film after talk
+ return;
+ }
+ } else {
+ SetActorPlayFilm(_ctx->reelActor, hFilm);
+ }
+
+ // Register the film for this actor
+ if (hFilm != GetActorPresFilm(_ctx->reelActor)) {
+ _ctx->bPrinciple = true;
+ StoreActorPresFilm(_ctx->reelActor, hFilm, x, y);
+ } else {
+ _ctx->bPrinciple = false;
+
+ // If this reel is already playing for this actor, just forget it.
+ if (ActorReelPlaying(_ctx->reelActor, column))
+ return;
+ }
+
+ /*
+ * Insert the object
+ */
+ // Poke in the background palette
+ PokeInPalette(_ctx->pmi);
+
+ // Set ghost bit if wanted
+ if (ActorIsGhost(_ctx->reelActor)) {
+ assert(_ctx->pmi->mulFlags == DMA_WNZ || _ctx->pmi->mulFlags == (DMA_WNZ | DMA_GHOST));
+ _ctx->pmi->mulFlags |= DMA_GHOST;
+ }
+
+ // Set up and insert the multi-object
+ _ctx->pPlayObj = MultiInitObject(_ctx->pmi);
+ if (!bTop)
+ MultiInsertObject(GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
+ else
+ MultiInsertObject(GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
+
+ /*
+ * More action for moving actors
+ */
+ _ctx->pMover = GetMover(_ctx->reelActor);
+ if (_ctx->pMover != NULL) {
+ HideMover(_ctx->pMover);
+
+ if (!MoverIs(_ctx->pMover)) {
+ // Used to do a Stand here to prevent glitches
+
+ _ctx->bNewMover = true;
+ } else
+ _ctx->bNewMover = false;
+ }
+
+ // Register the reel for this actor
+ StoreActorReel(_ctx->reelActor, column, _ctx->pPlayObj);
+
+ _ctx->filmNumber = GetActorFilmNumber(_ctx->reelActor);
+
+ /*
+ * Sort out x and y
+ */
+ assert( ((_ctx->pmi->otherFlags & OTH_RELATIVE) && !(_ctx->pmi->otherFlags & OTH_ABSOLUTE))
+ || ((_ctx->pmi->otherFlags & OTH_ABSOLUTE) && !(_ctx->pmi->otherFlags & OTH_RELATIVE)) );
+
+ _ctx->bRelative = _ctx->pmi->otherFlags & OTH_RELATIVE;
+
+ if (_ctx->bRelative) {
+ // Use actor's position. If (x, y) specified, move the actor.
+ if (x == -1 && y == -1)
+ GetActorPos(_ctx->reelActor, &x, &y);
+ else
+ StoreActorPos(_ctx->reelActor, x, y);
+ } else if (x == -1 && y == -1)
+ x = y = 0; // Use (0,0) if no specified
+
+ // Add embedded co-ords
+ MultiSetAniXY(_ctx->pPlayObj, x + _ctx->pmi->mulX, y + _ctx->pmi->mulY);
+
+ /*
+ * Sort out z
+ */
+ if (bRestore) {
+ _ctx->myZ = GetActorZpos(_ctx->reelActor, column);
+
+ SoundReelWaitCheck();
+ } else {
+ static int baseZposn; // Z-position of column zero
+ static uint32 baseZfact; // Z-factor of column zero's actor
+
+ // N.B. It HAS been ensured that the first column gets here first
+
+ if (_ctx->pmi->mulZ != -1) {
+ // Z override in script
+
+ baseZfact = _ctx->pmi->mulZ;
+ baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj);
+ if (bTop)
+ baseZposn += Z_TOPPLAY;
+ } else if (column == 0
+ || GetZfactor(_ctx->reelActor, _ctx->pMover, _ctx->bNewMover) > baseZfact) {
+ // Subsequent columns are based on this one
+
+ baseZfact = GetZfactor(_ctx->reelActor, _ctx->pMover, _ctx->bNewMover);
+ baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj);
+ if (bTop)
+ baseZposn += Z_TOPPLAY;
+ }
+ _ctx->myZ = baseZposn + column;
+ }
+ MultiSetZPosition(_ctx->pPlayObj, _ctx->myZ);
+ StoreActorZpos(_ctx->reelActor, _ctx->myZ, column);
+
+ /*
+ * Play until the script finishes,
+ * another reel starts up for this actor,
+ * or the actor gets killed.
+ */
+ InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, _ctx->pFreel->script, speed);
+
+ if (bRestore || (ActorEsc(_ctx->reelActor) == true &&
+ ActorEev(_ctx->reelActor) != GetEscEvents())) {
+ // From restore, step to jump or end
+ SkipFrames(&_ctx->thisAnim, -1);
+ }
+
+ for (;;) {
+ if (_ctx->stepCount++ == 0) {
+ _ctx->frameCount++;
+ StoreActorSteps(_ctx->reelActor, _ctx->frameCount);
+ }
+ if (_ctx->stepCount == speed)
+ _ctx->stepCount = 0;
+
+ if (_ctx->bPrinciple && AboutToJumpOrEnd(&_ctx->thisAnim))
+ IncLoopCount(_ctx->reelActor);
+
+ if (StepAnimScript(&_ctx->thisAnim) == ScriptFinished)
+ break;
+
+ if (_ctx->bRelative) {
+ GetAniPosition(_ctx->pPlayObj, &x, &y);
+ StoreActorPos(_ctx->reelActor, x, y);
+ }
+
+ if (_ctx->bGotHidden) {
+ if (!ActorHidden(_ctx->reelActor)) {
+ MultiSetZPosition(_ctx->pPlayObj, _ctx->myZ);
+ _ctx->bGotHidden = false;
+ }
+ } else {
+ if (ActorHidden(_ctx->reelActor)) {
+ MultiSetZPosition(_ctx->pPlayObj, -1);
+ _ctx->bGotHidden = true;
+ }
+ }
+
+ CORO_SLEEP(1);
+
+ if (GetActorFilmNumber(_ctx->reelActor) != _ctx->filmNumber) {
+ _ctx->bReplaced = true;
+ break;
+ }
+
+ if (ActorEsc(_ctx->reelActor) == true && ActorEev(_ctx->reelActor) != GetEscEvents()) {
+ if (!_ctx->bEscapedAlready) {
+ SkipFrames(&_ctx->thisAnim, -1);
+ _ctx->bEscapedAlready = true;
+ }
+
+//WHY??? UpdateActorEsc(reelActor, GetEscEvents());
+// The above line of code, not commented out would fix the coffee pot flash
+// but why was it commented out?
+// The extra boolean is used instead, 'cos it's release week and I want to play it safe!
+ }
+ }
+
+ // Register the fact that we're NOT playing this for this actor
+ NotPlayingReel(_ctx->reelActor, _ctx->filmNumber, column);
+
+ // Ditch the object
+ if (!bTop)
+ MultiDeleteObject(GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
+ else
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
+
+ // Restore moving actor is nessesary
+ if (_ctx->pMover != NULL && _ctx->bPrinciple && !_ctx->bReplaced)
+ UnHideMover(_ctx->pMover);
+
+ CORO_END_CODE;
+}
+
+/**
* Run all animations that comprise the play film.
*/
-static void playProcess(CORO_PARAM, const void *param) {
- // get the stuff copied to process when it was created
- PPINIT *ppi = (PPINIT *)param;
+static void PlayProcess(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
- PlayReel(coroParam, ppi);
+ const PPINIT *ppi = (const PPINIT *)param;
+ CORO_BEGIN_CODE(_ctx);
+
+ if (TinselV2)
+ CORO_INVOKE_ARGS(t2PlayReel, (CORO_SUBCTX, ppi->x, ppi->y, ppi->bRestore, ppi->speed,
+ ppi->hFilm, ppi->column, ppi->myescEvent, ppi->bTop));
+ else
+ CORO_INVOKE_1(t1PlayReel, ppi);
+
+ CORO_END_CODE;
}
// *******************************************************
// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
-void newestFilm(SCNHANDLE film, const FREEL *reel) {
+void NewestFilm(SCNHANDLE film, const FREEL *reel) {
const MULTI_INIT *pmi; // MULTI_INIT structure
// Get the MULTI_INIT structure
pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(reel->mobj));
- setActorLatestFilm((int32)FROM_LE_32(pmi->mulID), film);
+ if (!TinselV2 || ((int32)FROM_LE_32(pmi->mulID) != -2))
+ SetActorLatestFilm((int32)FROM_LE_32(pmi->mulID), film);
}
// *******************************************************
@@ -389,64 +968,90 @@ void newestFilm(SCNHANDLE film, const FREEL *reel) {
* NOTE: The processes are started in reverse order so that the first
* column's process kicks in first.
*/
-void playFilm(SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact, bool escOn,
+void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact, bool escOn,
int myescEvent, bool bTop) {
- const FILM *pfilm = (const FILM *)LockMem(film);
- PPINIT ppi;
+ assert(hFilm != 0); // Trying to play NULL film
+ const FILM *pFilm;
+
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
- assert(film != 0); // Trying to play NULL film
+ CORO_BEGIN_CODE(_ctx);
+
+ pFilm = (const FILM *)LockMem(hFilm);
+ PPINIT ppi;
// Now allowed empty films!
- if (pfilm->numreels == 0)
+ if (pFilm->numreels == 0)
return; // Nothing to do!
- ppi.hFilm = film;
+ ppi.hFilm = hFilm;
ppi.x = x;
ppi.y = y;
ppi.z = 0;
- ppi.speed = (ONE_SECOND / FROM_LE_32(pfilm->frate));
+ ppi.bRestore = false;
+ ppi.speed = (ONE_SECOND / FROM_LE_32(pFilm->frate));
ppi.actorid = actorid;
ppi.splay = splay;
ppi.bTop = bTop;
ppi.sf = sfact;
ppi.escOn = escOn;
- ppi.myescEvent = myescEvent;
+ ppi.myescEvent = myescEvent;
// Start display process for each reel in the film
- for (int i = FROM_LE_32(pfilm->numreels) - 1; i >= 0; i--) {
- newestFilm(film, &pfilm->reels[i]);
+ for (int i = FROM_LE_32(pFilm->numreels) - 1; i >= 0; i--) {
+ NewestFilm(hFilm, &pFilm->reels[i]);
ppi.column = i;
- g_scheduler->createProcess(PID_REEL, playProcess, &ppi, sizeof(PPINIT));
+ g_scheduler->createProcess(PID_REEL, PlayProcess, &ppi, sizeof(PPINIT));
}
+
+ if (TinselV2) {
+ // Let it all kick in and position this process
+ // down the process list from the playing process(es)
+ // This ensures something
+ CORO_GIVE_WAY;
+
+ if (myescEvent && myescEvent != GetEscEvents())
+ g_scheduler->rescheduleAll();
+ }
+
+ CORO_END_CODE;
+}
+
+void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop) {
+ PlayFilm(coroParam, hFilm, x, y, 0, false, false, false, myescEvent, bTop);
}
/**
* Start up a play process for each slave column in a film.
* Play the first column directly from the parent process.
*/
-void playFilmc(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact,
+void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact,
bool escOn, int myescEvent, bool bTop) {
CORO_BEGIN_CONTEXT;
PPINIT ppi;
+ int i;
+ int loopCount;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- assert(film != 0); // Trying to play NULL film
- const FILM *pfilm;
+ assert(hFilm != 0); // Trying to play NULL film
+ const FILM *pFilm;
- pfilm = (const FILM *)LockMem(film);
+ pFilm = (const FILM *)LockMem(hFilm);
// Now allowed empty films!
- if (pfilm->numreels == 0)
+ if (pFilm->numreels == 0)
return; // Already played to completion!
- _ctx->ppi.hFilm = film;
+ _ctx->ppi.hFilm = hFilm;
_ctx->ppi.x = x;
_ctx->ppi.y = y;
_ctx->ppi.z = 0;
- _ctx->ppi.speed = (ONE_SECOND / FROM_LE_32(pfilm->frate));
+ _ctx->ppi.bRestore = false;
+ _ctx->ppi.speed = (ONE_SECOND / FROM_LE_32(pFilm->frate));
_ctx->ppi.actorid = actorid;
_ctx->ppi.splay = splay;
_ctx->ppi.bTop = bTop;
@@ -454,18 +1059,40 @@ void playFilmc(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay
_ctx->ppi.escOn = escOn;
_ctx->ppi.myescEvent = myescEvent;
- // Start display process for each secondary reel in the film
- for (int i = FROM_LE_32(pfilm->numreels) - 1; i > 0; i--) {
- newestFilm(film, &pfilm->reels[i]);
+ // Start display process for each secondary reel in the film in Tinsel 1,
+ // or all of them in Tinsel 2
+ for (int i = FROM_LE_32(pFilm->numreels) - 1; i >= (TinselV2 ? 0 : 1); i--) {
+ NewestFilm(hFilm, &pFilm->reels[i]);
_ctx->ppi.column = i;
- g_scheduler->createProcess(PID_REEL, playProcess, &_ctx->ppi, sizeof(PPINIT));
+ g_scheduler->createProcess(PID_REEL, PlayProcess, &_ctx->ppi, sizeof(PPINIT));
}
- newestFilm(film, &pfilm->reels[0]);
+ if (TinselV2) {
+ // Let it all kick in and position this 'waiting' process
+ // down the process list from the playing process(es)
+ // This ensures immediate return when the reel finishes
+ CORO_GIVE_WAY;
- _ctx->ppi.column = 0;
- CORO_INVOKE_1(PlayReel, &_ctx->ppi);
+ _ctx->i = ExtractActor(hFilm);
+ _ctx->loopCount = GetLoopCount(_ctx->i);
+
+ // Wait until film changes or loop count increases
+ while (GetActorPresFilm(_ctx->i) == hFilm && GetLoopCount(_ctx->i) == _ctx->loopCount) {
+ if (myescEvent && myescEvent != GetEscEvents()) {
+ g_scheduler->rescheduleAll();
+ break;
+ }
+
+ CORO_SLEEP(1);
+ }
+ } else {
+ // For Tinsel 1, launch the primary reel
+ NewestFilm(hFilm, &pFilm->reels[0]);
+
+ _ctx->ppi.column = 0;
+ CORO_INVOKE_1(t1PlayReel, &_ctx->ppi);
+ }
CORO_END_CODE;
}
@@ -473,13 +1100,14 @@ void playFilmc(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay
/**
* Start up a play process for a particular column in a film.
*
- * NOTE: This is specifically for actors during a restore scene.
+ * NOTE: This is specifically for actors during a Tinsel 1 restore scene.
*/
-void playThisReel(SCNHANDLE film, short reelnum, short z, int x, int y) {
- const FILM *pfilm = (const FILM *)LockMem(film);
+void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y) {
+ assert(!TinselV2);
+ const FILM *pfilm = (const FILM *)LockMem(hFilm);
PPINIT ppi;
- ppi.hFilm = film;
+ ppi.hFilm = hFilm;
ppi.x = x;
ppi.y = y;
ppi.z = z;
@@ -487,21 +1115,58 @@ void playThisReel(SCNHANDLE film, short reelnum, short z, int x, int y) {
ppi.actorid = 0;
ppi.splay = false;
ppi.bTop = false;
+ ppi.bRestore = true;
ppi.sf = 0;
ppi.column = reelnum;
+ ppi.myescEvent = 0;
- // FIXME: The PlayReel play loop was previously breaking out, and then deleting objects, when
- // returning to a scene because escOn and myescEvent were undefined. Need to make sure whether
- // restored objects should have any particular combination of these two values
ppi.escOn = false;
ppi.myescEvent = GetEscEvents();
assert(pfilm->numreels);
- newestFilm(film, &pfilm->reels[reelnum]);
+ NewestFilm(hFilm, &pfilm->reels[reelnum]);
// Start display process for the reel
- g_scheduler->createProcess(PID_REEL, playProcess, &ppi, sizeof(ppi));
+ g_scheduler->createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
+}
+
+/**
+ * Start up a play process for a particular column in a film.
+ *
+ * NOTE: This is specifically for actors during a Tinsel 2 restore scene.
+ */
+void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y) {
+ assert(TinselV2);
+ FILM *pFilm = (FILM *)LockMem(hFilm);
+ PPINIT ppi;
+
+ int i;
+ FREEL *pFreel;
+ PMULTI_INIT pmi; // MULTI_INIT structure
+
+ ppi.hFilm = hFilm;
+ ppi.x = (short)x;
+ ppi.y = (short)y;
+ ppi.bRestore = true;
+ ppi.speed = (short)(ONE_SECOND/pFilm->frate);
+ ppi.bTop = false;
+ ppi.myescEvent = 0;
+
+ // Search backwards for now as later column will be the one
+ for (i = pFilm->numreels - 1; i >= 0; i--) {
+ pFreel = &pFilm->reels[i];
+ pmi = (PMULTI_INIT) LockMem(pFreel->mobj);
+ if (pmi->mulID == actor) {
+ ppi.column = (short)i;
+ NewestFilm(hFilm, &pFilm->reels[i]);
+
+ // Start display process for the reel
+ g_scheduler->createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
+
+ soundReelWait++;
+ }
+ }
}
} // end of namespace Tinsel
diff --git a/engines/tinsel/play.h b/engines/tinsel/play.h
new file mode 100644
index 0000000000..5cdca25faa
--- /dev/null
+++ b/engines/tinsel/play.h
@@ -0,0 +1,64 @@
+/* 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$
+ *
+ * Plays films within a scene, takes into account the actor in each 'column'.
+ */
+
+#ifndef TINSEL_PLAY_H // prevent multiple includes
+#define TINSEL_PLAY_H
+
+#include "tinsel/coroutine.h"
+#include "tinsel/dw.h"
+#include "tinsel/multiobj.h"
+
+namespace Tinsel {
+
+#define MAX_SOUNDREELS 5
+
+struct SOUNDREELS {
+ SCNHANDLE hFilm; // The 'film'
+ int column; // Column number
+ int actorCol;
+};
+typedef SOUNDREELS *PSOUNDREELS;
+
+void PlayFilm(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, bool sfact, bool escOn,
+ int myescEvent, bool bTop);
+
+void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop);
+
+void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact,
+ bool escOn, int myescEvent, bool bTop);
+
+void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y);
+void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y);
+
+void PokeInPalette(const MULTI_INIT *pmi);
+
+void NoSoundReels(void);
+void SaveSoundReels(PSOUNDREELS psr);
+void RestoreSoundReels(PSOUNDREELS psr);
+
+} // end of namespace Tinsel
+
+#endif
diff --git a/engines/tinsel/polygons.cpp b/engines/tinsel/polygons.cpp
index d73e290277..30d3565c00 100644
--- a/engines/tinsel/polygons.cpp
+++ b/engines/tinsel/polygons.cpp
@@ -25,9 +25,13 @@
#include "tinsel/actors.h"
#include "tinsel/font.h"
#include "tinsel/handle.h"
+#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/tinsel.h"
#include "tinsel/token.h"
#include "common/util.h"
@@ -43,14 +47,13 @@ enum POLY_TYPE {
POLY_EXIT, POLY_TAG
};
-
// Note 7/10/94, with adjacency reduction ANKHMAP max is 3, UNSEEN max is 4
// so reduced this back to 6 (from 12) for now.
#define MAXADJ 6 // Max number of known adjacent paths
struct POLYGON {
- PTYPE polytype; // Polygon type
+ PTYPE polyType; // Polygon type
int subtype; // refer type in REFER polygons
// NODE/NORMAL in PATH polygons
@@ -64,10 +67,13 @@ struct POLYGON {
short cy[4];
int polyID;
+ /* For TAG polygons only */
+ int tagFlags;
+ SCNHANDLE hOverrideTag;
+
/* For TAG and EXIT (and EFFECT in future?) polygons only */
TSTATE tagState;
PSTATE pointState;
- SCNHANDLE oTagHandle; // Override tag.
/* For Path polygons only */
bool tried;
@@ -104,7 +110,7 @@ struct POLYGON {
POLYGON *adjpaths[MAXADJ];
};
-
+typedef POLYGON *PPOLYGON;
#define MAXONROUTE 40
@@ -127,10 +133,25 @@ struct LINEINFO {
int32 bc;
} PACKED_STRUCT;
-/** polygon struct - one per polygon */
-struct POLY {
- int32 type; //!< type of polygon
+#include "common/pack-end.h" // END STRUCT PACKING
+
+// POLY structure class. This is implemented as a class, because the structure
+// of POLY's changed between TINSEL v1 and v2
+class Poly {
+private:
+ const byte *_pStart;
+ const byte *_pData;
+ int _recordSize;
+ void nextPoly();
+public:
+ Poly(const byte *pSrc);
+ Poly(const byte *pSrc, int startIndex);
+ void operator++();
+ void setIndex(int index);
+
+ POLY_TYPE type; //!< type of polygon
int32 x[4], y[4]; // Polygon definition
+ uint32 xoff, yoff; // DW2 - polygon offset
int32 tagx, tagy; // } For tagged polygons
SCNHANDLE hTagtext; // } i.e. EXIT, TAG, EFFECT
@@ -143,6 +164,8 @@ struct POLY {
int32 id; // } EXIT and TAG
int32 scale1, scale2; // }
+ int32 level1, level2; // DW2 fields
+ int32 bright1, bright2; // DW2 fields
int32 reel; // } PATH and NPATH
int32 zFactor; // }
@@ -152,10 +175,86 @@ struct POLY {
int32 plinelist;
SCNHANDLE hScript; //!< handle of code segment for polygon events
-} PACKED_STRUCT;
+};
-#include "common/pack-end.h" // END STRUCT PACKING
+Poly::Poly(const byte *pSrc) {
+ _pStart = pSrc;
+ _pData = pSrc;
+ nextPoly();
+ _recordSize = _pData - pSrc;
+}
+
+Poly::Poly(const byte *pSrc, int startIndex) {
+ _pStart = pSrc;
+ _pData = pSrc;
+ nextPoly();
+ _recordSize = _pData - pSrc;
+ setIndex(startIndex);
+}
+void Poly::operator++() {
+ nextPoly();
+}
+
+void Poly::setIndex(int index) {
+ _pData = _pStart + index * _recordSize;
+ nextPoly();
+}
+
+uint32 nextLong(const byte *&p) {
+ uint32 result = *((const uint32 *)p);
+ p += 4;
+ return result;
+}
+
+void Poly::nextPoly() {
+ // Note: Originally I used a Serialiser, but dropped because for now we want
+ // the endian to remain as it is. It may be cleaner to later on switch to using
+ // it, and removing all endian conversions from the code that uses POLY's
+ int typeVal = nextLong(_pData);
+ if ((typeVal == 5) && TinselV2) ++typeVal;
+ type = (POLY_TYPE)typeVal;
+
+ for (int i = 0; i < 4; ++i) x[i] = nextLong(_pData);
+ for (int i = 0; i < 4; ++i) y[i] = nextLong(_pData);
+
+ if (TinselVersion == TINSEL_V2) {
+ xoff = nextLong(_pData);
+ yoff = nextLong(_pData);
+ id = nextLong(_pData);
+ reftype = nextLong(_pData);
+ }
+
+ tagx = nextLong(_pData);
+ tagy = nextLong(_pData);
+ hTagtext = nextLong(_pData);
+ nodex = nextLong(_pData);
+ nodey = nextLong(_pData);
+ hFilm = nextLong(_pData);
+
+ if (TinselVersion != TINSEL_V2) {
+ reftype = nextLong(_pData);
+ id = nextLong(_pData);
+ }
+
+ scale1 = nextLong(_pData);
+ scale2 = nextLong(_pData);
+
+ if (TinselVersion == TINSEL_V2) {
+ level1 = nextLong(_pData);
+ level2 = nextLong(_pData);
+ bright1 = nextLong(_pData);
+ bright2 = nextLong(_pData);
+ }
+
+ reel = nextLong(_pData);
+ zFactor = nextLong(_pData);
+ nodecount = nextLong(_pData);
+ pnodelistx = nextLong(_pData);
+ pnodelisty = nextLong(_pData);
+ plinelist = nextLong(_pData);
+ hScript = nextLong(_pData);
+}
//----------------- LOCAL GLOBAL DATA --------------------
@@ -179,7 +278,8 @@ static POLYGON *RouteEnd = 0;
int highestYet = 0;
#endif
-
+// dead/alive, offsets
+static POLY_VOLATILE volatileStuff[MAX_POLY];
//----------------- LOCAL MACROS --------------------
@@ -187,14 +287,15 @@ int highestYet = 0;
#define CHECK_HP_OR(mvar, str) assert((mvar >= 0 && mvar <= noofPolys) || mvar == MAX_POLY);
#define CHECK_HP(mvar, str) assert(mvar >= 0 && mvar <= noofPolys);
-static HPOLYGON PolyIndex(const POLYGON *pp) {
- for (int j = 0; j <= MAX_POLY; j++) {
- if (Polys[j] == pp)
- return j;
+//-------------------- METHODS ----------------------
+
+static HPOLYGON PolygonIndex(const POLYGON *pp) {
+ for (int i = 0; i <= MAX_POLY; ++i) {
+ if (Polys[i] == pp)
+ return i;
}
- error("PolyIndex(): polygon not found");
- return NOPOLY;
+ error("PolygonIndex(): polygon not found");
}
/**
@@ -234,6 +335,12 @@ bool IsInPolygon(int xt, int yt, HPOLYGON hp) {
pp = Polys[hp];
assert(pp != NULL); // Testing whether in a NULL polygon
+ // Shift cursor for relative polygons
+ if (TinselV2) {
+ xt -= volatileStuff[hp].xoff;
+ yt -= volatileStuff[hp].yoff;
+ }
+
/* Is point within the external rectangle? */
if (xt < pp->pleft || xt > pp->pright || yt < pp->ptop || yt > pp->pbottom)
return false;
@@ -254,7 +361,7 @@ bool IsInPolygon(int xt, int yt, HPOLYGON hp) {
if (BeenTested) {
// New dodgy code 29/12/94
- if (pp->polytype == BLOCKING) {
+ if (pp->polyType == BLOCK) {
// For each corner/side
for (i = 0; i < 4; i++) {
// Pretend the corners of blocking polys are not in the poly.
@@ -285,7 +392,7 @@ bool IsInPolygon(int xt, int yt, HPOLYGON hp) {
HPOLYGON InPolygon(int xt, int yt, PTYPE type) {
for (int j = 0; j <= MAX_POLY; j++) {
- if (Polys[j] && Polys[j]->polytype == type) {
+ if (Polys[j] && Polys[j]->polyType == type) {
if (IsInPolygon(xt, yt, j))
return j;
}
@@ -434,7 +541,6 @@ void FindBestPoint(HPOLYGON hp, int *x, int *y, int *pline) {
const POLYGON *pp;
uint8 *pps; // Compiled polygon data
- const POLY *ptp; // Compiled polygon data
int dropD; // length of perpendicular (i.e. distance of point from line)
int dropX, dropY; // (X, Y) where dropped perpendicular intersects the line
int d1, d2; // distance from perpendicular intersect to line's end nodes
@@ -453,14 +559,14 @@ void FindBestPoint(HPOLYGON hp, int *x, int *y, int *pline) {
// Pointer to polygon data
pps = LockMem(pHandle); // All polygons
- ptp = (const POLY *)pps + pp->pIndex; // This polygon
+ Poly ptp(pps, pp->pIndex); // This polygon
- nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelistx));
- nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelisty));
- llist = (LINEINFO *)(pps + (int)FROM_LE_32(ptp->plinelist));
+ nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelistx));
+ nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelisty));
+ llist = (LINEINFO *)(pps + (int)FROM_LE_32(ptp.plinelist));
// Look for fit of perpendicular to lines between nodes
- for (int i = 0; i < (int)FROM_LE_32(ptp->nodecount) - 1; i++) {
+ for (int i = 0; i < (int)FROM_LE_32(ptp.nodecount) - 1; i++) {
const int32 a = (int)FROM_LE_32(llist[i].a);
const int32 b = (int)FROM_LE_32(llist[i].b);
const int32 c = (int)FROM_LE_32(llist[i].c);
@@ -640,7 +746,7 @@ static HPOLYGON PathOnTheWay(HPOLYGON from, HPOLYGON to) {
for (i = 0; i < MAX_POLY; i++) { // For each polygon..
POLYGON *p = Polys[i];
- if (p && p->polytype == PATH) //...if it's a path
+ if (p && p->polyType == PATH) //...if it's a path
p->tried = false;
}
Polys[from]->tried = true;
@@ -648,31 +754,34 @@ static HPOLYGON PathOnTheWay(HPOLYGON from, HPOLYGON to) {
const POLYGON *p = TryPath(Polys[from], Polys[to], Polys[from]);
+ if (TinselV2 && !p)
+ return NOPOLY;
+
assert(p != NULL); // Trying to find route between unconnected paths
// Don't go a roundabout way to an adjacent path.
for (i = 0; i < pathsOnRoute; i++) {
- CHECK_HP(PolyIndex(RoutePaths[i]), "Out of range polygon handle (502)");
- if (IsAdjacentPath(from, PolyIndex(RoutePaths[i])))
- return PolyIndex(RoutePaths[i]);
+ CHECK_HP(PolygonIndex(RoutePaths[i]), "Out of range polygon handle (502)");
+ if (IsAdjacentPath(from, PolygonIndex(RoutePaths[i])))
+ return PolygonIndex(RoutePaths[i]);
}
- return PolyIndex(p);
+ return PolygonIndex(p);
}
/**
* Indirect method of calling PathOnTheWay(), to put the burden of
* recursion onto the main stack.
*/
-HPOLYGON getPathOnTheWay(HPOLYGON hFrom, HPOLYGON hTo) {
+HPOLYGON GetPathOnTheWay(HPOLYGON hFrom, HPOLYGON hTo) {
CHECK_HP(hFrom, "Out of range polygon handle (6)");
CHECK_HP(hTo, "Out of range polygon handle (7)");
// Reuse already computed result
if (RouteEnd == Polys[hTo]) {
for (int i = 0; i < pathsOnRoute; i++) {
- CHECK_HP(PolyIndex(RoutePaths[i]), "Out of range polygon handle (503)");
- if (IsAdjacentPath(hFrom, PolyIndex(RoutePaths[i]))) {
- return PolyIndex(RoutePaths[i]);
+ CHECK_HP(PolygonIndex(RoutePaths[i]), "Out of range polygon handle (503)");
+ if (IsAdjacentPath(hFrom, PolygonIndex(RoutePaths[i]))) {
+ return PolygonIndex(RoutePaths[i]);
}
}
}
@@ -691,19 +800,18 @@ int NearestEndNode(HPOLYGON hPath, int x, int y) {
int d1, d2;
uint8 *pps; // Compiled polygon data
- const POLY *ptp; // Pointer to compiled polygon data
int32 *nlistx, *nlisty;
CHECK_HP(hPath, "Out of range polygon handle (8)");
pp = Polys[hPath];
pps = LockMem(pHandle); // All polygons
- ptp = (const POLY *)pps + pp->pIndex; // This polygon
+ Poly ptp(pps, pp->pIndex); // This polygon
- nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelistx));
- nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelisty));
+ nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelistx));
+ nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelisty));
- const int nodecount = (int)FROM_LE_32(ptp->nodecount);
+ const int nodecount = (int)FROM_LE_32(ptp.nodecount);
d1 = ABS(x - (int)FROM_LE_32(nlistx[0])) + ABS(y - (int)FROM_LE_32(nlisty[0]));
d2 = ABS(x - (int)FROM_LE_32(nlistx[nodecount - 1])) + ABS(y - (int)FROM_LE_32(nlisty[nodecount - 1]));
@@ -725,7 +833,6 @@ int NearEndNode(HPOLYGON hSpath, HPOLYGON hDpath) {
int dist, NearDist;
int NearNode;
uint8 *pps; // Compiled polygon data
- const POLY *ps, *pd; // Pointer to compiled polygon data
int32 *snlistx, *snlisty;
int32 *dnlistx, *dnlisty;
@@ -735,16 +842,16 @@ int NearEndNode(HPOLYGON hSpath, HPOLYGON hDpath) {
pDpath = Polys[hDpath];
pps = LockMem(pHandle); // All polygons
- ps = (const POLY *)pps + pSpath->pIndex; // Start polygon
- pd = (const POLY *)pps + pDpath->pIndex; // Dest polygon
+ Poly ps(pps, pSpath->pIndex); // Start polygon
+ Poly pd(pps, pDpath->pIndex); // Dest polygon
- ns = (int)FROM_LE_32(ps->nodecount) - 1;
- nd = (int)FROM_LE_32(pd->nodecount) - 1;
+ ns = (int)FROM_LE_32(ps.nodecount) - 1;
+ nd = (int)FROM_LE_32(pd.nodecount) - 1;
- snlistx = (int32 *)(pps + (int)FROM_LE_32(ps->pnodelistx));
- snlisty = (int32 *)(pps + (int)FROM_LE_32(ps->pnodelisty));
- dnlistx = (int32 *)(pps + (int)FROM_LE_32(pd->pnodelistx));
- dnlisty = (int32 *)(pps + (int)FROM_LE_32(pd->pnodelisty));
+ snlistx = (int32 *)(pps + (int)FROM_LE_32(ps.pnodelistx));
+ snlisty = (int32 *)(pps + (int)FROM_LE_32(ps.pnodelisty));
+ dnlistx = (int32 *)(pps + (int)FROM_LE_32(pd.pnodelistx));
+ dnlisty = (int32 *)(pps + (int)FROM_LE_32(pd.pnodelisty));
// start[0] to dest[0]
NearDist = ABS((int)FROM_LE_32(snlistx[0]) - (int)FROM_LE_32(dnlistx[0])) + ABS((int)FROM_LE_32(snlisty[0]) - (int)FROM_LE_32(dnlisty[0]));
@@ -780,18 +887,17 @@ int NearestNodeWithin(HPOLYGON hNpath, int x, int y) {
int NumNodes; // Number of nodes in this follow nodes path
int NearestYet = 0; // Number of nearest node
uint8 *pps; // Compiled polygon data
- const POLY *ptp; // Pointer to compiled polygon data
int32 *nlistx, *nlisty;
CHECK_HP(hNpath, "Out of range polygon handle (11)");
pps = LockMem(pHandle); // All polygons
- ptp = (const POLY *)pps + Polys[hNpath]->pIndex; // This polygon
+ Poly ptp(pps, Polys[hNpath]->pIndex); // This polygon
- nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelistx));
- nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelisty));
+ nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelistx));
+ nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelisty));
- NumNodes = (int)FROM_LE_32(ptp->nodecount);
+ NumNodes = (int)FROM_LE_32(ptp.nodecount);
for (int i = 0; i < NumNodes; i++) {
ThisDistance = ABS(x - (int)FROM_LE_32(nlistx[i])) + ABS(y - (int)FROM_LE_32(nlisty[i]));
@@ -875,7 +981,6 @@ bool IsPolyCorner(HPOLYGON hPath, int x, int y) {
* Given a path polygon and a Y co-ordinate, return a scale value.
*/
int GetScale(HPOLYGON hPath, int y) {
- const POLY *ptp; // Pointer to compiled polygon data
int zones; // Number of different scales
int zlen; // Depth of each scale zone
int scale;
@@ -887,18 +992,18 @@ int GetScale(HPOLYGON hPath, int y) {
CHECK_HP(hPath, "Out of range polygon handle (14)");
- ptp = (const POLY *)LockMem(pHandle) + Polys[hPath]->pIndex;
+ Poly ptp(LockMem(pHandle), Polys[hPath]->pIndex);
// Path is of a constant scale?
- if (FROM_LE_32(ptp->scale2) == 0)
- return FROM_LE_32(ptp->scale1);
+ if (FROM_LE_32(ptp.scale2) == 0)
+ return FROM_LE_32(ptp.scale1);
- assert(FROM_LE_32(ptp->scale1) >= FROM_LE_32(ptp->scale2));
+ assert(FROM_LE_32(ptp.scale1) >= FROM_LE_32(ptp.scale2));
- zones = FROM_LE_32(ptp->scale1) - FROM_LE_32(ptp->scale2) + 1;
+ zones = FROM_LE_32(ptp.scale1) - FROM_LE_32(ptp.scale2) + 1;
zlen = (Polys[hPath]->pbottom - Polys[hPath]->ptop) / zones;
- scale = FROM_LE_32(ptp->scale1);
+ scale = FROM_LE_32(ptp.scale1);
top = Polys[hPath]->ptop;
do {
@@ -907,28 +1012,67 @@ int GetScale(HPOLYGON hPath, int y) {
return scale;
} while (--scale);
- return FROM_LE_32(ptp->scale2);
+ return FROM_LE_32(ptp.scale2);
}
/**
+ * Given a path polygon and a Y co-ordinate, return a brightness value.
+ */
+
+int GetBrightness(HPOLYGON hPath, int y) {
+ int zones; // Number of different brightnesses
+ int zlen; // Depth of each brightness zone
+ int brightness;
+ int top;
+
+ // To try and fix some unknown potential bug
+ if (hPath == NOPOLY)
+ return 10;
+
+ CHECK_HP(hPath, "Out of range polygon handle (38)");
+
+ Poly pp(LockMem(pHandle), Polys[hPath]->pIndex);
+
+ // Path is of a constant brightness?
+ if (pp.bright1 == pp.bright2)
+ return pp.bright1;
+
+ assert(FROM_LE_32(pp.bright1) >= FROM_LE_32(pp.bright2));
+
+ zones = FROM_LE_32(pp.bright1) - FROM_LE_32(pp.bright2) + 1;
+ zlen = (Polys[hPath]->pbottom - Polys[hPath]->ptop) / zones;
+
+ brightness = FROM_LE_32(pp.bright1);
+ top = Polys[hPath]->ptop;
+
+ do {
+ top += zlen;
+ if (y < top)
+ return brightness;
+ } while(--brightness);
+
+ return FROM_LE_32(pp.bright2);
+}
+
+
+/**
* Give the co-ordinates of a node in a node path.
*/
void getNpathNode(HPOLYGON hNpath, int node, int *px, int *py) {
uint8 *pps; // Compiled polygon data
- const POLY *ptp; // Pointer to compiled polygon data
int32 *nlistx, *nlisty;
CHECK_HP(hNpath, "Out of range polygon handle (15)");
- assert(Polys[hNpath] != NULL && Polys[hNpath]->polytype == PATH && Polys[hNpath]->subtype == NODE); // must be given a node path!
+ assert(Polys[hNpath] != NULL && Polys[hNpath]->polyType == PATH && Polys[hNpath]->subtype == NODE); // must be given a node path!
pps = LockMem(pHandle); // All polygons
- ptp = (const POLY *)pps + Polys[hNpath]->pIndex; // This polygon
+ Poly ptp(pps, Polys[hNpath]->pIndex); // This polygon
- nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelistx));
- nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp->pnodelisty));
+ nlistx = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelistx));
+ nlisty = (int32 *)(pps + (int)FROM_LE_32(ptp.pnodelisty));
// Might have just walked to the node from above.
- if (node == (int)FROM_LE_32(ptp->nodecount))
+ if (node == (int)FROM_LE_32(ptp.nodecount))
node -= 1;
*px = (int)FROM_LE_32(nlistx[node]);
@@ -936,98 +1080,68 @@ void getNpathNode(HPOLYGON hNpath, int node, int *px, int *py) {
}
/**
- * Get tag text handle and tag co-ordinates of a polygon.
+ * Get compiled tag text handle and tag co-ordinates of a tag polygon.
*/
-
-void getPolyTagInfo(HPOLYGON hp, SCNHANDLE *hTagText, int *tagx, int *tagy) {
- const POLY *pp; // Pointer to compiled polygon data
-
+void GetTagTag(HPOLYGON hp, SCNHANDLE *hTagText, int *tagx, int *tagy) {
CHECK_HP(hp, "Out of range polygon handle (16)");
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
- *tagx = (int)FROM_LE_32(pp->tagx);
- *tagy = (int)FROM_LE_32(pp->tagy);
- *hTagText = FROM_LE_32(pp->hTagtext);
+ *tagx = (int)FROM_LE_32(pp.tagx) + (TinselV2 ? volatileStuff[hp].xoff : 0);
+ *tagy = (int)FROM_LE_32(pp.tagy) + (TinselV2 ? volatileStuff[hp].yoff : 0);
+ *hTagText = FROM_LE_32(pp.hTagtext);
}
/**
* Get polygon's film reel handle.
*/
-
-SCNHANDLE getPolyFilm(HPOLYGON hp) {
- const POLY *pp; // Pointer to compiled polygon data
-
+SCNHANDLE GetPolyFilm(HPOLYGON hp) {
CHECK_HP(hp, "Out of range polygon handle (17)");
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
- return FROM_LE_32(pp->hFilm);
-}
-
-/**
- * Get polygon's associated node.
- */
-
-void getPolyNode(HPOLYGON hp, int *px, int *py) {
- const POLY *pp; // Pointer to compiled polygon data
-
- CHECK_HP(hp, "Out of range polygon handle (18)");
-
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
-
- *px = (int)FROM_LE_32(pp->nodex);
- *py = (int)FROM_LE_32(pp->nodey);
+ return FROM_LE_32(pp.hFilm);
}
/**
* Get handle to polygon's glitter code.
*/
-
-SCNHANDLE getPolyScript(HPOLYGON hp) {
- const POLY *pp; // Pointer to compiled polygon data
-
+SCNHANDLE GetPolyScript(HPOLYGON hp) {
CHECK_HP(hp, "Out of range polygon handle (19)");
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
- return FROM_LE_32(pp->hScript);
+ return FROM_LE_32(pp.hScript);
}
-REEL getPolyReelType(HPOLYGON hp) {
- const POLY *pp; // Pointer to compiled polygon data
-
+REEL GetPolyReelType(HPOLYGON hp) {
// To try and fix some unknown potential bug (toyshop entrance)
if (hp == NOPOLY)
return REEL_ALL;
CHECK_HP(hp, "Out of range polygon handle (20)");
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
- return (REEL)FROM_LE_32(pp->reel);
+ return (REEL)FROM_LE_32(pp.reel);
}
-int32 getPolyZfactor(HPOLYGON hp) {
- const POLY *pp; // Pointer to compiled polygon data
-
+int32 GetPolyZfactor(HPOLYGON hp) {
CHECK_HP(hp, "Out of range polygon handle (21)");
assert(Polys[hp] != NULL);
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
- return (int)FROM_LE_32(pp->zFactor);
+ return (int)FROM_LE_32(pp.zFactor);
}
int numNodes(HPOLYGON hp) {
- const POLY *pp; // Pointer to compiled polygon data
-
CHECK_HP(hp, "Out of range polygon handle (22)");
assert(Polys[hp] != NULL);
- pp = (const POLY *)LockMem(pHandle) + Polys[hp]->pIndex;
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
- return (int)FROM_LE_32(pp->nodecount);
+ return (int)FROM_LE_32(pp.nodecount);
}
// *************************************************************************
@@ -1116,126 +1230,45 @@ void syncPolyInfo(Serializer &s) {
*/
void SaveDeadPolys(bool *sdp) {
+ assert(!TinselV2);
memcpy(sdp, deadPolys, MAX_POLY*sizeof(bool));
}
void RestoreDeadPolys(bool *sdp) {
+ assert(!TinselV2);
memcpy(deadPolys, sdp, MAX_POLY*sizeof(bool));
}
-/**
- * Convert a BLOCKING to an EX_BLOCK poly.
- */
-void DisableBlock(int blockno) {
- for (int i = 0; i < MAX_POLY; i++) {
- if (Polys[i] && Polys[i]->polytype == BLOCKING && Polys[i]->polyID == blockno) {
- Polys[i]->polytype = EX_BLOCK;
- deadPolys[i] = true;
- }
- }
-}
-
-/**
- * Convert an EX_BLOCK to a BLOCKING poly.
- */
-void EnableBlock(int blockno) {
- for (int i = 0; i < MAX_POLY; i++) {
- if (Polys[i] && Polys[i]->polytype == EX_BLOCK && Polys[i]->polyID == blockno) {
- Polys[i]->polytype = BLOCKING;
- deadPolys[i] = false;
- }
- }
-}
-
-/**
- * Convert an EX_TAG to a TAG poly.
- */
-void EnableTag(int tagno) {
- for (int i = 0; i < MAX_POLY; i++) {
- if (Polys[i] && Polys[i]->polytype == EX_TAG && Polys[i]->polyID == tagno) {
- Polys[i]->polytype = TAG;
- }
- }
-
- TAGSTATE *pts;
- pts = &TagStates[SceneTags[currentTScene].offset];
- for (int j = 0; j < SceneTags[currentTScene].nooftags; j++, pts++) {
- if (pts->tid == tagno) {
- pts->enabled = true;
- break;
- }
- }
+void SavePolygonStuff(POLY_VOLATILE *sps) {
+ assert(TinselV2);
+ memcpy(sps, volatileStuff, MAX_POLY*sizeof(POLY_VOLATILE));
}
-/**
- * Convert an EX_EXIT to a EXIT poly.
- */
-void EnableExit(int exitno) {
- for (int i = 0; i < MAX_POLY; i++) {
- if (Polys[i] && Polys[i]->polytype == EX_EXIT && Polys[i]->polyID == exitno) {
- Polys[i]->polytype = EXIT;
- }
- }
-
- TAGSTATE *pts;
- pts = &ExitStates[SceneExits[currentEScene].offset];
- for (int j = 0; j < SceneExits[currentEScene].nooftags; j++, pts++) {
- if (pts->tid == exitno) {
- pts->enabled = true;
- break;
- }
- }
+void RestorePolygonStuff(POLY_VOLATILE *sps) {
+ assert(TinselV2);
+ memcpy(volatileStuff, sps, MAX_POLY*sizeof(POLY_VOLATILE));
}
-/**
- * Convert a TAG to an EX_TAG poly.
- */
-void DisableTag(int tagno) {
- TAGSTATE *pts;
-
- for (int i = 0; i < MAX_POLY; i++) {
- if (Polys[i] && Polys[i]->polytype == TAG && Polys[i]->polyID == tagno) {
- Polys[i]->polytype = EX_TAG;
- Polys[i]->tagState = TAG_OFF;
- Polys[i]->pointState = NOT_POINTING;
- }
- }
-
- pts = &TagStates[SceneTags[currentTScene].offset];
- for (int j = 0; j < SceneTags[currentTScene].nooftags; j++, pts++) {
- if (pts->tid == tagno) {
- pts->enabled = false;
- break;
- }
- }
-}
/**
- * Convert a EXIT to an EX_EXIT poly.
+ * Scan for a given polygon
*/
-void DisableExit(int exitno) {
- TAGSTATE *pts;
+static HPOLYGON FindPolygon(PTYPE type, int id) {
- for (int i = 0; i < MAX_POLY; i++) {
- if (Polys[i] && Polys[i]->polytype == EXIT && Polys[i]->polyID == exitno) {
- Polys[i]->polytype = EX_EXIT;
- Polys[i]->tagState = TAG_OFF;
- Polys[i]->pointState = NOT_POINTING;
+ for (int i = 0; i <= MAX_POLY; i++) {
+ if (Polys[i] && Polys[i]->polyType == type && Polys[i]->polyID == id) {
+ // Found it
+ return i;
}
}
- pts = &ExitStates[SceneExits[currentEScene].offset];
- for (int j = 0; j < SceneExits[currentEScene].nooftags; j++, pts++) {
- if (pts->tid == exitno) {
- pts->enabled = false;
- break;
- }
- }
+ // Not found
+ return NOPOLY;
}
HPOLYGON FirstPathPoly(void) {
for (int i = 0; i < noofPolys; i++) {
- if (Polys[i]->polytype == PATH)
+ if (Polys[i]->polyType == PATH)
return i;
}
error("FirstPathPoly() - no PATH polygons!");
@@ -1286,21 +1319,51 @@ static int DistinctCorners(HPOLYGON hp1, HPOLYGON hp2) {
return retval;
}
+/**
+ * Returns true if the two paths are on the same level
+ */
+static bool MatchingLevels(PPOLYGON p1, PPOLYGON p2) {
+ byte *pps; // Compiled polygon data
+
+ pps = LockMem(pHandle); // All polygons
+ Poly pp1(pps, p1->pIndex); // This polygon 1
+ Poly pp2(pps, p2->pIndex); // This polygon 2
+
+ assert(pp1.level1 <= pp1.level2);
+ assert(pp2.level1 <= pp2.level2);
+
+ for (int pl = pp1.level1; pl <= pp1.level2; pl++) {
+ if (pl >= pp2.level1 && pl <= pp2.level2)
+ return true;
+ }
+
+ return false;
+}
+
static void SetPathAdjacencies() {
POLYGON *p1, *p2; // Polygon pointers
+ int i1, i2;
+
+ // Reset them all
+ for (i1 = 0; i1 < noofPolys; i1++)
+ memset(Polys[i1]->adjpaths, 0, MAXADJ * sizeof(PPOLYGON));
// For each polygon..
- for (int i1 = 0; i1 < MAX_POLY-1; i1++) {
+ for (i1 = 0; i1 < MAX_POLY-1; i1++) {
// Get polygon, but only carry on if it's a path
p1 = Polys[i1];
- if (!p1 || p1->polytype != PATH)
+ if (!p1 || p1->polyType != PATH)
continue;
// For each subsequent polygon..
- for (int i2 = i1 + 1; i2 < MAX_POLY; i2++) {
+ for (i2 = i1 + 1; i2 < MAX_POLY; i2++) {
// Get polygon, but only carry on if it's a path
p2 = Polys[i2];
- if (!p2 || p2->polytype != PATH)
+ if (!p2 || p2->polyType != PATH)
+ continue;
+
+ // Must be on the same level
+ if (TinselV2 && !MatchingLevels(p1, p2))
continue;
int j = DistinctCorners(i1, i2);
@@ -1351,21 +1414,21 @@ void CheckNPathIntegrity() {
for (i = 0; i < MAX_POLY; i++) { // For each polygon..
rp = Polys[i];
- if (rp && rp->polytype == PATH && rp->subtype == NODE) { //...if it's a node path
+ if (rp && rp->polyType == PATH && rp->subtype == NODE) { //...if it's a node path
// Get compiled polygon structure
cp = (const POLY *)pps + rp->pIndex; // This polygon
- nlistx = (int32 *)(pps + (int)FROM_LE_32(cp->pnodelistx));
- nlisty = (int32 *)(pps + (int)FROM_LE_32(cp->pnodelisty));
+ nlistx = (int32 *)(pps + (int)FROM_LE_32(cp.pnodelistx));
+ nlisty = (int32 *)(pps + (int)FROM_LE_32(cp.pnodelisty));
- n = (int)FROM_LE_32(cp->nodecount) - 1; // Last node
+ n = (int)FROM_LE_32(cp.nodecount) - 1; // Last node
assert(n >= 1); // Node paths must have at least 2 nodes
- hp = PolyIndex(rp);
+ hp = PolygonIndex(rp);
for (j = 0; j <= n; j++) {
if (!IsInPolygon((int)FROM_LE_32(nlistx[j]), (int)FROM_LE_32(nlisty[j]), hp)) {
- sprintf(tBufferAddr(), "Node (%d, %d) is not in its own path (starting (%d, %d))",
+ sprintf(TextBufferAddr(), "Node (%d, %d) is not in its own path (starting (%d, %d))",
(int)FROM_LE_32(nlistx[j]), (int)FROM_LE_32(nlisty[j]), rp->cx[0], rp->cy[0]);
- error(tBufferAddr());
+ error(TextBufferAddr());
}
}
@@ -1374,15 +1437,15 @@ void CheckNPathIntegrity() {
if (rp->adjpaths[j] == NULL)
break;
- if (IsInPolygon((int)FROM_LE_32(nlistx[0]), (int)FROM_LE_32(nlisty[0]), PolyIndex(rp->adjpaths[j]))) {
- sprintf(tBufferAddr(), "Node (%d, %d) is in another path (starting (%d, %d))",
+ if (IsInPolygon((int)FROM_LE_32(nlistx[0]), (int)FROM_LE_32(nlisty[0]), PolygonIndex(rp->adjpaths[j]))) {
+ sprintf(TextBufferAddr(), "Node (%d, %d) is in another path (starting (%d, %d))",
(int)FROM_LE_32(nlistx[0]), (int)FROM_LE_32(nlisty[0]), rp->adjpaths[j]->cx[0], rp->adjpaths[j]->cy[0]);
- error(tBufferAddr())
+ error(TextBufferAddr())
}
- if (IsInPolygon((int)FROM_LE_32(nlistx[n]), (int)FROM_LE_32(nlisty[n]), PolyIndex(rp->adjpaths[j]))) {
- sprintf(tBufferAddr(), "Node (%d, %d) is in another path (starting (%d, %d))",
+ if (IsInPolygon((int)FROM_LE_32(nlistx[n]), (int)FROM_LE_32(nlisty[n]), PolygonIndex(rp->adjpaths[j]))) {
+ sprintf(TextBufferAddr(), "Node (%d, %d) is in another path (starting (%d, %d))",
(int)FROM_LE_32(nlistx[n]), (int)FROM_LE_32(nlisty[n]), rp->adjpaths[j]->cx[0], rp->adjpaths[j]->cy[0]);
- error(tBufferAddr())
+ error(TextBufferAddr())
}
}
}
@@ -1396,8 +1459,8 @@ void CheckNPathIntegrity() {
static void SetExBlocks() {
for (int i = 0; i < MAX_POLY; i++) {
if (deadPolys[i]) {
- if (Polys[i] && Polys[i]->polytype == BLOCKING)
- Polys[i]->polytype = EX_BLOCK;
+ if (Polys[i] && Polys[i]->polyType == BLOCK)
+ Polys[i]->polyType = EX_BLOCK;
#ifdef DEBUG
else
error("Impossible message!");
@@ -1410,8 +1473,8 @@ static void SetExBlocks() {
* Called at the start of a scene, nobbles TAG polygons which should be dead.
*/
static void SetExTags(SCNHANDLE ph) {
- TAGSTATE *pts;
int i, j;
+ TAGSTATE *pts;
for (i = 0; i < numScenesT; i++) {
if (SceneTags[i].sid == ph) {
@@ -1420,7 +1483,7 @@ static void SetExTags(SCNHANDLE ph) {
pts = &TagStates[SceneTags[i].offset];
for (j = 0; j < SceneTags[i].nooftags; j++, pts++) {
if (!pts->enabled)
- DisableTag(pts->tid);
+ DisableTag(nullContext, pts->tid);
}
return;
}
@@ -1435,7 +1498,7 @@ static void SetExTags(SCNHANDLE ph) {
SceneTags[i].nooftags = 0;
for (j = 0; j < MAX_POLY; j++) {
- if (Polys[j] && Polys[j]->polytype == TAG) {
+ if (Polys[j] && Polys[j]->polyType == TAG) {
TagStates[nextfreeT].tid = Polys[j]->polyID;
TagStates[nextfreeT].enabled = true;
nextfreeT++;
@@ -1474,7 +1537,7 @@ static void SetExExits(SCNHANDLE ph) {
SceneExits[i].nooftags = 0;
for (j = 0; j < MAX_POLY; j++) {
- if (Polys[j] && Polys[j]->polytype == EXIT) {
+ if (Polys[j] && Polys[j]->polyType == EXIT) {
ExitStates[nextfreeE].tid = Polys[j]->polyID;
ExitStates[nextfreeE].enabled = true;
nextfreeE++;
@@ -1522,6 +1585,57 @@ static void FiddlyBit(POLYGON *p) {
}
/**
+ * Allocate a POLYGON structure and reset it to default values
+ */
+static PPOLYGON GetPolyEntry() {
+ int i; // Loop counter
+ PPOLYGON p;
+
+ for (i = 0; i < MaxPolys; i++) {
+ if (!Polys[i]) {
+ p = Polys[i] = &Polygons[i];
+
+ // What the hell, just clear it all out - it's safer
+ memset(p, 0, sizeof(POLYGON));
+
+ return p;
+ }
+ }
+
+ error("Exceeded MaxPolys");
+}
+
+/**
+ * Variation of GetPolyEntry from Tinsel 1 that splits up getting a new
+ * polygon structure from initialising it
+ */
+static PPOLYGON CommonInits(PTYPE polyType, int pno, const Poly &pp, bool bRestart) {
+ int i;
+ HPOLYGON hp;
+ PPOLYGON p = GetPolyEntry(); // Obtain a slot
+
+ p->polyType = polyType; // Polygon type
+ p->pIndex = pno;
+
+ for (i = 0; i < 4; i++) { // Polygon definition
+ p->cx[i] = (short)pp.x[i];
+ p->cy[i] = (short)pp.y[i];
+ }
+
+ if (!bRestart) {
+ hp = PolygonIndex(p);
+ volatileStuff[hp].xoff = (short)pp.xoff;
+ volatileStuff[hp].yoff = (short)pp.yoff;
+ }
+
+ p->polyID = pp.id; // Identifier
+
+ FiddlyBit(p);
+
+ return p;
+}
+
+/**
* Calculate a point approximating to the centre of a polygon.
* Not very sophisticated.
*/
@@ -1529,17 +1643,17 @@ static void PseudoCentre(POLYGON *p) {
p->pcentrex = (p->cx[0] + p->cx[1] + p->cx[2] + p->cx[3])/4;
p->pcentrey = (p->cy[0] + p->cy[1] + p->cy[2] + p->cy[3])/4;
- if (!IsInPolygon(p->pcentrex, p->pcentrey, PolyIndex(p))) {
+ if (!IsInPolygon(p->pcentrex, p->pcentrey, PolygonIndex(p))) {
int i, top = 0, bot = 0;
for (i = p->ptop; i <= p->pbottom; i++) {
- if (IsInPolygon(p->pcentrex, i, PolyIndex(p))) {
+ if (IsInPolygon(p->pcentrex, i, PolygonIndex(p))) {
top = i;
break;
}
}
for (i = p->pbottom; i >= p->ptop; i--) {
- if (IsInPolygon(p->pcentrex, i, PolyIndex(p))) {
+ if (IsInPolygon(p->pcentrex, i, PolygonIndex(p))) {
bot = i;
break;
}
@@ -1547,68 +1661,30 @@ static void PseudoCentre(POLYGON *p) {
p->pcentrex = (top+bot)/2;
}
#ifdef DEBUG
- // assert(IsInPolygon(p->pcentrex, p->pcentrey, PolyIndex(p))); // Pseudo-centre is not in path
- if (!IsInPolygon(p->pcentrex, p->pcentrey, PolyIndex(p))) {
- sprintf(tBufferAddr(), "Pseudo-centre is not in path (starting (%d, %d)) - polygon reversed?",
+ // assert(IsInPolygon(p->pcentrex, p->pcentrey, PolygonIndex(p))); // Pseudo-centre is not in path
+ if (!IsInPolygon(p->pcentrex, p->pcentrey, PolygonIndex(p))) {
+ sprintf(TextBufferAddr(), "Pseudo-centre is not in path (starting (%d, %d)) - polygon reversed?",
p->cx[0], p->cy[0]);
- error(tBufferAddr());
+ error(TextBufferAddr());
}
#endif
}
/**
- * Allocate a POLYGON structure.
- */
-static POLYGON *GetPolyEntry(PTYPE type, const POLY *pp, int pno) {
- for (int i = 0; i < MaxPolys; i++) {
- if (!Polys[i]) {
- POLYGON *p = Polys[i] = &Polygons[i];
- memset(p, 0, sizeof(POLYGON));
-
- p->polytype = type; // Polygon type
- p->pIndex = pno;
- p->tagState = TAG_OFF;
- p->pointState = NOT_POINTING;
- p->polyID = FROM_LE_32(pp->id); // Identifier
-
- for (int j = 0; j < 4; j++) { // Polygon definition
- p->cx[j] = (short)FROM_LE_32(pp->x[j]);
- p->cy[j] = (short)FROM_LE_32(pp->y[j]);
- }
-
- return p;
- }
- }
-
- error("Exceeded MaxPolys");
- return NULL;
-}
-
-/**
* Initialise an EXIT polygon.
*/
-static void InitExit(const POLY *pp, int pno) {
- FiddlyBit(GetPolyEntry(EXIT, pp, pno));
+static void InitExit(const Poly &pp, int pno, bool bRestart) {
+ CommonInits(EXIT, pno, pp, bRestart);
}
/**
* Initialise a PATH or NPATH polygon.
*/
-static void InitPath(const POLY *pp, bool NodePath, int pno) {
- POLYGON *p;
-
- p = GetPolyEntry(PATH, pp, pno); // Obtain a slot
-
- if (NodePath) {
- p->subtype = NODE;
- } else {
- p->subtype = NORMAL;
- }
+static void InitPath(const Poly &pp, bool NodePath, int pno, bool bRestart) {
+ PPOLYGON p = CommonInits(PATH, pno, pp, bRestart);
- // Clear out ajacent path pointers
- memset(p->adjpaths, 0, MAXADJ*sizeof(POLYGON *));
+ p->subtype = NodePath ? NODE : NORMAL;
- FiddlyBit(p);
PseudoCentre(p);
}
@@ -1616,8 +1692,8 @@ static void InitPath(const POLY *pp, bool NodePath, int pno) {
/**
* Initialise a BLOCKING polygon.
*/
-static void InitBlock(const POLY *pp, int pno) {
- FiddlyBit(GetPolyEntry(BLOCKING, pp, pno));
+static void InitBlock(const Poly &pp, int pno, bool bRestart) {
+ CommonInits(BLOCK, pno, pp, bRestart);
}
/**
@@ -1626,16 +1702,16 @@ static void InitBlock(const POLY *pp, int pno) {
* trying to walk through the actor you first thought of.
* This is for dynamic blocking.
*/
-HPOLYGON InitExtraBlock(PMACTOR ca, PMACTOR ta) {
+HPOLYGON InitExtraBlock(PMOVER ca, PMOVER ta) {
int caX, caY; // Calling actor co-ords
int taX, taY; // Test actor co-ords
int left, right;
- GetMActorPosition(ca, &caX, &caY); // Calling actor co-ords
- GetMActorPosition(ta, &taX, &taY); // Test actor co-ords
+ GetMoverPosition(ca, &caX, &caY); // Calling actor co-ords
+ GetMoverPosition(ta, &taX, &taY); // Test actor co-ords
- left = GetMActorLeft(ta) - (GetMActorRight(ca) - caX);
- right = GetMActorRight(ta) + (caX - GetMActorLeft(ca));
+ left = GetMoverLeft(ta) - (GetMoverRight(ca) - caX);
+ right = GetMoverRight(ta) + (caX - GetMoverLeft(ca));
memset(&extraBlock, 0, sizeof(extraBlock));
@@ -1658,37 +1734,70 @@ HPOLYGON InitExtraBlock(PMACTOR ca, PMACTOR ta) {
/**
* Initialise an EFFECT polygon.
*/
-static void InitEffect(const POLY *pp, int pno) {
- FiddlyBit(GetPolyEntry(EFFECT, pp, pno));
+static void InitEffect(const Poly &pp, int pno, bool bRestart) {
+ CommonInits(EFFECT, pno, pp, bRestart);
}
/**
* Initialise a REFER polygon.
*/
-static void InitRefer(const POLY *pp, int pno) {
- POLYGON *p = GetPolyEntry(REFER, pp, pno); // Obtain a slot
-
- p->subtype = FROM_LE_32(pp->reftype); // Refer type
+static void InitRefer(const Poly &pp, int pno, bool bRestart) {
+ PPOLYGON p = CommonInits(REFER, pno, pp, bRestart);
- FiddlyBit(p);
+ p->subtype = FROM_LE_32(pp.reftype); // Refer type
}
/**
* Initialise a TAG polygon.
*/
-static void InitTag(const POLY *pp, int pno) {
- FiddlyBit(GetPolyEntry(TAG, pp, pno));
+static void InitTag(const Poly &pp, int pno, bool bRestart) {
+ CommonInits(TAG, pno, pp, bRestart);
}
+/**
+ * Called at the restart of a scene, nobbles polygons which are dead.
+ */
+static void KillDeadPolygons(void) {
+ int i;
+
+ for (i = 0; i < MAX_POLY; i++) {
+ if (volatileStuff[i].bDead) {
+ assert(Polys[i]);
+
+ switch (Polys[i]->polyType) {
+ case BLOCK:
+ Polys[i]->polyType = EX_BLOCK;
+ break;
+
+ case EFFECT:
+ Polys[i]->polyType = EX_EFFECT;
+ break;
+
+ case REFER:
+ Polys[i]->polyType = EX_REFER;
+ break;
+
+ case PATH:
+ Polys[i]->polyType = EX_PATH;
+ break;
+
+ case TAG:
+ Polys[i]->polyType = EX_TAG;
+ break;
+
+ default:
+ error("Impossible message!");
+ }
+ }
+ }
+}
/**
* Called at the start of a scene to initialise the polys in that scene.
*/
void InitPolygons(SCNHANDLE ph, int numPoly, bool bRestart) {
- const POLY *pp; // Pointer to compiled data polygon structure
-
pHandle = ph;
noofPolys = numPoly;
@@ -1702,62 +1811,90 @@ void InitPolygons(SCNHANDLE ph, int numPoly, bool bRestart) {
}
}
+ if (numPoly == 0)
+ return;
+
for (int i = 0; i < noofPolys; i++) {
if (Polys[i]) {
- Polys[i]->pointState = NOT_POINTING;
+ Polys[i]->pointState = PS_NOT_POINTING;
Polys[i] = NULL;
}
}
memset(RoutePaths, 0, sizeof(RoutePaths));
- if (!bRestart)
- memset(deadPolys, 0, sizeof(deadPolys));
+ if (!bRestart) {
+ if (TinselV2)
+ memset(volatileStuff, 0, sizeof(volatileStuff));
+ else
+ memset(deadPolys, 0, sizeof(deadPolys));
+ }
- pp = (const POLY *)LockMem(ph);
- for (int i = 0; i < numPoly; i++, pp++) {
- switch (FROM_LE_32(pp->type)) {
- case POLY_PATH:
- InitPath(pp, false, i);
- break;
+ if (numPoly > 0) {
+ Poly pp(LockMem(ph));
- case POLY_NPATH:
- InitPath(pp, true, i);
- break;
+ for (int i = 0; i < numPoly; ++i, ++pp) {
+ switch (FROM_LE_32(pp.type)) {
+ case POLY_PATH:
+ InitPath(pp, false, i, bRestart);
+ break;
- case POLY_BLOCK:
- InitBlock(pp, i);
- break;
+ case POLY_NPATH:
+ InitPath(pp, true, i, bRestart);
+ break;
- case POLY_REFER:
- InitRefer(pp, i);
- break;
+ case POLY_BLOCK:
+ InitBlock(pp, i, bRestart);
+ break;
- case POLY_EFFECT:
- InitEffect(pp, i);
- break;
+ case POLY_REFER:
+ InitRefer(pp, i, bRestart);
+ break;
- case POLY_EXIT:
- InitExit(pp, i);
- break;
+ case POLY_EFFECT:
+ InitEffect(pp, i, bRestart);
+ break;
- case POLY_TAG:
- InitTag(pp, i);
- break;
+ case POLY_EXIT:
+ InitExit(pp, i, bRestart);
+ break;
- default:
- error("Unknown polygon type");
+ case POLY_TAG:
+ InitTag(pp, i, bRestart);
+ break;
+
+ default:
+ error("Unknown polygon type");
+ }
}
}
- SetPathAdjacencies(); // Paths need to know the facts
+
+ if (!TinselV2) {
+ SetPathAdjacencies(); // Paths need to know the facts
#ifdef DEBUG
- CheckNPathIntegrity();
+ CheckNPathIntegrity();
#endif
- SetExTags(ph); // Some tags may have been killed
- SetExExits(ph); // Some exits may have been killed
- if (bRestart)
- SetExBlocks(); // Some blocks may have been killed
+ SetExTags(ph); // Some tags may have been killed
+ SetExExits(ph); // Some exits may have been killed
+
+ if (bRestart)
+ SetExBlocks(); // Some blocks may have been killed
+ } else {
+ if (bRestart) {
+ // Some may have been killed if this is a restore
+ KillDeadPolygons();
+ } else {
+ for (int i = numPoly - 1; i >= 0; i--) {
+ if (Polys[i]->polyType == TAG) {
+ PolygonEvent(nullContext, i, STARTUP, 0, false, 0);
+ }
+ }
+ }
+
+ // Paths need to know the facts
+ SetPathAdjacencies();
+ }
}
/**
@@ -1770,7 +1907,7 @@ void DropPolygons() {
for (int i = 0; i < noofPolys; i++) {
if (Polys[i]) {
- Polys[i]->pointState = NOT_POINTING;
+ Polys[i]->pointState = PS_NOT_POINTING;
Polys[i] = NULL;
}
}
@@ -1784,7 +1921,7 @@ void DropPolygons() {
PTYPE PolyType(HPOLYGON hp) {
CHECK_HP(hp, "Out of range polygon handle (25)");
- return Polys[hp]->polytype;
+ return Polys[hp]->polyType;
}
int PolySubtype(HPOLYGON hp) {
@@ -1829,12 +1966,6 @@ TSTATE PolyTagState(HPOLYGON hp) {
return Polys[hp]->tagState;
}
-SCNHANDLE PolyTagHandle(HPOLYGON hp) {
- CHECK_HP(hp, "Out of range polygon handle (33)");
-
- return Polys[hp]->oTagHandle;
-}
-
void SetPolyPointState(HPOLYGON hp, PSTATE ps) {
CHECK_HP(hp, "Out of range polygon handle (34)");
@@ -1850,7 +1981,7 @@ void SetPolyTagState(HPOLYGON hp, TSTATE ts) {
void SetPolyTagHandle(HPOLYGON hp, SCNHANDLE th) {
CHECK_HP(hp, "Out of range polygon handle (36)");
- Polys[hp]->oTagHandle = th;
+ Polys[hp]->hOverrideTag = th;
}
void MaxPolygons(int numPolys) {
@@ -1859,4 +1990,387 @@ void MaxPolygons(int numPolys) {
MaxPolys = numPolys;
}
+/**
+ * Get polygon's associated node.
+ * The one for WalkTag(), StandTag() etc.
+ */
+void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY) {
+ CHECK_HP(hp, "GetPolyNode(): Out of range polygon handle");
+
+ Poly pp(LockMem(pHandle), Polys[hp]->pIndex);
+
+ *pNodeX = FROM_LE_32(pp.nodex);
+ *pNodeY = FROM_LE_32(pp.nodey);
+
+ if (TinselV2) {
+ *pNodeX += volatileStuff[hp].xoff;
+ *pNodeY += volatileStuff[hp].yoff;
+ }
+}
+
+void SetPolyPointedTo(HPOLYGON hp, bool bPointedTo) {
+ CHECK_HP(hp, "Out of range polygon handle (34)");
+
+ if (bPointedTo)
+ Polys[hp]->tagFlags |= POINTING;
+ else
+ Polys[hp]->tagFlags &= ~POINTING;
+}
+
+bool PolyIsPointedTo(HPOLYGON hp) {
+ CHECK_HP(hp, "Out of range polygon handle (31)");
+
+ if (TinselV2)
+ return (Polys[hp]->tagFlags & POINTING);
+
+ return PolyPointState(hp) == PS_POINTING;
+}
+
+void SetPolyTagWanted(HPOLYGON hp, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag) {
+ CHECK_HP(hp, "Out of range polygon handle (35)");
+
+ if (bTagWanted) {
+ Polys[hp]->tagFlags |= TAGWANTED;
+ Polys[hp]->hOverrideTag = hOverrideTag;
+ } else {
+ Polys[hp]->tagFlags &= ~TAGWANTED;
+ Polys[hp]->hOverrideTag = 0;
+ }
+
+ if (bCursor)
+ Polys[hp]->tagFlags |= FOLLOWCURSOR;
+ else
+ Polys[hp]->tagFlags &= ~FOLLOWCURSOR;
+}
+
+bool PolyTagIsWanted(HPOLYGON hp) {
+ CHECK_HP(hp, "Out of range polygon handle (32)");
+
+ return (Polys[hp]->tagFlags & TAGWANTED);
+}
+
+bool PolyTagFollowsCursor(HPOLYGON hp) {
+ CHECK_HP(hp, "Out of range polygon handle (36)");
+
+ return (Polys[hp]->tagFlags & FOLLOWCURSOR);
+}
+
+SCNHANDLE GetPolyTagHandle(HPOLYGON hp) {
+ CHECK_HP(hp, "Out of range polygon handle (33)");
+
+ return Polys[hp]->hOverrideTag;
+}
+
+bool IsTagPolygon(int tagno) {
+ return (FindPolygon(TAG, tagno) != NOPOLY || FindPolygon(EX_TAG, tagno) != NOPOLY);
+}
+
+int GetTagPolyId(HPOLYGON hp) {
+ CHECK_HP(hp, "Out of range polygon handle (GetTagPolyId()");
+
+ assert(Polys[hp]->polyType == TAG || Polys[hp]->polyType == EX_TAG);
+
+ return Polys[hp]->polyID;
+}
+
+void GetPolyMidBottom( HPOLYGON hp, int *pX, int *pY) {
+ CHECK_HP(hp, "Out of range polygon handle (GetPolyMidBottom()");
+
+ *pY = Polys[hp]->pbottom + volatileStuff[hp].yoff;
+ *pX = (Polys[hp]->pleft + Polys[hp]->pright)/2 + volatileStuff[hp].xoff;
+}
+
+int PathCount(void) {
+ int i, count;
+
+ for (i = 0, count = 0; i < noofPolys; i++) {
+ if (Polys[i]->polyType == PATH)
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * Convert a BLOCK to an EX_BLOCK poly
+ */
+void DisableBlock(int block) {
+ int i = FindPolygon(BLOCK, block);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = EX_BLOCK;
+ volatileStuff[i].bDead = true;
+ }
+}
+
+/**
+ * Convert an EX_BLOCK to a BLOCK poly
+ */
+void EnableBlock(int block) {
+ int i = FindPolygon(EX_BLOCK, block);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = BLOCK;
+ volatileStuff[i].bDead = false;
+ }
+}
+
+/**
+ * Convert an EFFECT to an EX_EFFECT poly
+ */
+void DisableEffect(int effect) {
+ int i = FindPolygon(EFFECT, effect);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = EX_EFFECT;
+ volatileStuff[i].bDead = true;
+ }
+}
+
+/**
+ * Convert an EX_EFFECT to an EFFECT poly
+ */
+void EnableEffect(int effect) {
+ int i = FindPolygon(EX_EFFECT, effect);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = EFFECT;
+ volatileStuff[i].bDead = false;
+ }
+}
+
+/**
+ * Convert a PATH to an EX_PATH poly
+ */
+void DisablePath(int path) {
+ int i = FindPolygon(PATH, path);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = EX_PATH;
+ volatileStuff[i].bDead = true;
+
+ // Paths need to know the new facts
+ SetPathAdjacencies();
+ }
+}
+
+/**
+ * Convert a PATH to an EX_PATH poly
+ */
+void EnablePath(int path) {
+ int i = FindPolygon(EX_PATH, path);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = PATH;
+ volatileStuff[i].bDead = false;
+
+ // Paths need to know the new facts
+ SetPathAdjacencies();
+ }
+}
+
+/**
+ * Convert a REFER to an EX_REFER poly
+ */
+void DisableRefer(int refer) {
+ int i = FindPolygon(REFER, refer);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = EX_REFER;
+ volatileStuff[i].bDead = true;
+ }
+}
+
+/**
+ * Convert a REFER to an EX_REFER poly
+ */
+void EnableRefer(int refer) {
+ int i = FindPolygon(EX_REFER, refer);
+
+ if (i != NOPOLY) {
+ Polys[i]->polyType = REFER;
+ volatileStuff[i].bDead = false;
+ }
+}
+
+/**
+ * Convert an EX_TAG to a TAG poly.
+ */
+void EnableTag(CORO_PARAM, int tag) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if ((_ctx->i = FindPolygon(EX_TAG, tag)) != NOPOLY) {
+ Polys[_ctx->i]->polyType = TAG;
+ volatileStuff[_ctx->i].bDead = false;
+
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, SHOWEVENT, 0, true, 0));
+ } else if ((_ctx->i = FindPolygon(TAG, tag)) != NOPOLY) {
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, SHOWEVENT, 0, true, 0));
+ }
+
+ if (!TinselV2) {
+ TAGSTATE *pts = &TagStates[SceneTags[currentTScene].offset];
+ for (int j = 0; j < SceneTags[currentTScene].nooftags; j++, pts++) {
+ if (pts->tid == tag) {
+ pts->enabled = true;
+ break;
+ }
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Convert an EX_EXIT to a EXIT poly.
+ */
+void EnableExit(int exitno) {
+ for (int i = 0; i < MAX_POLY; i++) {
+ if (Polys[i] && Polys[i]->polyType == EX_EXIT && Polys[i]->polyID == exitno) {
+ Polys[i]->polyType = EXIT;
+ }
+ }
+
+ TAGSTATE *pts;
+ pts = &ExitStates[SceneExits[currentEScene].offset];
+ for (int j = 0; j < SceneExits[currentEScene].nooftags; j++, pts++) {
+ if (pts->tid == exitno) {
+ pts->enabled = true;
+ break;
+ }
+ }
+}
+
+/**
+ * Move a polygon relative to current offset.
+ */
+void MovePolygon(PTYPE ptype, int id, int x, int y) {
+ int i = FindPolygon(ptype, id);
+
+ // If not found, try its dead equivalent
+ if (i == NOPOLY) {
+ switch (ptype) {
+ case TAG:
+ ptype = EX_TAG;
+ break;
+ default:
+ break;
+ }
+
+ i = FindPolygon(ptype, id);
+ }
+
+ if (i != NOPOLY) {
+ volatileStuff[i].xoff += (short)x;
+ volatileStuff[i].yoff += (short)y;
+ }
+}
+
+/**
+ * Move a polygon relative to absolute offset.
+ */
+void MovePolygonTo(PTYPE ptype, int id, int x, int y) {
+ int i = FindPolygon(ptype, id);
+
+ // If not found, try its dead equivalent
+ if (i == NOPOLY) {
+ switch (ptype) {
+ case TAG:
+ ptype = EX_TAG;
+ break;
+ default:
+ break;
+ }
+
+ i = FindPolygon(ptype, id);
+ }
+
+ if (i != NOPOLY) {
+ volatileStuff[i].xoff = (short)x;
+ volatileStuff[i].yoff = (short)y;
+ }
+}
+
+
+/**
+ * Convert tag number to polygon handle.
+ */
+HPOLYGON GetTagHandle(int tagno) {
+ int i = FindPolygon(TAG, tagno);
+
+ if (i == NOPOLY)
+ i = FindPolygon(EX_TAG, tagno);
+
+ assert(i != NOPOLY);
+
+ return GetPolyHandle(i);
+}
+
+/**
+ * Convert a TAG to an EX_TAG poly.
+ */
+void DisableTag(CORO_PARAM, int tag) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if ((_ctx->i = FindPolygon(TAG, tag)) != NOPOLY) {
+ Polys[_ctx->i]->polyType = EX_TAG;
+ Polys[_ctx->i]->tagFlags = 0;
+ Polys[_ctx->i]->tagState = TAG_OFF;
+ Polys[_ctx->i]->pointState = PS_NOT_POINTING;
+
+ volatileStuff[_ctx->i].bDead = true;
+
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, HIDEEVENT, 0, true, 0));
+ } else if ((_ctx->i = FindPolygon(EX_TAG, tag)) != NOPOLY) {
+ if (TinselV2)
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, HIDEEVENT, 0, true, 0));
+ }
+
+ if (!TinselV2) {
+ TAGSTATE *pts = &TagStates[SceneTags[currentTScene].offset];
+ for (int j = 0; j < SceneTags[currentTScene].nooftags; j++, pts++) {
+ if (pts->tid == tag) {
+ pts->enabled = false;
+ break;
+ }
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Convert a EXIT to an EX_EXIT poly.
+ */
+void DisableExit(int exitno) {
+ TAGSTATE *pts;
+
+ for (int i = 0; i < MAX_POLY; i++) {
+ if (Polys[i] && Polys[i]->polyType == EXIT && Polys[i]->polyID == exitno) {
+ Polys[i]->polyType = EX_EXIT;
+ Polys[i]->tagState = TAG_OFF;
+ Polys[i]->pointState = PS_NOT_POINTING;
+ }
+ }
+
+ pts = &ExitStates[SceneExits[currentEScene].offset];
+ for (int j = 0; j < SceneExits[currentEScene].nooftags; j++, pts++) {
+ if (pts->tid == exitno) {
+ pts->enabled = false;
+ break;
+ }
+ }
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/polygons.h b/engines/tinsel/polygons.h
index 90c57d5f99..62ec0422c6 100644
--- a/engines/tinsel/polygons.h
+++ b/engines/tinsel/polygons.h
@@ -35,8 +35,12 @@ namespace Tinsel {
// Polygon Types
enum PTYPE {
- TEST, PATH, EXIT, BLOCKING,
- EFFECT, REFER, TAG, EX_TAG, EX_EXIT, EX_BLOCK
+ // Tinsel 2 Polygon type list
+ TEST,
+ BLOCK, EFFECT, PATH, REFER, TAG,
+ EX_BLOCK, EX_EFFECT, EX_PATH, EX_REFER, EX_TAG,
+ // Extra polygon types from Tinsel v1
+ EXIT, EX_EXIT
};
// subtype
@@ -45,6 +49,13 @@ enum {
NODE = 1 // For paths
};
+// tagFlags
+enum {
+ POINTING = 0x01,
+ TAGWANTED = 0x02,
+ FOLLOWCURSOR = 0x04
+};
+
// tagState
enum TSTATE {
TAG_OFF, TAG_ON
@@ -52,7 +63,7 @@ enum TSTATE {
// pointState
enum PSTATE {
- NO_POINT, NOT_POINTING, POINTING
+ PS_NO_POINT, PS_NOT_POINTING, PS_POINTING
};
@@ -60,7 +71,10 @@ enum {
NOPOLY = -1
};
-
+struct POLY_VOLATILE {
+ bool bDead;
+ short xoff, yoff; // Polygon offset
+};
/*-------------------------------------------------------------------------*/
@@ -69,26 +83,36 @@ HPOLYGON InPolygon(int xt, int yt, PTYPE type);
void BlockingCorner(HPOLYGON poly, int *x, int *y, int tarx, int tary);
void FindBestPoint(HPOLYGON path, int *x, int *y, int *line);
bool IsAdjacentPath(HPOLYGON path1, HPOLYGON path2);
-HPOLYGON getPathOnTheWay(HPOLYGON from, HPOLYGON to);
+HPOLYGON GetPathOnTheWay(HPOLYGON from, HPOLYGON to);
int NearestEndNode(HPOLYGON path, int x, int y);
int NearEndNode(HPOLYGON spath, HPOLYGON dpath);
int NearestNodeWithin(HPOLYGON npath, int x, int y);
void NearestCorner(int *x, int *y, HPOLYGON spath, HPOLYGON dpath);
bool IsPolyCorner(HPOLYGON hPath, int x, int y);
int GetScale(HPOLYGON path, int y);
+int GetBrightness(HPOLYGON hPath, int y);
void getNpathNode(HPOLYGON npath, int node, int *px, int *py);
-void getPolyTagInfo(HPOLYGON p, SCNHANDLE *hTagText, int *tagx, int *tagy);
-SCNHANDLE getPolyFilm(HPOLYGON p);
-void getPolyNode(HPOLYGON p, int *px, int *py);
-SCNHANDLE getPolyScript(HPOLYGON p);
-REEL getPolyReelType(HPOLYGON p);
-int32 getPolyZfactor(HPOLYGON p);
+void GetTagTag(HPOLYGON p, SCNHANDLE *hTagText, int *tagx, int *tagy);
+SCNHANDLE GetPolyFilm(HPOLYGON p);
+void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY);
+SCNHANDLE GetPolyScript(HPOLYGON p);
+REEL GetPolyReelType(HPOLYGON p);
+int32 GetPolyZfactor(HPOLYGON p);
int numNodes(HPOLYGON pp);
void RebootDeadTags(void);
+void DisableBlock(int block);
+void EnableBlock(int block);
+void DisableEffect(int effect);
+void EnableEffect(int effect);
+void DisablePath(int path);
+void EnablePath(int path);
+void DisableRefer(int refer);
+void EnableRefer(int refer);
void DisableBlock(int blockno);
void EnableBlock(int blockno);
-void DisableTag(int tagno);
-void EnableTag(int tagno);
+HPOLYGON GetTagHandle(int tagno);
+void DisableTag(CORO_PARAM, int tag);
+void EnableTag(CORO_PARAM, int tag);
void DisableExit(int exitno);
void EnableExit(int exitno);
HPOLYGON FirstPathPoly(void);
@@ -99,6 +123,8 @@ void DropPolygons(void);
void SaveDeadPolys(bool *sdp);
void RestoreDeadPolys(bool *sdp);
+void SavePolygonStuff(POLY_VOLATILE *sps);
+void RestorePolygonStuff(POLY_VOLATILE *sps);
/*-------------------------------------------------------------------------*/
@@ -110,7 +136,6 @@ int PolyCornerX(HPOLYGON hp, int n); // ->cx[n]
int PolyCornerY(HPOLYGON hp, int n); // ->cy[n]
PSTATE PolyPointState(HPOLYGON hp); // ->pointState
TSTATE PolyTagState(HPOLYGON hp); // ->tagState
-SCNHANDLE PolyTagHandle(HPOLYGON hp); // ->oTagHandle
void SetPolyPointState(HPOLYGON hp, PSTATE ps); // ->pointState
void SetPolyTagState(HPOLYGON hp, TSTATE ts); // ->tagState
@@ -118,6 +143,21 @@ void SetPolyTagHandle(HPOLYGON hp, SCNHANDLE th);// ->oTagHandle
void MaxPolygons(int maxPolys);
+int GetTagPolyId(HPOLYGON hp);
+void GetTagTag(HPOLYGON hp, SCNHANDLE *hTagText, int *tagX, int *tagY);
+void SetPolyPointedTo(HPOLYGON hp, bool bPointedTo);
+bool PolyIsPointedTo(HPOLYGON hp);
+void SetPolyTagWanted(HPOLYGON hp, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
+bool PolyTagIsWanted(HPOLYGON hp);
+bool PolyTagFollowsCursor(HPOLYGON hp);
+SCNHANDLE GetPolyTagHandle(HPOLYGON hp);
+bool IsTagPolygon(int tagno);
+int GetTagPolyId(HPOLYGON hp);
+void GetPolyMidBottom( HPOLYGON hp, int *pX, int *pY);
+int PathCount(void);
+void MovePolygon(PTYPE ptype, int id, int x, int y);
+void MovePolygonTo(PTYPE ptype, int id, int x, int y);
+
/*-------------------------------------------------------------------------*/
} // end of namespace Tinsel
diff --git a/engines/tinsel/rince.cpp b/engines/tinsel/rince.cpp
index a9b24bcac9..6590aed749 100644
--- a/engines/tinsel/rince.cpp
+++ b/engines/tinsel/rince.cpp
@@ -31,16 +31,20 @@
#include "tinsel/dw.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
-#include "tinsel/inventory.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/rince.h"
#include "tinsel/sched.h"
+#include "tinsel/sysvar.h"
#include "tinsel/timers.h"
+#include "tinsel/tinsel.h"
#include "tinsel/token.h"
#include "common/util.h"
@@ -49,8 +53,65 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
-static MACTOR Movers[MAX_MOVERS];
+static MOVER Movers[MAX_MOVERS];
+//----------------- FUNCTIONS ----------------------------
+
+/**
+ * Called from ActorPalette(), normally once just after the beginning of time.
+ */
+void StoreMoverPalette(PMOVER pMover, int startColour, int length) {
+ pMover->startColour = startColour;
+ 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(BgPal(),
+ pMover->startColour,
+ 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(BgPal());
+
+ // Do it all immediately
+ DimPartPalette(BgPal(), pMover->startColour, pMover->paletteLength, brightness);
+
+ // The actor is probably hidden at this point,
+ pMover->brightness = brightness;
+}
/**
* RebootMovers
@@ -63,11 +124,11 @@ void RebootMovers(void) {
* Given an actor number, return pointer to its moving actor structure,
* if it is a moving actor.
*/
-PMACTOR GetMover(int ano) {
+PMOVER GetMover(int ano) {
int i;
// Slot 0 is reserved for lead actor
- if (ano == LeadId() || ano == LEAD_ACTOR)
+ if (ano == GetLeadId() || ano == LEAD_ACTOR)
return &Movers[0];
for (i = 1; i < MAX_MOVERS; i++)
@@ -80,13 +141,13 @@ PMACTOR GetMover(int ano) {
/**
* Register an actor as being a moving one.
*/
-PMACTOR SetMover(int ano) {
+PMOVER RegisterMover(int ano) {
int i;
// Slot 0 is reserved for lead actor
- if (ano == LeadId() || ano == LEAD_ACTOR) {
+ if (ano == GetLeadId() || ano == LEAD_ACTOR) {
Movers[0].actorToken = TOKEN_LEAD;
- Movers[0].actorID = LeadId();
+ Movers[0].actorID = GetLeadId();
return &Movers[0];
}
@@ -114,10 +175,10 @@ PMACTOR SetMover(int ano) {
*
* At the time of writing, used by the effect process.
*/
-PMACTOR GetLiveMover(int index) {
+PMOVER GetLiveMover(int index) {
assert(index >= 0 && index < MAX_MOVERS); // out of range
- if (Movers[index].MActorState == NORM_MACTOR)
+ if (Movers[index].bActive)
return &Movers[index];
else
return NULL;
@@ -126,33 +187,33 @@ PMACTOR GetLiveMover(int index) {
bool IsMAinEffectPoly(int index) {
assert(index >= 0 && index < MAX_MOVERS); // out of range
- return Movers[index].InEffect;
+ return Movers[index].bInEffect;
}
-void SetMAinEffectPoly(int index, bool tf) {
+void SetMoverInEffect(int index, bool tf) {
assert(index >= 0 && index < MAX_MOVERS); // out of range
- Movers[index].InEffect = tf;
+ Movers[index].bInEffect = tf;
}
/**
* Remove a moving actor from the current scene.
*/
-void KillMActor(PMACTOR pActor) {
- if (pActor->MActorState == NORM_MACTOR) {
- pActor->MActorState = NO_MACTOR;
- MultiDeleteObject(GetPlayfieldList(FIELD_WORLD), pActor->actorObj);
- pActor->actorObj = NULL;
- assert(g_scheduler->getCurrentProcess() != pActor->pProc);
- g_scheduler->killProcess(pActor->pProc);
+void KillMover(PMOVER pMover) {
+ if (pMover->bActive) {
+ pMover->bActive = false;
+ MultiDeleteObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
+ pMover->actorObj = NULL;
+ assert(g_scheduler->getCurrentProcess() != pMover->pProc);
+ g_scheduler->killProcess(pMover->pProc);
}
}
/**
* getMActorState
*/
-MAS getMActorState(PMACTOR pActor) {
- return pActor->MActorState;
+bool getMActorState(PMOVER pActor) {
+ return pActor->bActive;
}
/**
@@ -160,114 +221,237 @@ MAS getMActorState(PMACTOR pActor) {
* MultiHideObject() is deliberately not used, as StepAnimScript() calls
* cause the object to re-appear.
*/
-void hideMActor(PMACTOR pActor, int sf) {
- assert(pActor); // Hiding null moving actor
+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 (IsTaggedActor(pMover->actorID)) {
+ // It may be pointed to
+ SetActorPointedTo(pMover->actorID, false);
+ SetActorTagWanted(pMover->actorID, false, false, 0);
+ }
+ }
- pActor->aHidden = true;
- pActor->SlowFactor = sf;
+ if (pMover->actorObj)
+ MultiSetZPosition(pMover->actorObj, -1);
+}
- if (pActor->actorObj)
- MultiSetZPosition(pActor->actorObj, -1);
+/**
+ * MoverHidden
+ */
+bool MoverHidden(PMOVER pMover) {
+ if (pMover)
+ return pMover->bHidden;
+ else
+ return false;
}
/**
- * getMActorHideState
+ * To be or not to be? If it be, then it is.
*/
-bool getMActorHideState(PMACTOR pActor) {
- if (pActor)
- return pActor->aHidden;
+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;
}
/**
- * unhideMActor
+ * Return an actor's walk ticket.
*/
-void unhideMActor(PMACTOR pActor) {
- assert(pActor); // unHiding null moving actor
+int GetWalkNumber(PMOVER pMover) {
+ return pMover->walkNumber;
+}
- pActor->aHidden = false;
+/**
+ * GetMoverId
+ */
+int GetMoverId(PMOVER pMover) {
+ return pMover->actorID;
+}
- // Make visible on the screen
- if (pActor->actorObj) {
- // If no path, just use first path in the scene
- if (pActor->hCpath != NOPOLY)
- MAsetZPos(pActor, pActor->objy, getPolyZfactor(pActor->hCpath));
- else
- MAsetZPos(pActor, pActor->objy, getPolyZfactor(FirstPathPoly()));
+/**
+ * Sets the mover Z position
+ */
+void SetMoverZ(PMOVER pMover, int y, uint32 zFactor) {
+ if (!pMover->bHidden) {
+ 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 = NULL;
+ 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 DropMActors(void) {
- for (int i = 0; i < MAX_MOVERS; i++) {
- Movers[i].MActorState = NO_MACTOR;
- Movers[i].objx = 0;
- Movers[i].objy = 0;
- Movers[i].actorObj = NULL; // No moving actor objects
-
- Movers[i].hCpath = NOPOLY; // No moving actor path
- }
+void DropMovers(void) {
+ for (int i = 0; i < MAX_MOVERS; i++)
+ InitMover(&Movers[i]);
}
/**
* Reposition a moving actor.
*/
-void MoveMActor(PMACTOR pActor, int x, int y) {
+void PositionMover(PMOVER pMover, int x, int y) {
int z;
int node;
HPOLYGON hPath;
- assert(pActor); // Moving null moving actor
- assert(pActor->actorObj);
+ assert(pMover); // Moving null moving actor
+ assert(pMover->actorObj);
- pActor->objx = x;
- pActor->objy = y;
- MultiSetAniXY(pActor->actorObj, x, y);
+ pMover->objX = x;
+ pMover->objY = y;
+ MultiSetAniXY(pMover->actorObj, x, y);
hPath = InPolygon(x, y, PATH);
if (hPath != NOPOLY) {
- pActor->hCpath = hPath;
+ pMover->hCpath = hPath;
if (PolySubtype(hPath) == NODE) {
node = NearestNodeWithin(hPath, x, y);
- getNpathNode(hPath, node, &pActor->objx, &pActor->objy);
- pActor->hFnpath = hPath;
- pActor->line = node;
- pActor->npstatus = GOING_UP;
+ getNpathNode(hPath, node, &pMover->objX, &pMover->objY);
+ pMover->hFnpath = hPath;
+ pMover->line = node;
+ pMover->npstatus = GOING_UP;
} else {
- pActor->hFnpath = NOPOLY;
- pActor->npstatus = NOT_IN;
+ pMover->hFnpath = NOPOLY;
+ pMover->npstatus = NOT_IN;
}
- z = GetScale(hPath, pActor->objy);
- pActor->scale = z;
- SetMActorStanding(pActor);
+ z = GetScale(hPath, pMover->objY);
+ pMover->scale = z;
+ SetMoverStanding(pMover);
} else {
- pActor->bNoPath = true;
+ pMover->bNoPath = true;
- pActor->hFnpath = NOPOLY; // Ain't in one
- pActor->npstatus = NOT_IN;
+ pMover->hFnpath = NOPOLY; // Ain't in one
+ pMover->npstatus = NOT_IN;
// Ensure legal reel and scale
- if (pActor->dirn < 0 || pActor->dirn > 3)
- pActor->dirn = FORWARD;
- if (pActor->scale < 0 || pActor->scale > TOTAL_SCALES)
- pActor->scale = 1;
+ 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 GetMActorPosition(PMACTOR pActor, int *paniX, int *paniY) {
- assert(pActor); // Getting null moving actor's position
+void GetMoverPosition(PMOVER pMover, int *paniX, int *paniY) {
+ assert(pMover); // Getting null moving actor's position
- if (pActor->actorObj != NULL)
- GetAniPosition(pActor->actorObj, paniX, paniY);
+ if (pMover->actorObj != NULL)
+ GetAniPosition(pMover->actorObj, paniX, paniY);
else {
*paniX = 0;
*paniY = 0;
@@ -277,43 +461,63 @@ void GetMActorPosition(PMACTOR pActor, int *paniX, int *paniY) {
/**
* Moving actor's mid-top position.
*/
-void GetMActorMidTopPosition(PMACTOR pActor, int *aniX, int *aniY) {
- assert(pActor); // Getting null moving actor's mid-top position
- assert(pActor->actorObj); // Getting null 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(pActor->actorObj) + MultiRightmost(pActor->actorObj))/2;
- *aniY = MultiHighest(pActor->actorObj);
+ *aniX = (MultiLeftmost(pMover->actorObj) + MultiRightmost(pMover->actorObj)) / 2;
+ *aniY = MultiHighest(pMover->actorObj);
}
/**
* Moving actor's left-most co-ordinate.
*/
-int GetMActorLeft(PMACTOR pActor) {
- assert(pActor); // Getting null moving actor's leftmost position
- assert(pActor->actorObj); // Getting null moving actor's leftmost position
+int GetMoverLeft(PMOVER pMover) {
+ assert(pMover); // Getting null moving actor's leftmost position
+ assert(pMover->actorObj); // Getting null moving actor's leftmost position
- return MultiLeftmost(pActor->actorObj);
+ return MultiLeftmost(pMover->actorObj);
}
/**
* Moving actor's right-most co-ordinate.
*/
-int GetMActorRight(PMACTOR pActor) {
- assert(pActor); // Getting null moving actor's rightmost position
- assert(pActor->actorObj); // Getting null moving actor's rightmost position
+int GetMoverRight(PMOVER pMover) {
+ assert(pMover); // Getting null moving actor's rightmost position
+ 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
+ assert(pMover->actorObj); // Getting null moving actor's topmost position
- return MultiRightmost(pActor->actorObj);
+ return MultiHighest(pMover->actorObj);
+}
+
+/**
+ * Moving actor's bottom co-ordinate.
+ */
+int GetMoverBottom(PMOVER pMover) {
+ assert(pMover); // Getting null moving actor's bottommost position
+ assert(pMover->actorObj); // Getting null moving actor's bottommost position
+
+ return MultiLowest(pMover->actorObj);
}
/**
* See if moving actor is stood within a polygon.
*/
-bool MActorIsInPolygon(PMACTOR pActor, HPOLYGON hp) {
- assert(pActor); // Checking if null moving actor is in polygon
- assert(pActor->actorObj); // Checking if null moving actor is in 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(pActor->actorObj, &aniX, &aniY);
+ GetAniPosition(pMover->actorObj, &aniX, &aniY);
return IsInPolygon(aniX, aniY, hp);
}
@@ -321,157 +525,147 @@ bool MActorIsInPolygon(PMACTOR pActor, HPOLYGON hp) {
/**
* Change which reel is playing for a moving actor.
*/
-void AlterMActor(PMACTOR pActor, SCNHANDLE film, AR_FUNCTION fn) {
+void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn) {
const FILM *pfilm;
- assert(pActor->actorObj); // Altering null moving actor's animation script
+ assert(pMover->actorObj); // Altering null moving actor's animation script
if (fn == AR_POPREEL) {
- film = pActor->pushedfilm; // Use the saved film
+ // Use the saved film
+ film = pMover->hPushedFilm;
}
if (fn == AR_PUSHREEL) {
// Save the one we're replacing
- pActor->pushedfilm = (pActor->TagReelRunning) ? pActor->lastfilm : 0;
+ pMover->hPushedFilm = (pMover->bSpecReel) ? pMover->hLastFilm : 0;
}
if (film == 0) {
- if (pActor->TagReelRunning) {
+ if (pMover->bSpecReel) {
// Revert to 'normal' actor
- SetMActorWalkReel(pActor, pActor->dirn, pActor->scale, true);
- pActor->TagReelRunning = false;
+ SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true);
+ pMover->bSpecReel = false;
}
} else {
- pActor->lastfilm = film; // Remember this one
+ // Remember this one in case the actor talks
+ pMover->hLastFilm = film;
pfilm = (const FILM *)LockMem(film);
assert(pfilm != NULL);
- InitStepAnimScript(&pActor->actorAnim, pActor->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate));
- pActor->scount = 0;
+ InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate));
+ if (!TinselV2)
+ pMover->stepCount = 0;
// If no path, just use first path in the scene
- if (pActor->hCpath != NOPOLY)
- MAsetZPos(pActor, pActor->objy, getPolyZfactor(pActor->hCpath));
+ if (pMover->hCpath != NOPOLY)
+ SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
else
- MAsetZPos(pActor, pActor->objy, getPolyZfactor(FirstPathPoly()));
+ SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
if (fn == AR_WALKREEL) {
- pActor->TagReelRunning = false;
- pActor->walkReel = true;
+ pMover->bSpecReel = false;
+ pMover->bWalkReel = true;
} else {
- pActor->TagReelRunning = true;
- pActor->walkReel = false;
+ pMover->bSpecReel = true;
+ pMover->bWalkReel = false;
#ifdef DEBUG
- assert(StepAnimScript(&pActor->actorAnim) != ScriptFinished); // Actor reel has finished!
+ assert(StepAnimScript(&pMover->actorAnim) != ScriptFinished); // Actor reel has finished!
#else
- StepAnimScript(&pActor->actorAnim); // 04/01/95
+ StepAnimScript(&pMover->actorAnim); // 04/01/95
#endif
}
// Hang on, we may not want him yet! 04/01/95
- if (pActor->aHidden)
- MultiSetZPosition(pActor->actorObj, -1);
+ if (pMover->bHidden)
+ MultiSetZPosition(pMover->actorObj, -1);
}
}
/**
* Return the actor's direction.
*/
-DIRREEL GetMActorDirection(PMACTOR pActor) {
- return pActor->dirn;
+DIRECTION GetMoverDirection(PMOVER pMover) {
+ return pMover->direction;
}
/**
* Return the actor's scale.
*/
-int GetMActorScale(PMACTOR pActor) {
- return pActor->scale;
+int GetMoverScale(PMOVER pMover) {
+ return pMover->scale;
}
/**
* Point actor in specified derection
*/
-void SetMActorDirection(PMACTOR pActor, DIRREEL dirn) {
- pActor->dirn = dirn;
-}
-
-/**
- * MAmoving
- */
-bool MAmoving(PMACTOR pActor) {
- return pActor->bMoving;
-}
-
-/**
- * Return an actor's walk ticket.
- */
-int GetActorTicket(PMACTOR pActor) {
- return pActor->ticket;
+void SetMoverDirection(PMOVER pMover, DIRECTION dirn) {
+ pMover->direction = dirn;
}
/**
* Get actor to adopt its appropriate standing reel.
*/
-void SetMActorStanding(PMACTOR pActor) {
- assert(pActor->actorObj);
- AlterMActor(pActor, pActor->StandReels[pActor->scale-1][pActor->dirn], AR_NORMAL);
+void SetMoverStanding(PMOVER pMover) {
+ assert(pMover->actorObj);
+ AlterMover(pMover, pMover->standReels[pMover->scale - 1][pMover->direction], AR_NORMAL);
}
/**
* Get actor to adopt its appropriate walking reel.
*/
-void SetMActorWalkReel(PMACTOR pActor, DIRREEL reel, int scale, bool force) {
+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
- storeActorReel(pActor->actorID, NULL, 0, NULL, 0, 0, 0);
- unhideMActor(pActor);
+ storeActorReel(pMover->actorID, NULL, 0, NULL, 0, 0, 0);
+ UnHideMover(pMover);
// Don't do it if using a special walk reel
- if (pActor->walkReel)
+ if (pMover->bWalkReel)
return;
- if (force || pActor->scale != scale || pActor->dirn != reel) {
+ 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 (pActor->scale != scale
- && scale <= NUM_MAINSCALES && pActor->scale <= NUM_MAINSCALES
- && (whichReel = ScalingReel(pActor->actorID, pActor->scale, scale, reel)) != 0) {
+ 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 = pActor->WalkReels[scale-1][reel];
+ whichReel = pMover->walkReels[scale-1][reel];
assert(whichReel); // no reel
}
pfilm = (const FILM *)LockMem(whichReel);
assert(pfilm != NULL); // no film
- InitStepAnimScript(&pActor->actorAnim, pActor->actorObj, FROM_LE_32(pfilm->reels[0].script), 1);
+ InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), 1);
// Synchronised walking reels
- SkipFrames(&pActor->actorAnim, pActor->scount);
+ assert(pMover->stepCount >= 0);
+ SkipFrames(&pMover->actorAnim, pMover->stepCount);
- pActor->scale = scale;
- pActor->dirn = reel;
+ pMover->scale = scale;
+ pMover->direction = reel;
}
}
/**
* Sort some stuff out at actor start-up time.
*/
-static void InitialPathChecks(PMACTOR pActor, int xpos, int ypos) {
+static void InitialPathChecks(PMOVER pMover, int xpos, int ypos) {
HPOLYGON hPath;
int node;
int z;
- pActor->objx = xpos;
- pActor->objy = ypos;
+ pMover->objX = xpos;
+ pMover->objY = ypos;
/*--------------------------------------
| If Actor is in a follow nodes path, |
@@ -480,87 +674,38 @@ static void InitialPathChecks(PMACTOR pActor, int xpos, int ypos) {
hPath = InPolygon(xpos, ypos, PATH);
if (hPath != NOPOLY) {
- pActor->hCpath = hPath;
+ pMover->hCpath = hPath;
if (PolySubtype(hPath) == NODE) {
node = NearestNodeWithin(hPath, xpos, ypos);
- getNpathNode(hPath, node, &pActor->objx, &pActor->objy);
- pActor->hFnpath = hPath;
- pActor->line = node;
- pActor->npstatus = GOING_UP;
+ getNpathNode(hPath, node, &pMover->objX, &pMover->objY);
+ pMover->hFnpath = hPath;
+ pMover->line = node;
+ pMover->npstatus = GOING_UP;
}
- z = GetScale(hPath, pActor->objy);
+ z = GetScale(hPath, pMover->objY);
} else {
- pActor->bNoPath = true;
+ pMover->bNoPath = true;
- z = GetScale(FirstPathPoly(), pActor->objy);
+ z = GetScale(FirstPathPoly(), pMover->objY);
}
- SetMActorWalkReel(pActor, FORWARD, z, false);
-}
-
-/**
- * Clear everything out at actor start-up time.
- */
-static void InitMActor(PMACTOR pActor) {
-
- pActor->objx = pActor->objy = 0;
- pActor->targetX = pActor->targetY = -1;
- pActor->ItargetX = pActor->ItargetY = -1;
- pActor->hIpath = NOPOLY;
- pActor->UtargetX = pActor->UtargetY = -1;
- pActor->hUpath = NOPOLY;
- pActor->hCpath = NOPOLY;
-
- pActor->over = false;
- pActor->InDifficulty = NO_PROB;
-
- pActor->hFnpath = NOPOLY;
- pActor->npstatus = NOT_IN;
- pActor->line = 0;
-
- pActor->Tline = 0;
-
- pActor->TagReelRunning = false;
-
- if (pActor->dirn != FORWARD || pActor->dirn != AWAY
- || pActor->dirn != LEFTREEL || pActor->dirn != RIGHTREEL)
- pActor->dirn = FORWARD;
-
- if (pActor->scale < 0 || pActor->scale > TOTAL_SCALES)
- pActor->scale = 1;
-
- pActor->scount = 0;
-
- pActor->fromx = pActor->fromy = 0;
-
- pActor->bMoving = false;
- pActor->bNoPath = false;
- pActor->bIgPath = false;
- pActor->walkReel = false;
-
- pActor->actorObj = NULL;
-
- pActor->lastfilm = 0;
- pActor->pushedfilm = 0;
-
- pActor->InEffect = false;
- pActor->aHidden = false; // 20/2/95
+ SetMoverWalkReel(pMover, FORWARD, z, false);
}
-static void MActorProcessHelper(int X, int Y, int id, PMACTOR pActor) {
+static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) {
const FILM *pfilm;
const MULTI_INIT *pmi;
const FRAME *pFrame;
IMAGE *pim;
- assert(BackPal()); // Can't start actor without a background palette
- assert(pActor->WalkReels[0][FORWARD]); // Starting actor process without walk reels
+ assert(BgPal()); // Can't start actor without a background palette
+ assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels
- InitMActor(pActor);
- InitialPathChecks(pActor, X, Y);
+ InitMover(pMover);
+ InitialPathChecks(pMover, X, Y);
- pfilm = (const FILM *)LockMem(pActor->WalkReels[0][FORWARD]);
+ pfilm = (const FILM *)LockMem(pMover->walkReels[0][FORWARD]);
pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfilm->reels[0].mobj));
//---
@@ -568,55 +713,55 @@ static void MActorProcessHelper(int X, int Y, int id, PMACTOR pActor) {
// get pointer to image
pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image
- pim->hImgPal = TO_LE_32(BackPal());
+ pim->hImgPal = TO_LE_32(BgPal());
//---
- pActor->actorObj = MultiInitObject(pmi);
+ pMover->actorObj = MultiInitObject(pmi);
-/**/ assert(pActor->actorID == id);
- pActor->actorID = id;
+/**/ assert(pMover->actorID == id);
+ pMover->actorID = id;
// add it to display list
- MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pActor->actorObj);
- storeActorReel(id, NULL, 0, pActor->actorObj, 0, 0, 0);
+ MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
+ storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0);
- InitStepAnimScript(&pActor->actorAnim, pActor->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate));
- pActor->scount = 0;
+ InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate));
+ pMover->stepCount = 0;
- MultiSetAniXY(pActor->actorObj, pActor->objx, pActor->objy);
+ MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);
// If no path, just use first path in the scene
- if (pActor->hCpath != NOPOLY)
- MAsetZPos(pActor, pActor->objy, getPolyZfactor(pActor->hCpath));
+ if (pMover->hCpath != NOPOLY)
+ SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
else
- MAsetZPos(pActor, pActor->objy, getPolyZfactor(FirstPathPoly()));
+ SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
// Make him the right size
- SetMActorStanding(pActor);
+ SetMoverStanding(pMover);
//**** if added 18/11/94, am
if (X != MAGICX && Y != MAGICY) {
- hideMActor(pActor, 0); // Allows a play to come in before this appears
- pActor->aHidden = false; // ...but don't stay hidden
+ HideMover(pMover, 0); // Allows a play to come in before this appears
+ pMover->bHidden = false; // ...but don't stay hidden
}
- pActor->MActorState = NORM_MACTOR;
+ pMover->bActive = true;
}
/**
* Moving actor process - 1 per moving actor in current scene.
*/
-void MActorProcess(CORO_PARAM, const void *param) {
+void T1MoverProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
- PMACTOR pActor = *(PMACTOR *)param;
+ const PMOVER pActor = *(const PMOVER *)param;
CORO_BEGIN_CODE(_ctx);
while (1) {
- if (pActor->TagReelRunning) {
- if (!pActor->aHidden)
+ if (pActor->bSpecReel) {
+ if (!pActor->bHidden)
#ifdef DEBUG
assert(StepAnimScript(&pActor->actorAnim) != ScriptFinished); // Actor reel has finished!
#else
@@ -632,34 +777,118 @@ void MActorProcess(CORO_PARAM, const void *param) {
CORO_END_CODE;
}
-void MActorProcessCreate(int X, int Y, int id, PMACTOR pActor) {
- MActorProcessHelper(X, Y, id, pActor);
- pActor->pProc = g_scheduler->createProcess(PID_MACTOR, MActorProcess, &pActor, sizeof(PMACTOR));
+/**
+ * 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(pFilm->reels[0].mobj);
+
+ // Poke in the background palette
+ PokeInPalette(pmi);
+
+ pMover->actorObj = MultiInitObject(pmi);
+ 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;
}
+/**
+ * Creates a handling process for a moving actor
+ */
+void MoverProcessCreate(int X, int Y, int id, PMOVER pMover) {
+ if (TinselV2) {
+ static MAINIT iStruct;
+ iStruct.X = X;
+ iStruct.Y = Y;
+ iStruct.pMover = pMover;
+
+ g_scheduler->createProcess(PID_MOVER, T2MoverProcess, &iStruct, sizeof(MAINIT));
+ } else {
+ MoverProcessHelper(X, Y, id, pMover);
+ pMover->pProc = g_scheduler->createProcess(PID_MOVER, T1MoverProcess, &pMover, sizeof(PMOVER));
+ }
+}
/**
* Check for moving actor collision.
*/
-PMACTOR InMActorBlock(PMACTOR pActor, int x, int y) {
+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 = pActor->objx;
- if (pActor->hFnpath != NOPOLY || bNoBlocking)
+ caX = pMover->objX;
+ if (pMover->hFnpath != NOPOLY || GetNoBlocking())
return NULL;
- caL = GetMActorLeft(pActor) + x - caX;
- caR = GetMActorRight(pActor) + x - caX;
+ caL = GetMoverLeft(pMover) + x - caX;
+ caR = GetMoverRight(pMover) + x - caX;
for (int i = 0; i < MAX_MOVERS; i++) {
- if (pActor == &Movers[i] || Movers[i].MActorState == NO_MACTOR)
+ if (pMover == &Movers[i] ||
+ (TinselV2 && (Movers[i].actorObj == NULL)) ||
+ (!TinselV2 && !Movers[i].bActive))
continue;
// At around the same height?
- GetMActorPosition(&Movers[i], &taX, &taY);
+ GetMoverPosition(&Movers[i], &taX, &taY);
if (Movers[i].hFnpath != NOPOLY)
continue;
@@ -667,12 +896,12 @@ PMACTOR InMActorBlock(PMACTOR pActor, int x, int y) {
continue;
// To the left?
- taL = GetMActorLeft(&Movers[i]);
+ taL = GetMoverLeft(&Movers[i]);
if (caR <= taL)
continue;
// To the right?
- taR = GetMActorRight(&Movers[i]);
+ taR = GetMoverRight(&Movers[i]);
if (caL >= taR)
continue;
@@ -686,24 +915,54 @@ PMACTOR InMActorBlock(PMACTOR pActor, int x, int y) {
*/
void SaveMovers(SAVED_MOVER *sMoverInfo) {
for (int i = 0; i < MAX_MOVERS; i++) {
- sMoverInfo[i].MActorState= Movers[i].MActorState;
+ sMoverInfo[i].bActive = !TinselV2 ? Movers[i].bActive : Movers[i].actorObj != NULL;
sMoverInfo[i].actorID = Movers[i].actorID;
- sMoverInfo[i].objx = Movers[i].objx;
- sMoverInfo[i].objy = Movers[i].objy;
- sMoverInfo[i].lastfilm = Movers[i].lastfilm;
+ sMoverInfo[i].objX = Movers[i].objX;
+ sMoverInfo[i].objY = Movers[i].objY;
+ sMoverInfo[i].hLastfilm = Movers[i].hLastFilm;
+
+ if (TinselV2) {
+ sMoverInfo[i].bHidden = Movers[i].bHidden;
+ sMoverInfo[i].brightness = Movers[i].brightness;
+ sMoverInfo[i].startColour = Movers[i].startColour;
+ sMoverInfo[i].paletteLength = Movers[i].paletteLength;
+ }
- memcpy(sMoverInfo[i].WalkReels, Movers[i].WalkReels, TOTAL_SCALES*4*sizeof(SCNHANDLE));
- memcpy(sMoverInfo[i].StandReels, Movers[i].StandReels, TOTAL_SCALES*4*sizeof(SCNHANDLE));
- memcpy(sMoverInfo[i].TalkReels, Movers[i].TalkReels, TOTAL_SCALES*4*sizeof(SCNHANDLE));
+ memcpy(sMoverInfo[i].walkReels, Movers[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
+ memcpy(sMoverInfo[i].standReels, Movers[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
+ memcpy(sMoverInfo[i].talkReels, Movers[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
}
}
void RestoreAuxScales(SAVED_MOVER *sMoverInfo) {
for (int i = 0; i < MAX_MOVERS; i++) {
- memcpy(Movers[i].WalkReels, sMoverInfo[i].WalkReels, TOTAL_SCALES*4*sizeof(SCNHANDLE));
- memcpy(Movers[i].StandReels, sMoverInfo[i].StandReels, TOTAL_SCALES*4*sizeof(SCNHANDLE));
- memcpy(Movers[i].TalkReels, sMoverInfo[i].TalkReels, TOTAL_SCALES*4*sizeof(SCNHANDLE));
+ if (TinselV2)
+ Movers[i].actorID = sMoverInfo[i].actorID;
+
+ memcpy(Movers[i].walkReels, sMoverInfo[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
+ memcpy(Movers[i].standReels, sMoverInfo[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
+ memcpy(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 - Movers + 1;
+
+ if (Movers[next].actorID)
+ return &Movers[next];
+ else
+ return NULL;
+}
+
+void StopMover(PMOVER pMover) {
+ pMover->bStop = true;
+ DoMoveActor(pMover);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/rince.h b/engines/tinsel/rince.h
index 7ccf017c65..4a60fdca07 100644
--- a/engines/tinsel/rince.h
+++ b/engines/tinsel/rince.h
@@ -29,6 +29,7 @@
#include "tinsel/anim.h" // for ANIM
#include "tinsel/scene.h" // for TFTYPE
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -39,31 +40,30 @@ enum NPS {NOT_IN, GOING_UP, GOING_DOWN, LEAVING, ENTERING};
enum IND {NO_PROB, TRY_CENTRE, TRY_CORNER, TRY_NEXTCORNER};
-enum MAS {NO_MACTOR, NORM_MACTOR};
+enum DIRECTION { LEFTREEL, RIGHTREEL, FORWARD, AWAY };
-enum DIRREEL{ LEFTREEL, RIGHTREEL, FORWARD, AWAY };
+#define NUM_MAINSCALES (TinselV2 ? 10 : 5)
+#define NUM_AUXSCALES 5
+#define TOTAL_SCALES (NUM_MAINSCALES + NUM_AUXSCALES)
+#define REQ_MAIN_SCALES 10
+#define REQ_TOTAL_SCALES 15
-enum {
- NUM_MAINSCALES = 5,
- NUM_AUXSCALES = 5,
- TOTAL_SCALES = NUM_MAINSCALES + NUM_AUXSCALES
-};
+#define BOGUS_BRIGHTNESS -1
-struct MACTOR {
+struct MOVER {
- int objx; /* Co-ordinates object */
- int objy;
+ int objX, objY; /* Co-ordinates object */
int targetX, targetY;
int ItargetX, ItargetY; /* Intermediate destination */
- HPOLYGON hIpath;
int UtargetX, UtargetY; /* Ultimate destination */
- HPOLYGON hUpath;
+ HPOLYGON hIpath;
+ HPOLYGON hUpath;
HPOLYGON hCpath;
bool over;
- int ticket;
+ int walkNumber;
IND InDifficulty;
@@ -74,128 +74,146 @@ struct MACTOR {
int Tline; // NEW
- bool TagReelRunning;
-
+ // TODO: TagReelRunning may be the same as bSpecReel
+ bool bSpecReel;
/* Used internally */
- DIRREEL dirn; // Current reel
- int scale; // Current scale
- int scount; // Step count for walking reel synchronisation
+ DIRECTION direction; // Current reel
+ int scale; // Current scale
+
+ int stepCount; // Step count for walking reel synchronisation
- unsigned fromx;
- unsigned fromy;
+ int walkedFromX, walkedFromY;
bool bMoving; // Set this to TRUE during a walk
bool bNoPath;
bool bIgPath;
- bool walkReel;
+ bool bWalkReel;
OBJECT *actorObj; // Actor's object
ANIM actorAnim; // Actor's animation script
- SCNHANDLE lastfilm; // } Used by AlterActor()
- SCNHANDLE pushedfilm; // }
+ SCNHANDLE hLastFilm; // } Used by AlterMover()
+ SCNHANDLE hPushedFilm; // }
int actorID;
int actorToken;
- SCNHANDLE WalkReels[TOTAL_SCALES][4];
- SCNHANDLE StandReels[TOTAL_SCALES][4];
- SCNHANDLE TalkReels[TOTAL_SCALES][4];
+ SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
+ SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
+ SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
- MAS MActorState;
+ bool bActive;
- bool aHidden;
int SlowFactor; // Slow down movement while hidden
- bool stop;
+ bool bStop;
/* NOTE: If effect polys can overlap, this needs improving */
- bool InEffect;
+ bool bInEffect;
PROCESS *pProc;
+
+ // Discworld 2 specific fields
+ int32 zOverride;
+ bool bHidden;
+ int brightness; // Current brightness
+ int startColour;
+ int paletteLength;
+ HPOLYGON hRpath; // Recent path
+};
+typedef MOVER *PMOVER;
+
+struct MAINIT {
+ int X;
+ int Y;
+ PMOVER pMover;
};
-typedef MACTOR *PMACTOR;
+typedef MAINIT *PMAINIT;
//---------------------------------------------------------------------------
-void MActorProcessCreate(int X, int Y, int id, MACTOR *pActor);
+void MoverProcessCreate(int X, int Y, int id, PMOVER pMover);
enum AR_FUNCTION { AR_NORMAL, AR_PUSHREEL, AR_POPREEL, AR_WALKREEL };
-
-MACTOR *GetMover(int ano);
-MACTOR *SetMover(int ano);
-void KillMActor(MACTOR *pActor);
-MACTOR *GetLiveMover(int index);
-
-MAS getMActorState(MACTOR *psActor);
-
-void hideMActor(MACTOR *pActor, int sf);
-bool getMActorHideState(MACTOR *pActor);
-void unhideMActor(MACTOR *pActor);
-void DropMActors(void);
-void MoveMActor(MACTOR *pActor, int x, int y);
-
-void GetMActorPosition(MACTOR *pActor, int *aniX, int *aniY);
-void GetMActorMidTopPosition(MACTOR *pActor, int *aniX, int *aniY);
-int GetMActorLeft(MACTOR *pActor);
-int GetMActorRight(MACTOR *pActor);
-
-bool MActorIsInPolygon(MACTOR *pActor, HPOLYGON hPoly);
-void AlterMActor(MACTOR *actor, SCNHANDLE film, AR_FUNCTION fn);
-DIRREEL GetMActorDirection(MACTOR *pActor);
-int GetMActorScale(MACTOR *pActor);
-void SetMActorDirection(MACTOR *pActor, DIRREEL dirn);
-void SetMActorStanding(MACTOR *actor);
-void SetMActorWalkReel(MACTOR *actor, DIRREEL reel, int scale, bool force);
-
-MACTOR *InMActorBlock(MACTOR *pActor, int x, int y);
+void StoreMoverPalette(PMOVER pMover, int startColour, int length);
+
+void MoverBrightness(PMOVER pMover, int brightness);
+
+MOVER *GetMover(int ano);
+MOVER *RegisterMover(int ano);
+void KillMover(PMOVER pMover);
+MOVER *GetLiveMover(int index);
+
+bool getMActorState(MOVER *psActor);
+int GetMoverId(PMOVER pMover);
+void SetMoverZ(PMOVER pMover, int y, uint32 zFactor);
+void SetMoverZoverride(PMOVER pMover, uint32 zFactor);
+
+void HideMover(PMOVER pMover, int sf = 0);
+bool MoverHidden(PMOVER pMover);
+bool MoverIs(PMOVER pMover);
+bool MoverIsSWalking(PMOVER pMover);
+bool MoverMoving(PMOVER pMover);
+int GetWalkNumber(PMOVER pMover);
+void UnHideMover(PMOVER pMover);
+void DropMovers(void);
+void PositionMover(PMOVER pMover, int x, int y);
+
+void GetMoverPosition(PMOVER pMover, int *aniX, int *aniY);
+void GetMoverMidTop(PMOVER pMover, int *aniX, int *aniY);
+int GetMoverLeft(PMOVER pMover);
+int GetMoverRight(PMOVER pMover);
+int GetMoverTop(PMOVER pMover);
+int GetMoverBottom(PMOVER pMover);
+
+bool MoverIsInPolygon(PMOVER pMover, HPOLYGON hPoly);
+void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn);
+DIRECTION GetMoverDirection(PMOVER pMover);
+int GetMoverScale(PMOVER pMover);
+void SetMoverDirection(PMOVER pMover, DIRECTION dirn);
+void SetMoverStanding(PMOVER pMover);
+void SetMoverWalkReel(PMOVER pMover, DIRECTION reel, int scale, bool force);
+
+PMOVER InMoverBlock(PMOVER pMover, int x, int y);
void RebootMovers(void);
bool IsMAinEffectPoly(int index);
-void SetMAinEffectPoly(int index, bool tf);
-
-bool MAmoving(MACTOR *pActor);
+void SetMoverInEffect(int index, bool tf);
-int GetActorTicket(MACTOR *pActor);
+void StopMover(PMOVER pMover);
/*----------------------------------------------------------------------*/
struct SAVED_MOVER {
- MAS MActorState;
- int actorID;
- int objx;
- int objy;
- SCNHANDLE lastfilm;
+ int actorID;
+ int objX;
+ int objY;
+ SCNHANDLE hLastfilm;
- SCNHANDLE WalkReels[TOTAL_SCALES][4];
- SCNHANDLE StandReels[TOTAL_SCALES][4];
- SCNHANDLE TalkReels[TOTAL_SCALES][4];
+ SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
+ SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
+ SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
+ bool bActive;
+ bool bHidden;
+ int brightness;
+ int startColour;
+ int paletteLength;
};
void SaveMovers(SAVED_MOVER *sMoverInfo);
void RestoreAuxScales(SAVED_MOVER *sMoverInfo);
-/*----------------------------------------------------------------------*/
+PMOVER NextMover(PMOVER pMover);
-/*
-* Dodgy bit...
-* These functions are now in MAREELS.C, but I can't be bothered to
-* create a new header file.
-*/
-SCNHANDLE GetMactorTalkReel(MACTOR *pAactor, TFTYPE dirn);
-
-void setscalingreels(int actor, int scale, int direction,
- SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away);
-SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRREEL reel);
-void RebootScalingReels(void);
+/*----------------------------------------------------------------------*/
enum {
MAGICX = -101,
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index 73a3faf602..3d532300c2 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -25,8 +25,9 @@
*/
#include "tinsel/actors.h"
+#include "tinsel/dialogs.h"
+#include "tinsel/drives.h"
#include "tinsel/dw.h"
-#include "tinsel/inventory.h"
#include "tinsel/rince.h"
#include "tinsel/savescn.h"
#include "tinsel/serializer.h"
@@ -63,7 +64,11 @@ namespace Tinsel {
#define VER(x) x
+//----------------- GLOBAL GLOBAL DATA --------------------
+int thingHeld = 0;
+int restoreCD = 0;
+SRSTATE SRstate = SR_IDLE;
//----------------- EXTERN FUNCTIONS --------------------
@@ -79,9 +84,6 @@ extern void syncGlobInfo(Serializer &s);
// in POLYGONS.C
extern void syncPolyInfo(Serializer &s);
-// in SAVESCN.CPP
-extern void RestoreScene(SAVED_DATA *sd, bool bFadeOut);
-
//----------------- LOCAL DEFINES --------------------
struct SaveGameHeader {
@@ -93,15 +95,17 @@ struct SaveGameHeader {
};
enum {
- SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld ScummVM"
+ DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM"
+ DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWOrld 2 ScummVM"
SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7
};
+#define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
//----------------- LOCAL GLOBAL DATA --------------------
static int numSfiles = 0;
-static SFILES savedFiles[MAX_SFILES];
+static SFILES savedFiles[MAX_SAVED_FILES];
static bool NeedLoad = true;
@@ -112,8 +116,6 @@ static const char *SaveSceneDesc = 0;
static int *SaveSceneSsCount = 0;
static char *SaveSceneSsData = 0; // points to 'SAVED_DATA ssdata[MAX_NEST]'
-static SRSTATE SRstate = SR_IDLE;
-
//------------- SAVE/LOAD SUPPORT METHODS ----------------
static void syncTime(Serializer &s, struct tm &t) {
@@ -153,29 +155,38 @@ static bool syncSaveGameHeader(Serializer &s, SaveGameHeader &hdr) {
}
static void syncSavedMover(Serializer &s, SAVED_MOVER &sm) {
- SCNHANDLE *pList[3] = { (SCNHANDLE *)&sm.WalkReels, (SCNHANDLE *)&sm.StandReels, (SCNHANDLE *)&sm.TalkReels };
+ SCNHANDLE *pList[3] = { (SCNHANDLE *)&sm.walkReels,
+ (SCNHANDLE *)&sm.standReels, (SCNHANDLE *)&sm.talkReels };
- s.syncAsUint32LE(sm.MActorState);
+ s.syncAsUint32LE(sm.bActive);
s.syncAsSint32LE(sm.actorID);
- s.syncAsSint32LE(sm.objx);
- s.syncAsSint32LE(sm.objy);
- s.syncAsUint32LE(sm.lastfilm);
+ s.syncAsSint32LE(sm.objX);
+ s.syncAsSint32LE(sm.objY);
+ s.syncAsUint32LE(sm.hLastfilm);
for (int pIndex = 0; pIndex < 3; ++pIndex) {
SCNHANDLE *p = pList[pIndex];
for (int i = 0; i < TOTAL_SCALES * 4; ++i)
s.syncAsUint32LE(*p++);
}
+
+ if (TinselV2) {
+ s.syncAsByte(sm.bHidden);
+
+ s.syncAsSint32LE(sm.brightness);
+ s.syncAsSint32LE(sm.startColour);
+ s.syncAsSint32LE(sm.paletteLength);
+ }
}
static void syncSavedActor(Serializer &s, SAVED_ACTOR &sa) {
s.syncAsUint16LE(sa.actorID);
- s.syncAsUint16LE(sa.z);
+ s.syncAsUint16LE(sa.zFactor);
s.syncAsUint32LE(sa.bAlive);
s.syncAsUint32LE(sa.presFilm);
s.syncAsUint16LE(sa.presRnum);
- s.syncAsUint16LE(sa.presX);
- s.syncAsUint16LE(sa.presY);
+ s.syncAsUint16LE(sa.presPlayX);
+ s.syncAsUint16LE(sa.presPlayY);
}
extern void syncAllActorsAlive(Serializer &s);
@@ -186,6 +197,24 @@ static void syncNoScrollB(Serializer &s, NOSCROLLB &ns) {
s.syncAsSint32LE(ns.c2);
}
+static void syncZPosition(Serializer &s, Z_POSITIONS &zp) {
+ s.syncAsSint16LE(zp.actor);
+ s.syncAsSint16LE(zp.column);
+ s.syncAsSint32LE(zp.z);
+}
+
+static void syncPolyVolatile(Serializer &s, POLY_VOLATILE &p) {
+ s.syncAsByte(p.bDead);
+ s.syncAsSint16LE(p.xoff);
+ s.syncAsSint16LE(p.yoff);
+}
+
+static void syncSoundReel(Serializer &s, SOUNDREELS &sr) {
+ s.syncAsUint32LE(sr.hFilm);
+ s.syncAsSint32LE(sr.column);
+ s.syncAsSint32LE(sr.actorCol);
+}
+
static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
s.syncAsUint32LE(sd.SavedSceneHandle);
s.syncAsUint32LE(sd.SavedBgroundHandle);
@@ -197,7 +226,7 @@ static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
s.syncAsSint32LE(sd.NumSavedActors);
s.syncAsSint32LE(sd.SavedLoffset);
s.syncAsSint32LE(sd.SavedToffset);
- for (int i = 0; i < MAX_INTERPRET; ++i)
+ for (int i = 0; i < NUM_INTERPRET; ++i)
sd.SavedICInfo[i].syncWithSerializer(s);
for (int i = 0; i < MAX_POLY; ++i)
s.syncAsUint32LE(sd.SavedDeadPolys[i]);
@@ -213,6 +242,32 @@ static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoH);
+
+ // Tinsel 2 fields
+ if (TinselV2) {
+ // SavedNoScrollData
+ s.syncAsUint32LE(sd.SavedNoScrollData.xTrigger);
+ s.syncAsUint32LE(sd.SavedNoScrollData.xDistance);
+ s.syncAsUint32LE(sd.SavedNoScrollData.xSpeed);
+ s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerTop);
+ s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerBottom);
+ s.syncAsUint32LE(sd.SavedNoScrollData.yDistance);
+ s.syncAsUint32LE(sd.SavedNoScrollData.ySpeed);
+
+ for (int i = 0; i < NUM_ZPOSITIONS; ++i)
+ syncZPosition(s, sd.zPositions[i]);
+ s.syncBytes(sd.savedActorZ, MAX_SAVED_ACTOR_Z);
+ for (int i = 0; i < MAX_POLY; ++i)
+ syncPolyVolatile(s, sd.SavedPolygonStuff[i]);
+ for (int i = 0; i < 3; ++i)
+ s.syncAsUint32LE(sd.SavedTune[i]);
+ s.syncAsByte(sd.bTinselDim);
+ s.syncAsSint32LE(sd.SavedScrollFocus);
+ for (int i = 0; i < SV_TOPVALID; ++i)
+ s.syncAsSint32LE(sd.SavedSystemVars[i]);
+ for (int i = 0; i < MAX_SOUNDREELS; ++i)
+ syncSoundReel(s, sd.SavedSoundReels[i]);
+ }
}
@@ -247,6 +302,11 @@ static char *NewName(void) {
* the number of files found).
*/
int getList(Common::SaveFileManager *saveFileMan, const Common::String &target) {
+ // No change since last call?
+ // TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
+ if (!NeedLoad)
+ return numSfiles;
+
int i;
const Common::String pattern = target + ".???";
@@ -255,7 +315,7 @@ int getList(Common::SaveFileManager *saveFileMan, const Common::String &target)
numSfiles = 0;
for (Common::StringList::const_iterator file = files.begin(); file != files.end(); ++file) {
- if (numSfiles >= MAX_SFILES)
+ if (numSfiles >= MAX_SAVED_FILES)
break;
const Common::String &fname = *file;
@@ -304,11 +364,10 @@ int getList(void) {
// TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
if (!NeedLoad)
return numSfiles;
-
+
return getList(_vm->getSaveFileMan(), _vm->getTargetName());
}
-
char *ListEntry(int i, letype which) {
if (i == -1)
i = numSfiles;
@@ -322,7 +381,16 @@ char *ListEntry(int i, letype which) {
}
static void DoSync(Serializer &s) {
- int sg = 0;
+ int sg;
+
+ if (TinselV2) {
+ if (s.isSaving())
+ restoreCD = GetCurrentCD();
+ s.syncAsSint16LE(restoreCD);
+ }
+
+ if (TinselV2 && s.isLoading())
+ HoldItem(INV_NOICON);
syncSavedData(s, *srsd);
syncGlobInfo(s); // Glitter globals
@@ -332,11 +400,16 @@ static void DoSync(Serializer &s) {
if (s.isSaving())
sg = WhichItemHeld();
s.syncAsSint32LE(sg);
- if (s.isLoading())
- HoldItem(sg);
+ if (s.isLoading()) {
+ if (TinselV2)
+ thingHeld = sg;
+ else
+ HoldItem(sg);
+ }
syncTimerInfo(s); // Timer data
- syncPolyInfo(s); // Dead polygon data
+ if (!TinselV2)
+ syncPolyInfo(s); // Dead polygon data
syncSCdata(s); // Hook Scene and delayed scene
s.syncAsSint32LE(*SaveSceneSsCount);
@@ -347,7 +420,8 @@ static void DoSync(Serializer &s) {
syncSavedData(s, *sdPtr);
}
- syncAllActorsAlive(s);
+ if (!TinselV2)
+ syncAllActorsAlive(s);
}
/**
@@ -440,7 +514,7 @@ void ProcessSRQueue(void) {
switch (SRstate) {
case SR_DORESTORE:
if (DoRestore()) {
- RestoreScene(srsd, false);
+ DoRestoreScene(srsd, false);
}
SRstate = SR_IDLE;
break;
@@ -467,6 +541,15 @@ void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *pSsCount, SAVE
}
void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
+ if (TinselV2) {
+ if (num == -1)
+ return;
+ else if (num == -2) {
+ // From CD change for restore
+ num = RestoreGameNumber;
+ }
+ }
+
assert(num >= 0);
RestoreGameNumber = num;
@@ -476,4 +559,14 @@ void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsD
SRstate = SR_DORESTORE;
}
+/**
+ * Returns the index of the most recently saved savegame. This will always be
+ * the file at the first index, since the list is sorted by date/time
+ */
+int NewestSavedGame(void) {
+ int numFiles = getList();
+
+ return (numFiles == 0) ? -1 : 0;
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/savescn.cpp b/engines/tinsel/savescn.cpp
index 9f0d7b9039..418868aa74 100644
--- a/engines/tinsel/savescn.cpp
+++ b/engines/tinsel/savescn.cpp
@@ -28,19 +28,24 @@
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
+#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/faders.h" // FadeOutFast()
#include "tinsel/graphics.h" // ClearScreen()
#include "tinsel/handle.h"
-#include "tinsel/inventory.h"
+#include "tinsel/heapmem.h"
+#include "tinsel/dialogs.h"
#include "tinsel/music.h"
#include "tinsel/pid.h"
+#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/savescn.h"
+#include "tinsel/scene.h"
#include "tinsel/sched.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h"
+#include "tinsel/sysvar.h"
#include "tinsel/tinlib.h"
#include "tinsel/token.h"
@@ -49,7 +54,6 @@ namespace Tinsel {
//----------------- EXTERN FUNCTIONS --------------------
// in BG.C
-extern void startupBackground(SCNHANDLE bfilm);
extern SCNHANDLE GetBgroundHandle(void);
extern void SetDoFadeIn(bool tf);
@@ -59,14 +63,8 @@ void RestoreMasterProcess(INT_CONTEXT *pic);
// in EVENTS.C (declared here and not in events.h because of strange goings-on)
void RestoreProcess(INT_CONTEXT *pic);
-// in PLAY.C
-extern void playThisReel(SCNHANDLE film, short reelnum, short z, int x, int y);
-
// in SCENE.C
extern SCNHANDLE GetSceneHandle(void);
-extern void NewScene(SCNHANDLE scene, int entry);
-
-
//----------------- LOCAL DEFINES --------------------
@@ -77,6 +75,11 @@ enum {
MAX_NEST = 4
};
+//----------------- EXTERNAL GLOBAL DATA --------------------
+
+extern int thingHeld;
+extern int restoreCD;
+extern SRSTATE SRstate;
//----------------- LOCAL GLOBAL DATA --------------------
@@ -84,54 +87,52 @@ static bool ASceneIsSaved = false;
static int savedSceneCount = 0;
-//static SAVED_DATA s_ssData[MAX_NEST];
-static SAVED_DATA *s_ssData = 0;
+static bool bNotDoneYet = false;
+
+//static SAVED_DATA ssData[MAX_NEST];
+static SAVED_DATA *ssData = NULL;
static SAVED_DATA sgData;
-static SAVED_DATA *s_rsd = 0;
+static SAVED_DATA *rsd = 0;
-static int s_restoreSceneCount = 0;
+static int RestoreSceneCount = 0;
static bool bNoFade = false;
//----------------- FORWARD REFERENCES --------------------
-
-
-void InitialiseSs(void) {
- if (s_ssData == NULL) {
- s_ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
- if (s_ssData == NULL) {
- error("Cannot allocate memory for scene changes");
- }
- } else
- savedSceneCount = 0;
-}
-
-void FreeSs(void) {
- if (s_ssData) {
- free(s_ssData);
- s_ssData = NULL;
- }
-}
-
/**
* Save current scene.
* @param sd Pointer to the scene data
*/
-void SaveScene(SAVED_DATA *sd) {
+void DoSaveScene(SAVED_DATA *sd) {
sd->SavedSceneHandle = GetSceneHandle();
sd->SavedBgroundHandle = GetBgroundHandle();
SaveMovers(sd->SavedMoverInfo);
sd->NumSavedActors = SaveActors(sd->SavedActorInfo);
PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
SaveInterpretContexts(sd->SavedICInfo);
- SaveDeadPolys(sd->SavedDeadPolys);
- sd->SavedControl = TestToken(TOKEN_CONTROL);
- CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
- sd->SavedNoBlocking = bNoBlocking;
+ sd->SavedControl = ControlIsOn();
+ sd->SavedNoBlocking = GetNoBlocking();
GetNoScrollData(&sd->SavedNoScrollData);
+ if (TinselV2) {
+ // Tinsel 2 specific data save
+ SaveActorZ(sd->savedActorZ);
+ SaveZpositions(sd->zPositions);
+ SavePolygonStuff(sd->SavedPolygonStuff);
+ _vm->_pcmMusic->getTunePlaying(sd->SavedTune, sizeof(sd->SavedTune));
+ sd->bTinselDim = _vm->_pcmMusic->getMusicTinselDimmed();
+ sd->SavedScrollFocus = GetScrollFocus();
+ SaveSysVars(sd->SavedSystemVars);
+ SaveSoundReels(sd->SavedSoundReels);
+
+ } else {
+ // Tinsel 1 specific data save
+ SaveDeadPolys(sd->SavedDeadPolys);
+ CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
+ }
+
ASceneIsSaved = true;
}
@@ -140,13 +141,32 @@ void SaveScene(SAVED_DATA *sd) {
* @param sd Pointer to the scene data
* @param bFadeOut Flag to perform a fade out
*/
-void RestoreScene(SAVED_DATA *sd, bool bFadeOut) {
- s_rsd = sd;
+void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
+ rsd = sd;
if (bFadeOut)
- s_restoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
+ RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
else
- s_restoreSceneCount = RS_COUNT; // Set restore scene count
+ RestoreSceneCount = RS_COUNT; // Set restore scene count
+}
+
+void InitialiseSaveScenes(void) {
+ if (ssData == NULL) {
+ ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
+ if (ssData == NULL) {
+ error("Cannot allocate memory for scene changes");
+ }
+ } else {
+ // Re-initialise - no scenes saved
+ savedSceneCount = 0;
+ }
+}
+
+void FreeSaveScenes(void) {
+ if (ssData) {
+ free(ssData);
+ ssData = NULL;
+ }
}
/**
@@ -154,39 +174,84 @@ void RestoreScene(SAVED_DATA *sd, bool bFadeOut) {
* the scene was saved.
* Also 'stand' all the moving actors at their saved positions.
*/
-void sortActors(SAVED_DATA *rsd) {
- for (int i = 0; i < rsd->NumSavedActors; i++) {
- ActorsLife(rsd->SavedActorInfo[i].actorID, rsd->SavedActorInfo[i].bAlive);
+void sortActors(SAVED_DATA *sd) {
+ assert(!TinselV2);
+ for (int i = 0; i < sd->NumSavedActors; i++) {
+ ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
// Should be playing the same reel.
- if (rsd->SavedActorInfo[i].presFilm != 0) {
- if (!actorAlive(rsd->SavedActorInfo[i].actorID))
+ if (sd->SavedActorInfo[i].presFilm != 0) {
+ if (!actorAlive(sd->SavedActorInfo[i].actorID))
continue;
- playThisReel(rsd->SavedActorInfo[i].presFilm, rsd->SavedActorInfo[i].presRnum, rsd->SavedActorInfo[i].z,
- rsd->SavedActorInfo[i].presX, rsd->SavedActorInfo[i].presY);
+ RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
+ sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
}
}
- RestoreAuxScales(rsd->SavedMoverInfo);
+ RestoreAuxScales(sd->SavedMoverInfo);
for (int i = 0; i < MAX_MOVERS; i++) {
- if (rsd->SavedMoverInfo[i].MActorState == NORM_MACTOR)
- stand(rsd->SavedMoverInfo[i].actorID, rsd->SavedMoverInfo[i].objx,
- rsd->SavedMoverInfo[i].objy, rsd->SavedMoverInfo[i].lastfilm);
+ if (sd->SavedMoverInfo[i].bActive)
+ Stand(nullContext, sd->SavedMoverInfo[i].actorID, sd->SavedMoverInfo[i].objX,
+ sd->SavedMoverInfo[i].objY, sd->SavedMoverInfo[i].hLastfilm);
}
}
+/**
+ * Stand all the moving actors at their saved positions.
+ * Not called from the foreground.
+ */
+static void SortMAProcess(CORO_PARAM, const void *) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ int viaActor;
+ CORO_END_CONTEXT(_ctx);
+
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Disable via actor for the stands
+ _ctx->viaActor = SysVar(ISV_DIVERT_ACTOR);
+ SetSysVar(ISV_DIVERT_ACTOR, 0);
+
+ RestoreAuxScales(rsd->SavedMoverInfo);
+
+ for (_ctx->i = 0; _ctx->i < MAX_MOVERS; _ctx->i++) {
+ if (rsd->SavedMoverInfo[_ctx->i].bActive) {
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, rsd->SavedMoverInfo[_ctx->i].actorID,
+ rsd->SavedMoverInfo[_ctx->i].objX, rsd->SavedMoverInfo[_ctx->i].objY,
+ rsd->SavedMoverInfo[_ctx->i].hLastfilm));
+
+ if (rsd->SavedMoverInfo[_ctx->i].bHidden)
+ HideMover(GetMover(rsd->SavedMoverInfo[_ctx->i].actorID));
+ }
+
+ ActorPalette(rsd->SavedMoverInfo[_ctx->i].actorID,
+ rsd->SavedMoverInfo[_ctx->i].startColour, rsd->SavedMoverInfo[_ctx->i].paletteLength);
+
+ if (rsd->SavedMoverInfo[_ctx->i].brightness != BOGUS_BRIGHTNESS)
+ ActorBrightness(rsd->SavedMoverInfo[_ctx->i].actorID, rsd->SavedMoverInfo[_ctx->i].brightness);
+ }
+
+ // Restore via actor
+ SetSysVar(ISV_DIVERT_ACTOR, _ctx->viaActor);
+
+ bNotDoneYet = false;
+
+ CORO_END_CODE;
+}
+
//---------------------------------------------------------------------------
-void ResumeInterprets(SAVED_DATA *rsd) {
+void ResumeInterprets(void) {
// Master script only affected on restore game, not restore scene
- if (rsd == &sgData) {
+ if (!TinselV2 && (rsd == &sgData)) {
g_scheduler->killMatchingProcess(PID_MASTER_SCR, -1);
FreeMasterInterpretContext();
}
- for (int i = 0; i < MAX_INTERPRET; i++) {
+ for (int i = 0; i < NUM_INTERPRET; i++) {
switch (rsd->SavedICInfo[i].GSort) {
case GS_NONE:
break;
@@ -203,14 +268,31 @@ void ResumeInterprets(SAVED_DATA *rsd) {
RestoreMasterProcess(&rsd->SavedICInfo[i]);
break;
+ case GS_PROCESS:
+ // Tinsel 2 process
+ RestoreSceneProcess(&rsd->SavedICInfo[i]);
+ break;
+
+ case GS_GPROCESS:
+ // Tinsel 2 Global processes only affected on restore game, not restore scene
+ if (rsd == &sgData)
+ RestoreGlobalProcess(&rsd->SavedICInfo[i]);
+ break;
+
case GS_ACTOR:
- RestoreActorProcess(rsd->SavedICInfo[i].actorid, &rsd->SavedICInfo[i]);
+ if (TinselV2)
+ RestoreProcess(&rsd->SavedICInfo[i]);
+ else
+ RestoreActorProcess(rsd->SavedICInfo[i].idActor, &rsd->SavedICInfo[i]);
break;
case GS_POLYGON:
case GS_SCENE:
RestoreProcess(&rsd->SavedICInfo[i]);
break;
+
+ default:
+ warning("Unhandled GSort in ResumeInterprets");
}
}
}
@@ -219,7 +301,7 @@ void ResumeInterprets(SAVED_DATA *rsd) {
* Do restore scene
* @param n Id
*/
-static int DoRestoreScene(SAVED_DATA *rsd, int n) {
+static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
switch (n) {
case RS_COUNT + COUNTOUT_COUNT:
// Trigger pre-load and fade and start countdown
@@ -229,31 +311,90 @@ static int DoRestoreScene(SAVED_DATA *rsd, int n) {
case RS_COUNT:
_vm->_sound->stopAllSamples();
ClearScreen();
- RestoreDeadPolys(rsd->SavedDeadPolys);
- NewScene(rsd->SavedSceneHandle, NO_ENTRY_NUM);
+
+ // Master script only affected on restore game, not restore scene
+ if (TinselV2 && (sd == &sgData)) {
+ g_scheduler->killMatchingProcess(PID_MASTER_SCR);
+ KillGlobalProcesses();
+ FreeMasterInterpretContext();
+ }
+
+ if (TinselV2) {
+ RestorePolygonStuff(sd->SavedPolygonStuff);
+
+ // Abandon temporarily if different CD
+ if (sd == &sgData && restoreCD != GetCurrentCD()) {
+ SRstate = SR_IDLE;
+
+ EndScene();
+ SetNextCD(restoreCD);
+ CDChangeForRestore(restoreCD);
+
+ return 0;
+ }
+ } else {
+ RestoreDeadPolys(sd->SavedDeadPolys);
+ }
+
+ // Start up the scene
+ StartNewScene(sd->SavedSceneHandle, NO_ENTRY_NUM);
+
SetDoFadeIn(!bNoFade);
bNoFade = false;
- startupBackground(rsd->SavedBgroundHandle);
- KillScroll();
- PlayfieldSetPos(FIELD_WORLD, rsd->SavedLoffset, rsd->SavedToffset);
- bNoBlocking = rsd->SavedNoBlocking;
- RestoreNoScrollData(&rsd->SavedNoScrollData);
-/*
- break;
+ StartupBackground(nullContext, sd->SavedBgroundHandle);
+
+ if (TinselV2) {
+ Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
+ } else {
+ KillScroll();
+ PlayfieldSetPos(FIELD_WORLD, sd->SavedLoffset, sd->SavedToffset);
+ SetNoBlocking(sd->SavedNoBlocking);
+ }
+
+ RestoreNoScrollData(&sd->SavedNoScrollData);
+
+ if (TinselV2) {
+ // create process to sort out the moving actors
+ g_scheduler->createProcess(PID_MOVER, SortMAProcess, NULL, 0);
+ bNotDoneYet = true;
+
+ RestoreActorZ(sd->savedActorZ);
+ RestoreZpositions(sd->zPositions);
+ RestoreSysVars(sd->SavedSystemVars);
+ CreateGhostPalette(BgPal());
+ RestoreActors(sd->NumSavedActors, sd->SavedActorInfo);
+ RestoreSoundReels(sd->SavedSoundReels);
+ return 1;
+ }
- case RS_COUNT - 1:
-*/
- sortActors(rsd);
+ sortActors(sd);
break;
case 2:
break;
case 1:
- RestoreMidiFacts(rsd->SavedMidi, rsd->SavedLoop);
- if (rsd->SavedControl)
- control(CONTROL_ON); // TOKEN_CONTROL was free
- ResumeInterprets(rsd);
+ if (TinselV2) {
+ if (bNotDoneYet)
+ return n;
+
+ if (sd == &sgData)
+ HoldItem(thingHeld, true);
+ if (sd->bTinselDim)
+ _vm->_pcmMusic->dim(true);
+ _vm->_pcmMusic->restoreThatTune(sd->SavedTune);
+ ScrollFocus(sd->SavedScrollFocus);
+ } else {
+ RestoreMidiFacts(sd->SavedMidi, sd->SavedLoop);
+ }
+
+ if (sd->SavedControl)
+ ControlOn(); // Control was on
+ ResumeInterprets();
+ break;
+
+ default:
+ break;
}
return n - 1;
@@ -266,7 +407,7 @@ static int DoRestoreScene(SAVED_DATA *rsd, int n) {
void RestoreGame(int num) {
KillInventory();
- RequestRestoreGame(num, &sgData, &savedSceneCount, s_ssData);
+ RequestRestoreGame(num, &sgData, &savedSceneCount, ssData);
// Actual restoring is performed by ProcessSRQueue
}
@@ -278,9 +419,9 @@ void RestoreGame(int num) {
*/
void SaveGame(char *name, char *desc) {
// Get current scene data
- SaveScene(&sgData);
+ DoSaveScene(&sgData);
- RequestSaveGame(name, desc, &sgData, &savedSceneCount, s_ssData);
+ RequestSaveGame(name, desc, &sgData, &savedSceneCount, ssData);
// Actual saving is performed by ProcessSRQueue
}
@@ -289,23 +430,23 @@ void SaveGame(char *name, char *desc) {
//---------------------------------------------------------------------------------
bool IsRestoringScene() {
- if (s_restoreSceneCount) {
- s_restoreSceneCount = DoRestoreScene(s_rsd, s_restoreSceneCount);
+ if (RestoreSceneCount) {
+ RestoreSceneCount = DoRestoreSceneFrame(rsd, RestoreSceneCount);
}
- return s_restoreSceneCount ? true : false;
+ return RestoreSceneCount ? true : false;
}
/**
- * Please Restore Scene
+ * Restores Scene
*/
-void PleaseRestoreScene(bool bFade) {
+void TinselRestoreScene(bool bFade) {
// only called by restore_scene PCODE
- if (s_restoreSceneCount == 0) {
+ if (RestoreSceneCount == 0) {
assert(savedSceneCount >= 1); // No saved scene to restore
if (ASceneIsSaved)
- RestoreScene(&s_ssData[--savedSceneCount], bFade);
+ DoRestoreScene(&ssData[--savedSceneCount], bFade);
if (!bFade)
bNoFade = true;
}
@@ -314,7 +455,7 @@ void PleaseRestoreScene(bool bFade) {
/**
* Please Save Scene
*/
-void PleaseSaveScene(CORO_PARAM) {
+void TinselSaveScene(CORO_PARAM) {
// only called by save_scene PCODE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
@@ -325,10 +466,10 @@ void PleaseSaveScene(CORO_PARAM) {
// Don't save the same thing multiple times!
// FIXME/TODO: Maybe this can be changed to an assert?
- if (savedSceneCount && s_ssData[savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
+ if (savedSceneCount && ssData[savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
CORO_KILL_SELF();
- SaveScene(&s_ssData[savedSceneCount++]);
+ DoSaveScene(&ssData[savedSceneCount++]);
CORO_END_CODE;
}
diff --git a/engines/tinsel/savescn.h b/engines/tinsel/savescn.h
index a999c9bffa..79bf0ca2b2 100644
--- a/engines/tinsel/savescn.h
+++ b/engines/tinsel/savescn.h
@@ -33,13 +33,16 @@
#include "tinsel/dw.h" // SCNHANDLE
#include "tinsel/rince.h" // SAVED_MOVER
#include "tinsel/pcode.h" // INT_CONTEXT
+#include "tinsel/play.h"
+#include "tinsel/polygons.h"
#include "tinsel/scroll.h" // SCROLLDATA
+#include "tinsel/sysvar.h"
namespace Tinsel {
enum {
SG_DESC_LEN = 40, // Max. saved game description length
- MAX_SFILES = 30,
+ MAX_SAVED_FILES = 30,
// FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the
// name field in savedFiles. Raising it to 256 as a preliminary fix.
@@ -66,6 +69,16 @@ struct SAVED_DATA {
bool SavedLoop; // } Midi
bool SavedNoBlocking;
SCROLLDATA SavedNoScrollData;
+
+ // Tinsel 2 fields
+ Z_POSITIONS zPositions[NUM_ZPOSITIONS];
+ byte savedActorZ[MAX_SAVED_ACTOR_Z];
+ POLY_VOLATILE SavedPolygonStuff[MAX_POLY];
+ uint32 SavedTune[3]; // Music
+ bool bTinselDim;
+ int SavedScrollFocus;
+ int SavedSystemVars[SV_TOPVALID];
+ SOUNDREELS SavedSoundReels[MAX_SOUNDREELS];
};
@@ -74,8 +87,10 @@ enum SRSTATE {
SR_DOSAVE, SR_DONESAVE, SR_ABORTED
};
-void PleaseRestoreScene(bool bFade);
-void PleaseSaveScene(CORO_PARAM);
+void TinselRestoreScene(bool bFade);
+void TinselSaveScene(CORO_PARAM);
+void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut);
+void DoSaveScene(SAVED_DATA *sd);
bool IsRestoringScene();
@@ -95,8 +110,8 @@ void ProcessSRQueue(void);
void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData);
void RequestRestoreGame(int num, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData);
-void InitialiseSs(void);
-void FreeSs(void);
+void InitialiseSaveScenes(void);
+void FreeSaveScenes(void);
} // end of namespace Tinsel
diff --git a/engines/tinsel/scene.cpp b/engines/tinsel/scene.cpp
index 70700c16a3..8826d97099 100644
--- a/engines/tinsel/scene.cpp
+++ b/engines/tinsel/scene.cpp
@@ -29,21 +29,26 @@
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
+#include "tinsel/dialogs.h"
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
-#include "tinsel/inventory.h"
#include "tinsel/film.h"
+#include "tinsel/font.h"
+#include "tinsel/mareels.h"
#include "tinsel/move.h"
+#include "tinsel/music.h"
+#include "tinsel/object.h"
+#include "tinsel/pcode.h"
+#include "tinsel/pid.h" // process IDs
+#include "tinsel/play.h"
+#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/sched.h"
#include "tinsel/scn.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h" // stopAllSamples()
-#include "tinsel/object.h"
-#include "tinsel/pcode.h"
-#include "tinsel/pid.h" // process IDs
-#include "tinsel/polygons.h"
+#include "tinsel/sysvar.h"
#include "tinsel/token.h"
@@ -72,20 +77,28 @@ extern void EnableTags(void);
/** scene structure - one per scene */
struct SCENE_STRUC {
- int32 numEntrance; //!< number of entrances in this scene
- int32 numPoly; //!< number of various polygons in this scene
- int32 numActor; //!< number of actors in this scene
- int32 defRefer; //!< Default refer direction
- SCNHANDLE hSceneScript; //!< handle to scene script
- SCNHANDLE hEntrance; //!< handle to table of entrances
- SCNHANDLE hPoly; //!< handle to table of polygons
- SCNHANDLE hActor; //!< handle to table of actors
+ int32 defRefer; // Default refer direction (REFTYPE)
+ SCNHANDLE hSceneScript; // handle to scene script
+ SCNHANDLE hSceneDesc; // handle to scene description
+ int32 numEntrance; // number of entrances in this scene
+ SCNHANDLE hEntrance; // handle to table of entrances
+ int32 numPoly; // number of various polygons in this scene
+ SCNHANDLE hPoly; // handle to table of polygons
+ int32 numTaggedActor; // number of tagged actors in this scene
+ SCNHANDLE hTaggedActor; // handle to table of tagged actors
+ int32 numProcess; // number of processes in this scene
+ SCNHANDLE hProcess; // handle to table of processes
+ SCNHANDLE hMusicScript; // handle to music script data
+ SCNHANDLE hMusicSegment;// handle to music segments
} PACKED_STRUCT;
/** entrance structure - one per entrance */
struct ENTRANCE_STRUC {
int32 eNumber; //!< entrance number
SCNHANDLE hScript; //!< handle to entrance script
+ // Tinsel 2 fields
+ SCNHANDLE hEntDesc; // handle to entrance description
+ uint32 flags;
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
@@ -97,8 +110,39 @@ struct ENTRANCE_STRUC {
static bool ShowPosition = false; // Set when showpos() has been called
#endif
+SCNHANDLE newestScene = 0;
+
static SCNHANDLE SceneHandle = 0; // Current scene handle - stored in case of Save_Scene()
+static bool bWatchingOut = false;
+
+SCENE_STRUC tempStruc;
+
+struct TP_INIT {
+ SCNHANDLE hTinselCode; // Code
+ TINSEL_EVENT event; // Triggering event
+};
+
+const SCENE_STRUC *GetSceneStruc(const byte *pStruc) {
+ if (TinselVersion == TINSEL_V2)
+ return (const SCENE_STRUC *)pStruc;
+
+ // Copy appropriate fields into tempStruc, and return a pointer to it
+ const uint32 *p = (const uint32 *)pStruc;
+ memset(&tempStruc, sizeof(SCENE_STRUC), 0);
+
+ tempStruc.numEntrance = *p++;
+ tempStruc.numPoly = *p++;
+ tempStruc.numTaggedActor = *p++;
+ tempStruc.defRefer = *p++;
+ tempStruc.hSceneScript = *p++;
+ tempStruc.hEntrance = *p++;
+ tempStruc.hPoly = *p++;
+ tempStruc.hTaggedActor = *p++;
+
+ return &tempStruc;
+}
+
/**
* Started up for scene script and entrance script.
@@ -107,68 +151,163 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
+ const TP_INIT *pInit;
CORO_END_CONTEXT(_ctx);
- // get the stuff copied to process when it was created
- SCNHANDLE *ss = (SCNHANDLE *)param;
- assert(*ss); // Must have some code to run
-
CORO_BEGIN_CODE(_ctx);
- _ctx->pic = InitInterpretContext(GS_SCENE, READ_LE_UINT32(ss), NOEVENT, NOPOLY, 0, NULL);
+ // get the stuff copied to process when it was created
+ _ctx->pInit = (const TP_INIT *)param;
+ assert(_ctx->pInit);
+ assert(_ctx->pInit->hTinselCode); // Must have some code to run
+
+ _ctx->pic = InitInterpretContext(GS_SCENE,
+ READ_LE_UINT32(&_ctx->pInit->hTinselCode),
+ TinselV2 ? _ctx->pInit->event : NOEVENT,
+ NOPOLY, // No polygon
+ 0, // No actor
+ NULL, // No object
+ 0);
CORO_INVOKE_1(Interpret, _ctx->pic);
+ if (_ctx->pInit->event == CLOSEDOWN || _ctx->pInit->event == LEAVE_T2)
+ bWatchingOut = false;
+
CORO_END_CODE;
}
/**
+ * Start up a SceneTinselProcess() running the scene
+ * script for various events.
+ */
+void SendSceneTinselProcess(TINSEL_EVENT event) {
+ SCENE_STRUC *ss;
+
+ if (event == CLOSEDOWN || event == LEAVE_T2)
+ bWatchingOut = true;
+
+ if (SceneHandle != (SCNHANDLE)NULL) {
+ ss = (SCENE_STRUC *) FindChunk(SceneHandle, CHUNK_SCENE);
+
+ if (ss->hSceneScript) {
+ TP_INIT init;
+
+ init.event = event;
+ init.hTinselCode = ss->hSceneScript;
+
+ g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
+ } else if (event == CLOSEDOWN)
+ bWatchingOut = false;
+ }
+ else if (event == CLOSEDOWN)
+ bWatchingOut = false;
+}
+
+
+/**
* Get the SCENE_STRUC
* Initialise polygons for the scene
* Initialise the actors for this scene
* Run the appropriate entrance code (if any)
* Get the default refer type
*/
+
static void LoadScene(SCNHANDLE scene, int entry) {
+ uint i;
+ TP_INIT init;
const SCENE_STRUC *ss;
const ENTRANCE_STRUC *es;
- uint i;
-
- // Scene structure
- SceneHandle = scene; // Save scene handle in case of Save_Scene()
+ // Scene handle
+ SceneHandle = scene; // Save scene handle in case of Save_Scene()
LockMem(SceneHandle); // Make sure scene is loaded
LockScene(SceneHandle); // Prevent current scene from being discarded
- ss = (const SCENE_STRUC *)FindChunk(scene, CHUNK_SCENE);
+ if (TinselV2) {
+ // CdPlay() stuff
+ byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM);
+ assert(cptr);
+ i = READ_LE_UINT32(cptr);
+ assert(i < 512);
+ cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME);
+ assert(cptr);
+ SetCdPlaySceneDetails(i, (const char *)cptr);
+ }
+
+ // Find scene structure
+ ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
assert(ss != NULL);
- // Initialise all the polygons for this scene
- InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), (entry == NO_ENTRY_NUM));
+ if (TinselV2) {
+ // Handle to scene description
+ newestScene = FROM_LE_32(ss->hSceneDesc);
+
+ // Music stuff
+ char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME);
+ assert(cptr);
+ _vm->_pcmMusic->setMusicSceneDetails(FROM_LE_32(ss->hMusicScript),
+ FROM_LE_32(ss->hMusicSegment), cptr);
+ }
+
+ if (entry == NO_ENTRY_NUM) {
+ // Restoring scene
+
+ // Initialise all the polygons for this scene
+ InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), true);
+
+ // Initialise the actors for this scene
+ StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), false);
+
+ if (TinselV2)
+ // Returning from cutscene
+ SendSceneTinselProcess(RESTORE);
- // Initialise the actors for this scene
- StartActors(FROM_LE_32(ss->hActor), FROM_LE_32(ss->numActor), (entry != NO_ENTRY_NUM));
+ } else {
+ // Genuine new scene
- if (entry != NO_ENTRY_NUM) {
+ // Initialise all the polygons for this scene
+ InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), false);
+
+ // Initialise the actors for this scene
+ StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), true);
// Run the appropriate entrance code (if any)
es = (const ENTRANCE_STRUC *)LockMem(FROM_LE_32(ss->hEntrance));
- for (i = 0; i < FROM_LE_32(ss->numEntrance); i++, es++) {
+ for (i = 0; i < FROM_LE_32(ss->numEntrance); i++) {
if (FROM_LE_32(es->eNumber) == (uint)entry) {
- if (es->hScript)
- g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &es->hScript, sizeof(es->hScript));
+ if (es->hScript) {
+ init.event = STARTUP;
+ init.hTinselCode = es->hScript;
+
+ g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
+ }
break;
}
+
+ // Move to next entrance
+ if (TinselV2)
+ ++es;
+ else
+ es = (const ENTRANCE_STRUC *)((const byte *)es + 8);
+
}
if (i == FROM_LE_32(ss->numEntrance))
error("Non-existant scene entry number");
- if (ss->hSceneScript)
- g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &ss->hSceneScript, sizeof(ss->hSceneScript));
+ if (ss->hSceneScript) {
+ init.event = STARTUP;
+ init.hTinselCode = ss->hSceneScript;
+
+ g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
+ }
}
// Default refer type
SetDefaultRefer(FROM_LE_32(ss->defRefer));
+
+ // Scene's processes
+ SceneProcesses(FROM_LE_32(ss->numProcess), FROM_LE_32(ss->hProcess));
}
@@ -184,15 +323,27 @@ void EndScene(void) {
KillInventory(); // Close down any open inventory
DropPolygons(); // No polygons
- DropNoScrolls(); // No no-scrolls
+ DropScroll(); // No no-scrolls
DropBackground(); // No background
- DropMActors(); // No moving actors
+ DropMovers(); // No moving actors
DropCursor(); // No cursor
DropActors(); // No actor reels running
FreeAllTokens(); // No-one has tokens
FreeMostInterpretContexts(); // Only master script still interpreting
+ if (TinselV2) {
+ SetSysVar(ISV_DIVERT_ACTOR, 0);
+ SetSysVar(ISV_GHOST_ACTOR, 0);
+ SetSysVar(SV_MinimumXoffset, 0);
+ SetSysVar(SV_MaximumXoffset, 0);
+ SetSysVar(SV_MinimumYoffset, 0);
+ SetSysVar(SV_MaximumYoffset, 0);
+ ResetFontHandles();
+ NoSoundReels();
+ }
+
_vm->_sound->stopAllSamples(); // Kill off any still-running sample
+ _vm->_mixer->stopAll();
// init the palette manager
ResetPalAllocator();
@@ -251,11 +402,12 @@ void PrimeBackground(void) {
*/
void PrimeScene(void) {
-
- bNoBlocking = false;
+ SetNoBlocking(false);
+ SetSysVar(SYS_SceneFxDimFactor, SysVar(SYS_DefaultFxDimFactor));
RestartCursor(); // Restart the cursor
- EnableTags(); // Next scene with tags enabled
+ if (!TinselV2)
+ EnableTags(); // Next scene with tags enabled
g_scheduler->createProcess(PID_SCROLL, ScrollProcess, NULL, 0);
g_scheduler->createProcess(PID_SCROLL, EffectPolyProcess, NULL, 0);
@@ -276,9 +428,15 @@ void PrimeScene(void) {
* Wrap up the last scene and start up the next scene.
*/
-void NewScene(SCNHANDLE scene, int entry) {
+void StartNewScene(SCNHANDLE scene, int entry) {
EndScene(); // Wrap up the last scene.
+ if (TinselV2) {
+ TouchMoverReels();
+
+ LockMem(scene); // Do CD change before PrimeScene
+ }
+
PrimeScene(); // Start up the standard stuff for the next scene.
LoadScene(scene, entry);
@@ -303,4 +461,29 @@ SCNHANDLE GetSceneHandle(void) {
return SceneHandle;
}
+/**
+ * DoHailScene
+ */
+
+void DoHailScene(SCNHANDLE scene) {
+ // Find scene structure
+ const SCENE_STRUC *ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
+
+ if (ss != NULL && ss->hSceneScript) {
+ TP_INIT init;
+
+ init.event = NOEVENT;
+ init.hTinselCode = ss->hSceneScript;
+
+ g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
+ }
+}
+
+/**
+ * WrapScene
+ */
+void WrapScene(void) {
+ SendSceneTinselProcess(CLOSEDOWN);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h
index d0fc6e1ae3..e47f0b864c 100644
--- a/engines/tinsel/scene.h
+++ b/engines/tinsel/scene.h
@@ -28,6 +28,7 @@
#define TINSEL_SCENE_H
#include "tinsel/dw.h"
+#include "tinsel/events.h"
namespace Tinsel {
@@ -39,13 +40,19 @@ enum {
MAX_ACTOR = 32 //!< maximum number of actors in a scene
};
+// ENTRANCE_STRUC bitflags
+enum ENTRANCE_FLAGS {
+ fCall = 0x00000001L,
+ fHook = 0x00000002L
+};
+
/** reference direction */
enum REFTYPE {
REF_DEFAULT, REF_UP, REF_DOWN, REF_LEFT, REF_RIGHT, REF_POINT
};
enum TFTYPE {
- TF_NONE, TF_UP, TF_DOWN, TF_LEFT, TF_RIGHT, TF_BOGUS
+ TF_NONE, TF_UP, TF_DOWN, TF_LEFT, TF_RIGHT, TF_FILM
};
/** different actor masks */
@@ -68,6 +75,23 @@ enum REEL {
REEL_DEFAULT, REEL_ALL, REEL_HORIZ, REEL_VERT
};
+typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS;
+
+// amount to shift scene handles by
+#define SCNHANDLE_SHIFT (TinselV2 ? 25 : 23)
+#define OFFSETMASK (TinselV2 ? 0x01ffffffL : 0x007fffffL)
+#define HANDLEMASK (TinselV2 ? 0xFE000000L : 0xFF800000L)
+
+void DoHailScene(SCNHANDLE scene);
+
+void WrapScene(void);
+
+void StartNewScene(SCNHANDLE scene, int entry);
+
+void EndScene(void);
+
+void SendSceneTinselProcess(TINSEL_EVENT event);
+
} // end of namespace Tinsel
#endif // TINSEL_SCENE_H
diff --git a/engines/tinsel/sched.cpp b/engines/tinsel/sched.cpp
index 72cfeaf6b0..526ad3fcf6 100644
--- a/engines/tinsel/sched.cpp
+++ b/engines/tinsel/sched.cpp
@@ -24,6 +24,10 @@
* Process scheduler.
*/
+#include "tinsel/handle.h"
+#include "tinsel/pcode.h"
+#include "tinsel/pid.h"
+#include "tinsel/polygons.h"
#include "tinsel/sched.h"
#include "common/util.h"
@@ -32,18 +36,26 @@ namespace Tinsel {
Scheduler *g_scheduler = 0;
-/** process structure */
-struct PROCESS {
- PROCESS *pNext; //!< pointer to next process in active or free list
+#include "common/pack-start.h" // START STRUCT PACKING
- CoroContext state; //!< the state of the coroutine
- CORO_ADDR coroAddr; //!< the entry point of the coroutine
+struct PROCESS_STRUC {
+ uint32 processId; // ID of process
+ SCNHANDLE hProcessCode; // handle to actor script
+} PACKED_STRUCT;
- int sleepTime; //!< number of scheduler cycles to sleep
- int pid; //!< process ID
- char param[PARAM_SIZE]; //!< process specific info
-};
+#include "common/pack-end.h" // END STRUCT PACKING
+CoroContext nullContext = NULL;
+
+//----------------- LOCAL GLOBAL DATA --------------------
+
+static uint32 numSceneProcess;
+static SCNHANDLE hSceneProcess;
+
+static uint32 numGlobalProcess;
+static PROCESS_STRUC *pGlobalProcess;
+
+//--------------------- FUNCTIONS ------------------------
Scheduler::Scheduler() {
processList = 0;
@@ -59,6 +71,7 @@ Scheduler::Scheduler() {
pRCfunction = 0;
active = new PROCESS;
+ active->pPrevious = NULL;
g_scheduler = this; // FIXME HACK
}
@@ -83,7 +96,7 @@ void Scheduler::reset() {
if (processList == NULL) {
// first time - allocate memory for process list
- processList = (PROCESS *)calloc(NUM_PROCESS, sizeof(PROCESS));
+ processList = (PROCESS *)calloc(MAX_PROCESSES, sizeof(PROCESS));
// make sure memory allocated
if (processList == NULL) {
@@ -91,7 +104,7 @@ void Scheduler::reset() {
}
// fill with garbage
- memset(processList, 'S', NUM_PROCESS * sizeof(PROCESS));
+ memset(processList, 'S', MAX_PROCESSES * sizeof(PROCESS));
}
// no active processes
@@ -101,12 +114,10 @@ void Scheduler::reset() {
pFreeProcesses = processList;
// link all other processes after first
- for (int i = 1; i < NUM_PROCESS; i++) {
- processList[i - 1].pNext = processList + i;
+ for (int i = 1; i <= NUM_PROCESS; i++) {
+ processList[i - 1].pNext = (i == NUM_PROCESS) ? NULL : processList + i;
+ processList[i - 1].pPrevious = (i == 1) ? active : processList + (i - 2);
}
-
- // null the last process
- processList[NUM_PROCESS - 1].pNext = NULL;
}
@@ -119,33 +130,172 @@ void Scheduler::printStats(void) {
}
#endif
+#ifdef DEBUG
+/**
+ * Checks both the active and free process list to insure all the links are valid,
+ * and that no processes have been lost
+ */
+void Scheduler::CheckStack() {
+ Common::List<PROCESS *> pList;
+
+ // Check both the active and free process lists
+ for (int i = 0; i < 2; ++i) {
+ PROCESS *p = (i == 0) ? active : pFreeProcesses;
+
+ if (p != NULL) {
+ // Make sure the linkages are correct
+ while (p->pNext != NULL) {
+ assert(p->pNext->pPrevious == p);
+ pList.push_back(p);
+ p = p->pNext;
+ }
+ pList.push_back(p);
+ }
+ }
+
+ // Make sure all processes are accounted for
+ for (int idx = 0; idx < NUM_PROCESS; idx++) {
+ bool found = false;
+ for (Common::List<PROCESS *>::iterator i = pList.begin(); i != pList.end(); ++i) {
+ PROCESS *pTemp = *i;
+ if (*i == &processList[idx]) {
+ found = true;
+ break;
+ }
+ }
+
+ assert(found);
+ }
+}
+#endif
/**
* Give all active processes a chance to run
*/
void Scheduler::schedule(void) {
// start dispatching active process list
- PROCESS *pPrevProc = active;
+ PROCESS *pNext;
PROCESS *pProc = active->pNext;
while (pProc != NULL) {
+ pNext = pProc->pNext;
+
if (--pProc->sleepTime <= 0) {
// process is ready for dispatch, activate it
pCurrent = pProc;
pProc->coroAddr(pProc->state, pProc->param);
- pCurrent = NULL;
+
if (!pProc->state || pProc->state->_sleep <= 0) {
// Coroutine finished
+ pCurrent = pCurrent->pPrevious;
killProcess(pProc);
- pProc = pPrevProc;
} else {
pProc->sleepTime = pProc->state->_sleep;
}
+
+ // pCurrent may have been changed
+ pNext = pCurrent->pNext;
+ pCurrent = NULL;
}
- pPrevProc = pProc;
- pProc = pProc->pNext;
+
+ pProc = pNext;
}
}
+/**
+ * Reschedules all the processes to run again this query
+ */
+void Scheduler::rescheduleAll() {
+ assert(pCurrent);
+
+ // Unlink current process
+ pCurrent->pPrevious->pNext = pCurrent->pNext;
+ if (pCurrent->pNext)
+ pCurrent->pNext->pPrevious = pCurrent->pPrevious;
+
+ // Add process to the start of the active list
+ pCurrent->pNext = active->pNext;
+ active->pNext->pPrevious = pCurrent;
+ active->pNext = pCurrent;
+ pCurrent->pPrevious = active;
+}
+
+/**
+ * If the specified process has already run on this tick, make it run
+ * again on the current tick.
+ */
+void Scheduler::reschedule(PPROCESS pReSchedProc) {
+ // If not currently processing the schedule list, then no action is needed
+ if (!pCurrent)
+ return;
+
+ if (!pReSchedProc)
+ pReSchedProc = pCurrent;
+
+ PPROCESS pEnd;
+
+ // Find the last process in the list.
+ // But if the target process is down the list from here, do nothing
+ for (pEnd = pCurrent; pEnd->pNext != NULL; pEnd = pEnd->pNext) {
+ if (pEnd->pNext == pReSchedProc)
+ return;
+ }
+
+ assert(pEnd->pNext == NULL);
+
+ // Could be in the middle of a KillProc()!
+ // Dying process was last and this process was penultimate
+ if (pReSchedProc->pNext == NULL)
+ return;
+
+ // If we're moving the current process, move it back by one, so that the next
+ // schedule() iteration moves to the now next one
+ if (pCurrent == pReSchedProc)
+ pCurrent = pCurrent->pPrevious;
+
+ // Unlink the process, and add it at the end
+ pReSchedProc->pPrevious->pNext = pReSchedProc->pNext;
+ pReSchedProc->pNext->pPrevious = pReSchedProc->pPrevious;
+ pEnd->pNext = pReSchedProc;
+ pReSchedProc->pPrevious = pEnd;
+ pReSchedProc->pNext = NULL;
+}
+
+/**
+ * Moves the specified process to the end of the dispatch queue
+ * allowing it to run again within the current game cycle.
+ * @param pGiveProc Which process
+ */
+void Scheduler::giveWay(PPROCESS pReSchedProc) {
+ // If not currently processing the schedule list, then no action is needed
+ if (!pCurrent)
+ return;
+
+ if (!pReSchedProc)
+ pReSchedProc = pCurrent;
+
+ // If the process is already at the end of the queue, nothing has to be done
+ if (!pReSchedProc->pNext)
+ return;
+
+ PPROCESS pEnd;
+
+ // Find the last process in the list.
+ for (pEnd = pCurrent; pEnd->pNext != NULL; pEnd = pEnd->pNext) ;
+ assert(pEnd->pNext == NULL);
+
+
+ // If we're moving the current process, move it back by one, so that the next
+ // schedule() iteration moves to the now next one
+ if (pCurrent == pReSchedProc)
+ pCurrent = pCurrent->pPrevious;
+
+ // Unlink the process, and add it at the end
+ pReSchedProc->pPrevious->pNext = pReSchedProc->pNext;
+ pReSchedProc->pNext->pPrevious = pReSchedProc->pPrevious;
+ pEnd->pNext = pReSchedProc;
+ pReSchedProc->pPrevious = pEnd;
+ pReSchedProc->pNext = NULL;
+}
/**
* Creates a new process.
@@ -172,16 +322,27 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
// get link to next free process
pFreeProcesses = pProc->pNext;
+ if (pFreeProcesses)
+ pFreeProcesses->pPrevious = NULL;
if (pCurrent != NULL) {
// place new process before the next active process
pProc->pNext = pCurrent->pNext;
+ if (pProc->pNext)
+ pProc->pNext->pPrevious = pProc;
// make this new process the next active process
pCurrent->pNext = pProc;
+ pProc->pPrevious = pCurrent;
+
} else { // no active processes, place process at head of list
pProc->pNext = active->pNext;
+ pProc->pPrevious = active;
+
+ if (pProc->pNext)
+ pProc->pNext->pPrevious = pProc;
active->pNext = pProc;
+
}
// set coroutine entry point
@@ -204,7 +365,6 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
memcpy(pProc->param, pParam, sizeParam);
}
-
// return created process
return pProc;
}
@@ -215,8 +375,6 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
* @param pKillProc which process to kill
*/
void Scheduler::killProcess(PROCESS *pKillProc) {
- PROCESS *pProc, *pPrev; // process list pointers
-
// make sure a valid process pointer
assert(pKillProc >= processList && pKillProc <= processList + NUM_PROCESS - 1);
@@ -229,32 +387,25 @@ void Scheduler::killProcess(PROCESS *pKillProc) {
assert(numProcs >= 0);
#endif
- // search the active list for the process
- for (pProc = active->pNext, pPrev = active; pProc != NULL; pPrev = pProc, pProc = pProc->pNext) {
- if (pProc == pKillProc) {
- // found process in active list
-
- // Free process' resources
- if (pRCfunction != NULL)
- (pRCfunction)(pProc);
-
- delete pProc->state;
+ // Free process' resources
+ if (pRCfunction != NULL)
+ (pRCfunction)(pKillProc);
- // make prev point to next to unlink pProc
- pPrev->pNext = pProc->pNext;
+ delete pKillProc->state;
- // link first free process after pProc
- pProc->pNext = pFreeProcesses;
+ // Take the process out of the active chain list
+ pKillProc->pPrevious->pNext = pKillProc->pNext;
+ if (pKillProc->pNext)
+ pKillProc->pNext->pPrevious = pKillProc->pPrevious;
- // make pProc the first free process
- pFreeProcesses = pProc;
-
- return;
- }
- }
+ // link first free process after pProc
+ pKillProc->pNext = pFreeProcesses;
+ if (pFreeProcesses)
+ pKillProc->pNext->pPrevious = pKillProc;
+ pKillProc->pPrevious = NULL;
- // process not found in active list if we get to here
- error("killProcess(): tried to kill a process not in the list of active processes");
+ // make pKillProc the first free process
+ pFreeProcesses = pKillProc;
}
@@ -302,11 +453,21 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
// kill this process
numKilled++;
+ // Free the process' resources
+ if (pRCfunction != NULL)
+ (pRCfunction)(pProc);
+
+ delete pProc->state;
+
// make prev point to next to unlink pProc
pPrev->pNext = pProc->pNext;
+ if (pProc->pNext)
+ pPrev->pNext->pPrevious = pPrev;
// link first free process after pProc
pProc->pNext = pFreeProcesses;
+ pProc->pPrevious = NULL;
+ pFreeProcesses->pPrevious = pProc;
// make pProc the first free process
pFreeProcesses = pProc;
@@ -327,8 +488,6 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
return numKilled;
}
-
-
/**
* Set pointer to a function to be called by killProcess().
*
@@ -342,4 +501,262 @@ void Scheduler::setResourceCallback(VFPTRPP pFunc) {
pRCfunction = pFunc;
}
+/**************************************************************************\
+|*********** Stuff to do with scene and global processes ************|
+\**************************************************************************/
+
+/**
+ * The code for for restored scene processes.
+ */
+static void RestoredProcessProcess(CORO_PARAM, const void *) {
+ CORO_BEGIN_CONTEXT;
+ INT_CONTEXT *pic;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ PROCESS *pProc; // this process pointer
+
+ // get the stuff copied to process when it was created
+ pProc = g_scheduler->getCurrentProcess();
+ _ctx->pic = *((INT_CONTEXT **) pProc->param);
+
+ _ctx->pic = RestoreInterpretContext(_ctx->pic);
+ AttachInterpret(_ctx->pic, pProc);
+
+ CORO_INVOKE_1(Interpret, _ctx->pic);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Process Tinsel Process
+ */
+static void ProcessTinselProcess(CORO_PARAM, const void *) {
+ PPROCESS pProc = g_scheduler->getCurrentProcess();
+ PINT_CONTEXT *pPic = (PINT_CONTEXT *) pProc->param;
+
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // get the stuff copied to process when it was created
+ CORO_INVOKE_1(Interpret, *pPic);
+
+ CORO_KILL_SELF();
+ CORO_END_CODE;
+}
+
+
+/**************************************************************************\
+|***************** Stuff to do with scene processes *****************|
+\**************************************************************************/
+
+/**
+ * Called to restore a scene process.
+ */
+void RestoreSceneProcess(INT_CONTEXT *pic) {
+ uint32 i;
+ PROCESS_STRUC *pStruc;
+
+ pStruc = (PROCESS_STRUC *)LockMem(hSceneProcess);
+ for (i = 0; i < numSceneProcess; i++) {
+ if (pStruc[i].hProcessCode == pic->hCode) {
+ g_scheduler->createProcess(PID_PROCESS + i, RestoredProcessProcess,
+ &pic, sizeof(pic));
+ break;
+ }
+ }
+
+ assert(i < numSceneProcess);
+}
+
+/**
+ * Run a scene process with the given event.
+ */
+void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape,
+ bool *result) {
+ uint32 i; // Loop counter
+ if (result) *result = false;
+
+ CORO_BEGIN_CONTEXT;
+ PROCESS_STRUC *pStruc;
+ PPROCESS pProc;
+ PINT_CONTEXT pic;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->pStruc = (PROCESS_STRUC *)LockMem(hSceneProcess);
+ for (i = 0; i < numSceneProcess; i++) {
+ if (_ctx->pStruc[i].processId == procID) {
+ assert(_ctx->pStruc[i].hProcessCode); // Must have some code to run
+
+ _ctx->pic = InitInterpretContext(GS_PROCESS,
+ _ctx->pStruc[i].hProcessCode,
+ event,
+ NOPOLY, // No polygon
+ 0, // No actor
+ NULL, // No object
+ myEscape);
+ if (_ctx->pic == NULL)
+ return;
+
+ _ctx->pProc = g_scheduler->createProcess(PID_PROCESS + i, ProcessTinselProcess,
+ &_ctx->pic, sizeof(_ctx->pic));
+ AttachInterpret(_ctx->pic, _ctx->pProc);
+ break;
+ }
+ }
+
+ if (i == numSceneProcess)
+ return;
+
+ if (bWait) {
+ CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Kill all instances of a scene process.
+ */
+void KillSceneProcess(uint32 procID) {
+ uint32 i; // Loop counter
+ PROCESS_STRUC *pStruc;
+
+ pStruc = (PROCESS_STRUC *) LockMem(hSceneProcess);
+ for (i = 0; i < numSceneProcess; i++) {
+ if (pStruc[i].processId == procID) {
+ g_scheduler->killMatchingProcess(PID_PROCESS + i, -1);
+ break;
+ }
+ }
+}
+
+/**
+ * Register the scene processes in a scene.
+ */
+void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess) {
+ numSceneProcess = numProcess;
+ hSceneProcess = hProcess;
+}
+
+
+/**************************************************************************\
+|***************** Stuff to do with global processes ****************|
+\**************************************************************************/
+
+/**
+ * Called to restore a global process.
+ */
+void RestoreGlobalProcess(INT_CONTEXT *pic) {
+ uint32 i; // Loop counter
+
+ for (i = 0; i < numGlobalProcess; i++) {
+ if (pGlobalProcess[i].hProcessCode == pic->hCode) {
+ g_scheduler->createProcess(PID_GPROCESS + i, RestoredProcessProcess,
+ &pic, sizeof(pic));
+ break;
+ }
+ }
+
+ assert(i < numGlobalProcess);
+}
+
+/**
+ * Kill them all (restore game).
+ */
+void KillGlobalProcesses(void) {
+
+ for (uint32 i = 0; i < numGlobalProcess; ++i) {
+ g_scheduler->killMatchingProcess(PID_GPROCESS + i, -1);
+ }
+}
+
+/**
+ * Run a global process with the given event.
+ */
+bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape) {
+ CORO_BEGIN_CONTEXT;
+ PINT_CONTEXT pic;
+ PPROCESS pProc;
+ CORO_END_CONTEXT(_ctx);
+
+ bool result = false;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ uint32 i; // Loop counter
+ _ctx->pProc = NULL;
+
+ for (i = 0; i < numGlobalProcess; ++i) {
+ if (pGlobalProcess[i].processId == procID) {
+ assert(pGlobalProcess[i].hProcessCode); // Must have some code to run
+
+ _ctx->pic = InitInterpretContext(GS_GPROCESS,
+ pGlobalProcess[i].hProcessCode,
+ event,
+ NOPOLY, // No polygon
+ 0, // No actor
+ NULL, // No object
+ myEscape);
+
+ if (_ctx->pic != NULL) {
+
+ _ctx->pProc = g_scheduler->createProcess(PID_GPROCESS + i, ProcessTinselProcess,
+ &_ctx->pic, sizeof(_ctx->pic));
+ AttachInterpret(_ctx->pic, _ctx->pProc);
+ }
+ break;
+ }
+ }
+
+ if ((i == numGlobalProcess) || (_ctx->pic == NULL))
+ result = false;
+ else if (bWait)
+ CORO_INVOKE_ARGS_V(WaitInterpret, false, (CORO_SUBCTX, _ctx->pProc, &result));
+
+ CORO_END_CODE;
+ return result;
+}
+
+/**
+ * Kill all instances of a global process.
+ */
+void xKillGlobalProcess(uint32 procID) {
+ uint32 i; // Loop counter
+
+ for (i = 0; i < numGlobalProcess; ++i) {
+ if (pGlobalProcess[i].processId == procID) {
+ g_scheduler->killMatchingProcess(PID_GPROCESS + i, -1);
+ break;
+ }
+ }
+}
+
+/**
+ * Register the global processes list
+ */
+void GlobalProcesses(uint32 numProcess, byte *pProcess) {
+ pGlobalProcess = new PROCESS_STRUC[numProcess];
+ numGlobalProcess = numProcess;
+ byte *p = pProcess;
+
+ for (uint i = 0; i < numProcess; ++i, p += 8) {
+ pGlobalProcess[i].processId = READ_LE_UINT32(p);
+ pGlobalProcess[i].hProcessCode = READ_LE_UINT32(p + 4);
+ }
+}
+
+/**
+ * Frees the global processes list
+ */
+void FreeGlobalProcesses() {
+ delete[] pGlobalProcess;
+ numGlobalProcess = 0;
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/sched.h b/engines/tinsel/sched.h
index 0d90b3bb9f..1e2d3f796e 100644
--- a/engines/tinsel/sched.h
+++ b/engines/tinsel/sched.h
@@ -29,6 +29,8 @@
#include "tinsel/dw.h" // new data types
#include "tinsel/coroutine.h"
+#include "tinsel/events.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
@@ -36,12 +38,26 @@ namespace Tinsel {
#define PARAM_SIZE 32
// the maximum number of processes
-#define NUM_PROCESS 64
+#define NUM_PROCESS (TinselV2 ? 70 : 64)
+#define MAX_PROCESSES 70
typedef void (*CORO_ADDR)(CoroContext &, const void *);
+/** process structure */
+struct PROCESS {
+ PROCESS *pNext; //!< pointer to next process in active or free list
+ PROCESS *pPrevious; //!< pointer to previous process in active or free list
-struct PROCESS;
+ CoroContext state; //!< the state of the coroutine
+ CORO_ADDR coroAddr; //!< the entry point of the coroutine
+
+ int sleepTime; //!< number of scheduler cycles to sleep
+ int pid; //!< process ID
+ char param[PARAM_SIZE]; //!< process specific info
+};
+typedef PROCESS *PPROCESS;
+
+struct INT_CONTEXT;
/**
* Create and manage "processes" (really coroutines).
@@ -64,11 +80,13 @@ private:
/** the currently active process */
PROCESS *pCurrent;
-
+
#ifdef DEBUG
// diagnostic process counters
int numProcs;
int maxProcs;
+
+ void CheckStack();
#endif
/**
@@ -90,13 +108,16 @@ public:
#endif
void schedule();
-
+ void rescheduleAll();
+ void reschedule(PPROCESS pReSchedProc = NULL);
+ void giveWay(PPROCESS pReSchedProc = NULL);
+
PROCESS *createProcess(int pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam);
void killProcess(PROCESS *pKillProc);
PROCESS *getCurrentProcess();
int getCurrentPID() const;
- int killMatchingProcess(int pidKill, int pidMask);
+ int killMatchingProcess(int pidKill, int pidMask = -1);
void setResourceCallback(VFPTRPP pFunc);
@@ -105,6 +126,23 @@ public:
extern Scheduler *g_scheduler; // FIXME: Temporary global var, to be used until everything has been OOifyied
+//----------------- FUNCTION PROTOTYPES --------------------
+
+void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess);
+void CallSceneProcess(uint32 procID);
+void KillSceneProcess(uint32 procID);
+void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait,
+ int myEscape, bool *result = NULL);
+void RestoreSceneProcess(INT_CONTEXT *pic);
+
+void GlobalProcesses(uint32 numProcess, byte *pProcess);
+void xCallGlobalProcess(uint32 procID);
+void xKillGlobalProcess(uint32 procID);
+bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape);
+void RestoreGlobalProcess(INT_CONTEXT *pic);
+void KillGlobalProcesses(void);
+void FreeGlobalProcesses();
+
} // end of namespace Tinsel
#endif // TINSEL_SCHED_H
diff --git a/engines/tinsel/scn.cpp b/engines/tinsel/scn.cpp
index 8639979b41..2825a90fe3 100644
--- a/engines/tinsel/scn.cpp
+++ b/engines/tinsel/scn.cpp
@@ -47,10 +47,15 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
uint32 *lptr = (uint32 *)bptr;
uint32 add;
- // V1 chunk types can be found by substracting 2 from the
+ // Initial adjustmnet for Tinsel 1 chunk types
+ if ((TinselVersion != TINSEL_V2) && (chunk >= CHUNK_SCENE) &&
+ (chunk != CHUNK_MBSTRING))
+ --chunk;
+
+ // V0 chunk types can be found by substracting 2 from the
// chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are
- // the same in V1 and V2
- if (_vm->getVersion() == TINSEL_V0 &&
+ // the same in V0 and V1
+ if (TinselVersion == TINSEL_V0 &&
chunk != CHUNK_STRING && chunk != CHUNK_BITMAP)
chunk -= 0x2L;
@@ -70,10 +75,10 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
/**
* Get the actor id from a film (column 0)
*/
-int extractActor(SCNHANDLE film) {
- const FILM *pfilm = (const FILM *)LockMem(film);
- const FREEL *preel = &pfilm->reels[0];
- const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(preel->mobj));
+int ExtractActor(SCNHANDLE hFilm) {
+ const FILM *pFilm = (const FILM *)LockMem(hFilm);
+ const FREEL *pReel = &pFilm->reels[0];
+ const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj));
return (int)FROM_LE_32(pmi->mulID);
}
diff --git a/engines/tinsel/scn.h b/engines/tinsel/scn.h
index 29f3dc51fc..5b16714b36 100644
--- a/engines/tinsel/scn.h
+++ b/engines/tinsel/scn.h
@@ -30,6 +30,14 @@
namespace Tinsel {
+#define INDEX_FILENAME "index" // name of scene index file
+#define INDEXFILE_LENGTH 12 // length of filenames in the MEMHANDLE structure
+
+#define GLOBALS_FILENAME "gdata" // name of globals file
+#define HOPPER_FILENAME "hopper"
+#define CD_ID_FILENAME "volume"
+
+#define BMOVIE_EXTENSION ".bmv"
// chunk identifier numbers
@@ -48,20 +56,42 @@ namespace Tinsel {
#define CHUNK_ENTRANCE 0x3334000BL // not used!
#define CHUNK_POLYGONS 0x3334000CL // not used!
#define CHUNK_ACTORS 0x3334000DL // not used!
-#define CHUNK_SCENE 0x3334000EL
-#define CHUNK_TOTAL_ACTORS 0x3334000FL
-#define CHUNK_TOTAL_GLOBALS 0x33340010L
-#define CHUNK_TOTAL_OBJECTS 0x33340011L
-#define CHUNK_OBJECTS 0x33340012L
-#define CHUNK_MIDI 0x33340013L // not used!
-#define CHUNK_SAMPLE 0x33340014L // not used!
-#define CHUNK_TOTAL_POLY 0x33340015L
-#define CHUNK_MBSTRING 0x33340022L // Multi-byte characters
+
+#define CHUNK_PROCESSES 0x3334000EL // Tinsel 2 only
+
+// Following chunk Ids should be decremented by 1 for Tinsel 1
+#define CHUNK_SCENE 0x3334000FL
+#define CHUNK_TOTAL_ACTORS 0x33340010L
+#define CHUNK_TOTAL_GLOBALS 0x33340011L
+#define CHUNK_TOTAL_OBJECTS 0x33340012L
+#define CHUNK_OBJECTS 0x33340013L
+#define CHUNK_MIDI 0x33340014L // not used!
+#define CHUNK_SAMPLE 0x33340015L // not used!
+#define CHUNK_TOTAL_POLY 0x33340016L
+
+// Following chunks are Tinsel 2 only
+#define CHUNK_NUM_PROCESSES 0x33340017L // Master scene only
+#define CHUNK_MASTER_SCRIPT 0x33340018L
+#define CHUNK_CDPLAY_FILENUM 0x33340019L
+#define CHUNK_CDPLAY_HANDLE 0x3334001AL
+#define CHUNK_CDPLAY_FILENAME 0x3334001BL
+#define CHUNK_MUSIC_FILENAME 0x3334001CL
+#define CHUNK_MUSIC_SCRIPT 0x3334001DL
+#define CHUNK_MUSIC_SEGMENT 0x3334001EL
+#define CHUNK_SCENE_HOPPER 0x3334001FL // Hopper file only
+#define CHUNK_SCENE_HOPPER2 0x33340030L // Hopper file only
+#define CHUNK_TIME_STAMPS 0x33340020L
+
+// This single chunk is common to all Tinsel versions
+#define CHUNK_MBSTRING 0x33340022L
+
+// This is a base, subsequent numbers may also get used
+#define CHUNK_GRAB_NAME 0x33340100L
#define INDEX_FILENAME "index" // name of index file
byte *FindChunk(SCNHANDLE handle, uint32 chunk);
-int extractActor(SCNHANDLE film);
+int ExtractActor(SCNHANDLE hFilm);
} // end of namespace Tinsel
diff --git a/engines/tinsel/scroll.cpp b/engines/tinsel/scroll.cpp
index aa1bc67298..579775c91f 100644
--- a/engines/tinsel/scroll.cpp
+++ b/engines/tinsel/scroll.cpp
@@ -33,17 +33,11 @@
#include "tinsel/rince.h"
#include "tinsel/scroll.h"
#include "tinsel/sched.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/tinsel.h"
namespace Tinsel {
-//----------------- EXTERNAL FUNCTIONS ---------------------
-
-// in BG.C
-extern int BackgroundWidth(void);
-extern int BackgroundHeight(void);
-
-
-
//----------------- LOCAL DEFINES --------------------
#define LEFT 'L'
@@ -58,7 +52,7 @@ extern int BackgroundHeight(void);
static int LeftScroll = 0, DownScroll = 0; // Number of iterations outstanding
static int scrollActor = 0;
-static PMACTOR psActor = 0;
+static PMOVER pScrollMover = 0;
static int oldx = 0, oldy = 0;
/** Boundaries and numbers of boundaries */
@@ -72,6 +66,14 @@ static SCROLLDATA sd = {
{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}
},
0,
+ 0,
+ // DW2 fields
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
0
};
@@ -81,7 +83,8 @@ static bool ScrollCursor = 0; // If a TAG or EXIT polygon is clicked on,
// the cursor is kept over that polygon
// whilst scrolling
-static int scrollPixels = SCROLLPIXELS;
+static int scrollPixelsX = SCROLLPIXELS;
+static int scrollPixelsY = SCROLLPIXELS;
/**
@@ -124,13 +127,6 @@ void SetNoScroll(int x1, int y1, int x2, int y2) {
}
/**
- * Does the obvious - called at the end of a scene.
- */
-void DropNoScrolls(void) {
- sd.NumNoH = sd.NumNoV = 0;
-}
-
-/**
* Called from scroll process when it thinks that a scroll is in order.
* Checks for no-scroll boundaries and sets off a scroll if allowed.
*/
@@ -157,8 +153,13 @@ static void NeedScroll(int direction) {
}
if (LeftScroll <= 0) {
- scrollPixels = SCROLLPIXELS;
- LeftScroll = RLSCROLL;
+ if (TinselV2) {
+ scrollPixelsX = sd.xSpeed;
+ LeftScroll += sd.xDistance;
+ } else {
+ scrollPixelsX = SCROLLPIXELS;
+ LeftScroll = RLSCROLL;
+ }
}
break;
@@ -175,8 +176,13 @@ static void NeedScroll(int direction) {
}
if (LeftScroll >= 0) {
- scrollPixels = SCROLLPIXELS;
- LeftScroll = -RLSCROLL;
+ if (TinselV2) {
+ scrollPixelsX = sd.xSpeed;
+ LeftScroll -= sd.xDistance;
+ } else {
+ scrollPixelsX = SCROLLPIXELS;
+ LeftScroll = -RLSCROLL;
+ }
}
break;
@@ -194,8 +200,13 @@ static void NeedScroll(int direction) {
}
if (DownScroll <= 0) {
- scrollPixels = SCROLLPIXELS;
- DownScroll = UDSCROLL;
+ if (TinselV2) {
+ scrollPixelsY = sd.ySpeed;
+ DownScroll += sd.yDistance;
+ } else {
+ scrollPixelsY = SCROLLPIXELS;
+ DownScroll = UDSCROLL;
+ }
}
break;
@@ -212,8 +223,13 @@ static void NeedScroll(int direction) {
}
if (DownScroll >= 0) {
- scrollPixels = SCROLLPIXELS;
- DownScroll = -UDSCROLL;
+ if (TinselV2) {
+ scrollPixelsY = sd.ySpeed;
+ DownScroll -= sd.yDistance;
+ } else {
+ scrollPixelsY = SCROLLPIXELS;
+ DownScroll = -UDSCROLL;
+ }
}
break;
}
@@ -234,7 +250,7 @@ static void ScrollImage(void) {
* Keeping cursor on a tag?
*/
if (ScrollCursor) {
- GetCursorXY(&curX, &curY, true);
+ GetCursorXYNoWait(&curX, &curY, true);
if (InPolygon(curX, curY, TAG) != NOPOLY || InPolygon(curX, curY, EXIT) != NOPOLY) {
OldLoffset = Loffset;
OldToffset = Toffset;
@@ -246,49 +262,66 @@ static void ScrollImage(void) {
* Horizontal scrolling
*/
if (LeftScroll > 0) {
- LeftScroll -= scrollPixels;
+ LeftScroll -= scrollPixelsX;
if (LeftScroll < 0) {
Loffset += LeftScroll;
LeftScroll = 0;
}
- Loffset += scrollPixels; // Move right
+ Loffset += scrollPixelsX; // Move right
if (Loffset > ImageW - SCREEN_WIDTH)
Loffset = ImageW - SCREEN_WIDTH;// Now at extreme right
+
+ /*** New feature to prop up rickety scroll boundaries ***/
+ if (TinselV2 && SysVar(SV_MaximumXoffset) && (Loffset > SysVar(SV_MaximumXoffset)))
+ Loffset = SysVar(SV_MaximumXoffset);
+
} else if (LeftScroll < 0) {
- LeftScroll += scrollPixels;
+ LeftScroll += scrollPixelsX;
if (LeftScroll > 0) {
Loffset += LeftScroll;
LeftScroll = 0;
}
- Loffset -= scrollPixels; // Move left
+ Loffset -= scrollPixelsX; // Move left
if (Loffset < 0)
Loffset = 0; // Now at extreme left
+
+ /*** New feature to prop up rickety scroll boundaries ***/
+ if (TinselV2 && SysVar(SV_MinimumXoffset) && (Loffset < SysVar(SV_MinimumXoffset)))
+ Loffset = SysVar(SV_MinimumXoffset);
}
/*
* Vertical scrolling
*/
if (DownScroll > 0) {
- DownScroll -= scrollPixels;
+ DownScroll -= scrollPixelsY;
if (DownScroll < 0) {
Toffset += DownScroll;
DownScroll = 0;
}
- Toffset += scrollPixels; // Move down
+ Toffset += scrollPixelsY; // Move down
if (Toffset > ImageH - SCREEN_HEIGHT)
Toffset = ImageH - SCREEN_HEIGHT;// Now at extreme bottom
+ /*** New feature to prop up rickety scroll boundaries ***/
+ if (TinselV2 && SysVar(SV_MaximumYoffset) && Toffset > SysVar(SV_MaximumYoffset))
+ Toffset = SysVar(SV_MaximumYoffset);
+
} else if (DownScroll < 0) {
- DownScroll += scrollPixels;
+ DownScroll += scrollPixelsY;
if (DownScroll > 0) {
Toffset += DownScroll;
DownScroll = 0;
}
- Toffset -= scrollPixels; // Move up
+ Toffset -= scrollPixelsY; // Move up
if (Toffset < 0)
Toffset = 0; // Now at extreme top
+
+ /*** New feature to prop up rickety scroll boundaries ***/
+ if (TinselV2 && SysVar(SV_MinimumYoffset) && Toffset < SysVar(SV_MinimumYoffset))
+ Toffset = SysVar(SV_MinimumYoffset);
}
/*
@@ -312,8 +345,7 @@ static void MonitorScroll(void) {
/*
* Only do it if the actor is there and is visible
*/
- if (!psActor || getMActorHideState(psActor)
- || getMActorState(psActor) == NO_MACTOR)
+ if (!pScrollMover || MoverHidden(pScrollMover) || !MoverIs(pScrollMover))
return;
GetActorPos(scrollActor, &newx, &newy);
@@ -326,7 +358,7 @@ static void MonitorScroll(void) {
/*
* Approaching right side or left side of the screen?
*/
- if (newx > Loffset+SCREEN_WIDTH-RLDISTANCE && Loffset < ImageW-SCREEN_WIDTH) {
+ if (newx > Loffset+SCREEN_WIDTH - RLDISTANCE && Loffset < ImageW - SCREEN_WIDTH) {
if (newx > oldx)
NeedScroll(LEFT);
} else if (newx < Loffset + RLDISTANCE && Loffset) {
@@ -337,10 +369,10 @@ static void MonitorScroll(void) {
/*
* Approaching bottom or top of the screen?
*/
- if (newy > Toffset+SCREEN_HEIGHT-UDDISTANCE && Toffset < ImageH-SCREEN_HEIGHT) {
+ if (newy > Toffset+SCREEN_HEIGHT-DDISTANCE && Toffset < ImageH-SCREEN_HEIGHT) {
if (newy > oldy)
NeedScroll(UP);
- } else if (Toffset && newy < Toffset + UDDISTANCE + GetActorBottom(scrollActor) - GetActorTop(scrollActor)) {
+ } else if (Toffset && newy < Toffset + UDISTANCE + GetActorBottom(scrollActor) - GetActorTop(scrollActor)) {
if (newy < oldy)
NeedScroll(DOWN);
}
@@ -349,6 +381,30 @@ static void MonitorScroll(void) {
oldy = newy;
}
+static void RestoreScrollDefaults(void) {
+ sd.xTrigger = SysVar(SV_SCROLL_XTRIGGER);
+ sd.xDistance = SysVar(SV_SCROLL_XDISTANCE);
+ sd.xSpeed = SysVar(SV_SCROLL_XSPEED);
+ sd.yTriggerTop = SysVar(SV_SCROLL_YTRIGGERTOP);
+ sd.yTriggerBottom= SysVar(SV_SCROLL_YTRIGGERBOT);
+ sd.yDistance = SysVar(SV_SCROLL_YDISTANCE);
+ sd.ySpeed = SysVar(SV_SCROLL_YSPEED);
+}
+
+/**
+ * Does the obvious - called at the end of a scene.
+ */
+void DropScroll(void) {
+ sd.NumNoH = sd.NumNoV = 0;
+ if (TinselV2) {
+ LeftScroll = DownScroll = 0; // No iterations outstanding
+ oldx = oldy = 0;
+ scrollPixelsX = sd.xSpeed;
+ scrollPixelsY = sd.ySpeed;
+ RestoreScrollDefaults();
+ }
+}
+
/**
* Decide when to scroll and scroll when decided to.
*/
@@ -359,21 +415,28 @@ void ScrollProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CODE(_ctx);
- ImageH = BackgroundHeight(); // Dimensions
- ImageW = BackgroundWidth(); // of this scene.
+ // In Tinsel v2, scenes may play movies, so the background may not always
+ // already be initialised like it is in v1
+ while (!GetBgObject())
+ CORO_SLEEP(1);
+
+ ImageH = BgHeight(); // Dimensions
+ ImageW = BgWidth(); // of this scene.
// Give up if there'll be no purpose in this process
if (ImageW == SCREEN_WIDTH && ImageH == SCREEN_HEIGHT)
CORO_KILL_SELF();
- LeftScroll = DownScroll = 0; // No iterations outstanding
- oldx = oldy = 0;
- scrollPixels = SCROLLPIXELS;
+ if (!TinselV2) {
+ LeftScroll = DownScroll = 0; // No iterations outstanding
+ oldx = oldy = 0;
+ scrollPixelsX = scrollPixelsY = SCROLLPIXELS;
+ }
if (!scrollActor)
- scrollActor = LeadId();
+ scrollActor = GetLeadId();
- psActor = GetMover(scrollActor);
+ pScrollMover = GetMover(scrollActor);
while (1) {
MonitorScroll(); // Set scroll requirement
@@ -395,17 +458,26 @@ void ScrollFocus(int ano) {
oldx = oldy = 0;
scrollActor = ano;
- psActor = ano ? GetMover(scrollActor) : NULL;
+ pScrollMover = ano ? GetMover(scrollActor) : NULL;
}
}
/**
+ * Returns the actor which the camera is following
+ */
+int GetScrollFocus(void) {
+ return scrollActor;
+}
+
+
+/**
* Scroll to abslote position.
*/
-void ScrollTo(int x, int y, int iter) {
+void ScrollTo(int x, int y, int xIter, int yIter) {
int Loffset, Toffset; // for background offsets
- scrollPixels = iter != 0 ? iter : SCROLLPIXELS;
+ scrollPixelsX = xIter != 0 ? xIter : (TinselV2 ? sd.xSpeed : SCROLLPIXELS);
+ scrollPixelsY = yIter != 0 ? yIter : (TinselV2 ? sd.ySpeed : SCROLLPIXELS);
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); // get background offsets
@@ -429,4 +501,35 @@ void RestoreNoScrollData(SCROLLDATA *ssd) {
memcpy(&sd, ssd, sizeof(SCROLLDATA));
}
+/**
+ * SetScrollParameters
+ */
+void SetScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
+ int yTriggerBottom, int yDistance, int ySpeed) {
+ if (xTrigger == 0 && xDistance == 0 && xSpeed == 0
+ && yTriggerTop == 0 && yTriggerBottom && yDistance == 0 && ySpeed == 0) {
+ // Restore defaults
+ RestoreScrollDefaults();
+ } else {
+ if (xTrigger)
+ sd.xTrigger = xTrigger;
+ if (xDistance)
+ sd.xDistance = xDistance;
+ if (xSpeed)
+ sd.xSpeed = xSpeed;
+ if (yTriggerTop)
+ sd.yTriggerTop = yTriggerTop;
+ if (yTriggerBottom)
+ sd.yTriggerBottom = yTriggerBottom;
+ if (yDistance)
+ sd.yDistance = yDistance;
+ if (ySpeed)
+ sd.ySpeed = ySpeed;
+ }
+}
+
+bool IsScrolling(void) {
+ return (LeftScroll || DownScroll);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/scroll.h b/engines/tinsel/scroll.h
index ac903157f2..864fe74beb 100644
--- a/engines/tinsel/scroll.h
+++ b/engines/tinsel/scroll.h
@@ -30,8 +30,10 @@ namespace Tinsel {
#define SCROLLPIXELS 8 // Number of pixels to scroll per iteration
-#define RLDISTANCE 50 // Distance from edge that triggers a scroll
-#define UDDISTANCE 20
+// Distance from edge that triggers a scroll
+#define RLDISTANCE (TinselV2 ? sd.xTrigger : 50)
+#define UDISTANCE (TinselV2 ? sd.yTriggerTop : 20)
+#define DDISTANCE (TinselV2 ? sd.yTriggerBottom : 20)
// Number of iterations to make
#define RLSCROLL 160 // 20*8 = 160 = half a screen
@@ -52,26 +54,39 @@ struct SCROLLDATA{
NOSCROLLB NoVScroll[MAX_VNOSCROLL]; // Vertical no-scroll boundaries
NOSCROLLB NoHScroll[MAX_HNOSCROLL]; // Horizontal no-scroll boundaries
unsigned NumNoV, NumNoH; // Counts of no-scroll boundaries
+ // DW2 fields
+ int xTrigger;
+ int xDistance;
+ int xSpeed;
+ int yTriggerTop;
+ int yTriggerBottom;
+ int yDistance;
+ int ySpeed;
};
-
void DontScrollCursor(void);
void DoScrollCursor(void);
void SetNoScroll(int x1, int y1, int x2, int y2);
-void DropNoScrolls(void);
+void DropScroll(void);
void ScrollProcess(CORO_PARAM, const void *);
void ScrollFocus(int actor);
-void ScrollTo(int x, int y, int iter);
+int GetScrollFocus(void);
+void ScrollTo(int x, int y, int xIter, int yIter);
void KillScroll(void);
void GetNoScrollData(SCROLLDATA *ssd);
void RestoreNoScrollData(SCROLLDATA *ssd);
+void SetScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
+ int yTriggerBottom, int yDistance, int ySpeed);
+
+bool IsScrolling(void);
+
} // end of namespace Tinsel
#endif /* TINSEL_SCROLL_H */
diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp
index e37c80ec61..9d1996ca01 100644
--- a/engines/tinsel/sound.cpp
+++ b/engines/tinsel/sound.cpp
@@ -31,6 +31,8 @@
#include "tinsel/music.h"
#include "tinsel/strres.h"
#include "tinsel/tinsel.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/background.h"
#include "common/endian.h"
#include "common/file.h"
@@ -38,14 +40,20 @@
#include "sound/mixer.h"
#include "sound/audiocd.h"
+#include "sound/adpcm.h"
namespace Tinsel {
+extern LANGUAGE sampleLanguage;
+
//--------------------------- General data ----------------------------------
SoundManager::SoundManager(TinselEngine *vm) :
//_vm(vm), // TODO: Enable this once global _vm var is gone
_sampleIndex(0), _sampleIndexLen(0) {
+
+ for (int i = 0; i < kNumChannels; i++)
+ _channels[i].sampleNum = _channels[i].subSample = -1;
}
SoundManager::~SoundManager() {
@@ -67,24 +75,29 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
if (!_vm->_mixer->isReady())
return false;
+ Channel &curChan = _channels[kChannelTinsel1];
+
// stop any currently playing sample
- _vm->_mixer->stopHandle(_handle);
+ _vm->_mixer->stopHandle(curChan.handle);
// make sure id is in range
assert(id > 0 && id < _sampleIndexLen);
+ curChan.sampleNum = id;
+ curChan.subSample = 0;
+
// get file offset for this sample
- int32 dwSampleIndex = _sampleIndex[id];
+ uint32 dwSampleIndex = _sampleIndex[id];
// move to correct position in the sample file
_sampleStream.seek(dwSampleIndex);
- if (_sampleStream.ioFailed() || _sampleStream.pos() != dwSampleIndex)
- error("File %s is corrupt", SAMPLE_FILE);
+ if (_sampleStream.ioFailed() || (uint32)_sampleStream.pos() != dwSampleIndex)
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// read the length of the sample
uint32 sampleLen = _sampleStream.readUint32LE();
if (_sampleStream.ioFailed())
- error("File %s is corrupt", SAMPLE_FILE);
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// allocate a buffer
void *sampleBuf = malloc(sampleLen);
@@ -92,7 +105,7 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
// read all of the sample
if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
- error("File %s is corrupt", SAMPLE_FILE);
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volSound);
@@ -101,15 +114,211 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
// play it
- _vm->_mixer->playRaw(type, &_handle, sampleBuf, sampleLen, 22050,
+ _vm->_mixer->playRaw(type, &curChan.handle, sampleBuf, sampleLen, 22050,
Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED);
if (handle)
- *handle = _handle;
+ *handle = curChan.handle;
return true;
}
+bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int priority,
+ Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
+
+ // Floppy version has no sample file
+ if (_vm->getFeatures() & GF_FLOPPY)
+ return false;
+
+ // no sample driver?
+ if (!_vm->_mixer->isReady())
+ return false;
+
+ Channel *curChan;
+
+ uint8 sndVol = 255;
+
+ // Sample on screen?
+ if (!offscreenChecks(x, y))
+ return false;
+
+ // If that sample is already playing, stop it
+ stopSpecSample(id, sub);
+
+ if (type == Audio::Mixer::kSpeechSoundType) {
+ curChan = &_channels[kChannelTalk];
+ } else if (type == Audio::Mixer::kSFXSoundType) {
+ uint32 oldestTime = g_system->getMillis();
+ int oldestChan = kChannelSFX;
+
+ int chan;
+ for (chan = kChannelSFX; chan < kNumChannels; chan++) {
+ if (!_vm->_mixer->isSoundHandleActive(_channels[chan].handle))
+ break;
+
+ if ((_channels[chan].lastStart < oldestTime) &&
+ (_channels[chan].priority <= priority)) {
+
+ oldestTime = _channels[chan].lastStart;
+ oldestChan = chan;
+ }
+ }
+
+ if (chan == kNumChannels) {
+ if (_channels[oldestChan].priority > priority) {
+ warning("playSample: No free channel");
+ return false;
+ }
+
+ chan = oldestChan;
+ }
+
+ if (_vm->_pcmMusic->isDimmed() && SysVar(SYS_SceneFxDimFactor))
+ sndVol = 255 - 255/SysVar(SYS_SceneFxDimFactor);
+
+ curChan = &_channels[chan];
+ } else {
+ warning("playSample: Unknown SoundType");
+ return false;
+ }
+
+ // stop any currently playing sample
+ _vm->_mixer->stopHandle(curChan->handle);
+
+ // make sure id is in range
+ assert(id > 0 && id < _sampleIndexLen);
+
+ // get file offset for this sample
+ uint32 dwSampleIndex = _sampleIndex[id];
+
+ if (dwSampleIndex == 0) {
+ warning("Tinsel2 playSample, non-existant sample %d", id);
+ return false;
+ }
+
+ // move to correct position in the sample file
+ _sampleStream.seek(dwSampleIndex);
+ if (_sampleStream.ioFailed() || (uint32)_sampleStream.pos() != dwSampleIndex)
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
+
+ // read the length of the sample
+ uint32 sampleLen = _sampleStream.readUint32LE();
+ if (_sampleStream.ioFailed())
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
+
+ if (sampleLen & 0x80000000) {
+ // Has sub samples
+
+ int32 numSubs = sampleLen & ~0x80000000;
+
+ assert(sub >= 0 && sub < numSubs);
+
+ // Skipping
+ for (int32 i = 0; i < sub; i++) {
+ sampleLen = _sampleStream.readUint32LE();
+ _sampleStream.skip(sampleLen);
+ if (_sampleStream.ioFailed())
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
+ }
+ sampleLen = _sampleStream.readUint32LE();
+ if (_sampleStream.ioFailed())
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
+ }
+
+ debugC(DEBUG_DETAILED, kTinselDebugSound, "Playing sound %d.%d, %d bytes at %d (pan %d)", id, sub, sampleLen,
+ _sampleStream.pos(), getPan(x));
+
+ // allocate a buffer
+ byte *sampleBuf = (byte *) malloc(sampleLen);
+ assert(sampleBuf);
+
+ // read all of the sample
+ if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
+
+ Common::MemoryReadStream *sampleStream =
+ new Common::MemoryReadStream(sampleBuf, sampleLen, true);
+ Audio::AudioStream *_stream =
+ makeADPCMStream(sampleStream, true, sampleLen, Audio::kADPCMTinsel6, 22050, 1, 24);
+
+ // FIXME: Should set this in a different place ;)
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volSound);
+ //_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volVoice);
+
+ curChan->sampleNum = id;
+ curChan->subSample = sub;
+ curChan->looped = bLooped;
+ curChan->x = x;
+ curChan->y = y;
+ curChan->priority = priority;
+ curChan->lastStart = g_system->getMillis();
+ // /---Compression----\ Milis BytesPerSecond
+ curChan->timeDuration = (((sampleLen * 64) / 25) * 1000) / (22050 * 2);
+
+ // Play it
+ _vm->_mixer->playInputStream(type, &curChan->handle, _stream);
+ _vm->_mixer->setChannelVolume(curChan->handle, sndVol);
+ _vm->_mixer->setChannelBalance(curChan->handle, getPan(x));
+
+ if (handle)
+ *handle = curChan->handle;
+
+ return true;
+}
+
+/**
+ * Returns FALSE if sample doesn't need playing
+ */
+bool SoundManager::offscreenChecks(int x, int &y)
+{
+ // No action if no x specification
+ if (x == -1)
+ return true;
+
+ // convert x to offset from screen centre
+ x -= PlayfieldGetCentreX(FIELD_WORLD);
+
+ if (x < -SCREEN_WIDTH || x > SCREEN_WIDTH) {
+ // A long way offscreen, ignore it
+ return false;
+ } else if (x < -SCREEN_WIDTH/2 || x > SCREEN_WIDTH/2) {
+ // Off-screen, attennuate it
+
+ y = (y > 0) ? (y / 2) : 50;
+
+ return true;
+ } else
+ return true;
+}
+
+int8 SoundManager::getPan(int x) {
+
+ if (x == -1)
+ return 0;
+
+ x -= PlayfieldGetCentreX(FIELD_WORLD);
+
+ if (x == 0)
+ return 0;
+
+ if (x < 0) {
+ if (x < (-SCREEN_WIDTH / 2))
+ return -127;
+
+ x = (-x * 127) / (SCREEN_WIDTH / 2);
+
+ return 0 - x;
+ }
+
+ if (x > (SCREEN_WIDTH / 2))
+ return 127;
+
+ x = (x * 127) / (SCREEN_WIDTH / 2);
+
+ return x;
+}
+
/**
* Returns TRUE if there is a sample for the specified sample identifier.
* @param id Identifier of sample to be checked
@@ -131,8 +340,16 @@ bool SoundManager::sampleExists(int id) {
/**
* Returns true if a sample is currently playing.
*/
-bool SoundManager::sampleIsPlaying(void) {
- return _vm->_mixer->isSoundHandleActive(_handle);
+bool SoundManager::sampleIsPlaying(int id) {
+ if (!TinselV2)
+ return _vm->_mixer->isSoundHandleActive(_channels[kChannelTinsel1].handle);
+
+ for (int i = 0; i < kNumChannels; i++)
+ if (_channels[i].sampleNum == id)
+ if (_vm->_mixer->isSoundHandleActive(_channels[i].handle))
+ return true;
+
+ return false;
}
/**
@@ -140,7 +357,37 @@ bool SoundManager::sampleIsPlaying(void) {
*/
void SoundManager::stopAllSamples(void) {
// stop currently playing sample
- _vm->_mixer->stopHandle(_handle);
+
+ if (!TinselV2) {
+ _vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
+ return;
+ }
+
+ for (int i = 0; i < kNumChannels; i++)
+ _vm->_mixer->stopHandle(_channels[i].handle);
+}
+
+void SoundManager::stopSpecSample(int id, int sub) {
+ debugC(DEBUG_DETAILED, kTinselDebugSound, "stopSpecSample(%d, %d)", id, sub);
+
+ if (!TinselV2) {
+ if (_channels[kChannelTinsel1].sampleNum == id)
+ _vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
+ return;
+ }
+
+ for (int i = kChannelTalk; i < kNumChannels; i++) {
+ if ((_channels[i].sampleNum == id) && (_channels[i].subSample == sub))
+ _vm->_mixer->stopHandle(_channels[i].handle);
+ }
+}
+
+void SoundManager::setSFXVolumes(uint8 volume) {
+ if (!TinselV2)
+ return;
+
+ for (int i = kChannelSFX; i < kNumChannels; i++)
+ _vm->_mixer->setChannelVolume(_channels[i].handle, volume);
}
/**
@@ -158,7 +405,7 @@ void SoundManager::openSampleFiles(void) {
return;
// open sample index file in binary mode
- if (f.open(SAMPLE_INDEX)) {
+ if (f.open(_vm->getSampleIndex(sampleLanguage))) {
// get length of index file
f.seek(0, SEEK_END); // move to end of file
_sampleIndexLen = f.pos(); // get file pointer
@@ -166,7 +413,7 @@ void SoundManager::openSampleFiles(void) {
if (_sampleIndex == NULL) {
// allocate a buffer for the indices
- _sampleIndex = (int32 *)malloc(_sampleIndexLen);
+ _sampleIndex = (uint32 *)malloc(_sampleIndexLen);
// make sure memory allocated
if (_sampleIndex == NULL) {
@@ -179,7 +426,7 @@ void SoundManager::openSampleFiles(void) {
// load data
if (f.read(_sampleIndex, _sampleIndexLen) != (uint32)_sampleIndexLen)
// file must be corrupt if we get to here
- error("File %s is corrupt", SAMPLE_FILE);
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
#ifdef SCUMM_BIG_ENDIAN
// Convert all ids from LE to native format
@@ -194,18 +441,25 @@ void SoundManager::openSampleFiles(void) {
// convert file size to size in DWORDs
_sampleIndexLen /= sizeof(uint32);
} else
- error("Cannot find file %s", SAMPLE_INDEX);
+ error(CANNOT_FIND_FILE, _vm->getSampleIndex(sampleLanguage));
// open sample file in binary mode
- if (!_sampleStream.open(SAMPLE_FILE))
- error("Cannot find file %s", SAMPLE_FILE);
+ if (!_sampleStream.open(_vm->getSampleFile(sampleLanguage)))
+ error(CANNOT_FIND_FILE, _vm->getSampleFile(sampleLanguage));
/*
// gen length of the largest sample
sampleBuffer.size = _sampleStream.readUint32LE();
if (_sampleStream.ioFailed())
- error("File %s is corrupt", SAMPLE_FILE);
+ error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
*/
}
+void SoundManager::closeSampleStream(void) {
+ _sampleStream.close();
+ free(_sampleIndex);
+ _sampleIndex = 0;
+ _sampleIndexLen = 0;
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/sound.h b/engines/tinsel/sound.h
index 330409cf59..9b980822dc 100644
--- a/engines/tinsel/sound.h
+++ b/engines/tinsel/sound.h
@@ -37,6 +37,11 @@
namespace Tinsel {
+enum STYPE {FX, VOICE};
+
+enum PlayPriority { PRIORITY_SCRIPT, PRIORITY_SPLAY1, PRIORITY_SPLAY2, PRIORITY_TALK };
+
+enum SampleFlags {PS_COMPLETE = 0x01, PS_SUSTAIN = 0x02};
/*----------------------------------------------------------------------*\
|* Function Prototypes *|
@@ -44,14 +49,39 @@ namespace Tinsel {
class SoundManager {
protected:
+ static const int kNumSFX = 3; // Number of SFX channels
+ enum {
+ kChannelTalk = 0,
+ kChannelTinsel1 = 0, // Always using this channel for DW1
+ kChannelSFX = 1
+ };
+ static const int kNumChannels = kChannelSFX + kNumSFX;
+
+ struct Channel {
+ // Sample handle
+ Audio::SoundHandle handle;
+
+ // Sample id
+ int sampleNum;
+ int subSample;
+
+ // Playing properties
+ bool looped;
+ int x, y;
+ int priority;
+
+ // Time properties
+ uint32 timeStarted;
+ uint32 timeDuration;
+ uint32 lastStart;
+ };
+
+ Channel _channels[kNumChannels];
//TinselEngine *_vm; // TODO: Enable this once global _vm var is gone
- /** Sample handle */
- Audio::SoundHandle _handle;
-
/** Sample index buffer and number of entries */
- int32 *_sampleIndex;
+ uint32 *_sampleIndex;
/** Number of entries in the sample index */
long _sampleIndexLen;
@@ -59,19 +89,29 @@ protected:
/** file stream for sample file */
Common::File _sampleStream;
+ bool offscreenChecks(int x, int &y);
+ int8 getPan(int x);
+
public:
SoundManager(TinselEngine *vm);
~SoundManager();
bool playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
- void stopAllSamples(void); // Stops any currently playing sample
+ bool playSample(int id, int sub, bool bLooped, int x, int y, int priority,
+ Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
+
+ void stopAllSamples(void); // Stops any currently playing sample
+ void stopSpecSample(int id, int sub = 0); // Stops a specific sample
+ void setSFXVolumes(uint8 volume);
+
bool sampleExists(int id);
- bool sampleIsPlaying(void);
+ bool sampleIsPlaying(int id = -1);
// TODO: Internal method, make this protected?
void openSampleFiles(void);
+ void closeSampleStream(void);
};
} // end of namespace Tinsel
diff --git a/engines/tinsel/strres.cpp b/engines/tinsel/strres.cpp
index abf5a880f6..0997d68fbe 100644
--- a/engines/tinsel/strres.cpp
+++ b/engines/tinsel/strres.cpp
@@ -40,18 +40,39 @@ int newestString;
// buffer for resource strings
static uint8 *textBuffer = 0;
-// language resource string filenames
-static const char *languageFiles[] = {
- "english.txt",
- "french.txt",
- "german.txt",
- "italian.txt",
- "spanish.txt"
+static struct {
+ bool bPresent;
+ const char *szStem;
+ SCNHANDLE hDescription;
+ SCNHANDLE hFlagFilm;
+
+} languages[NUM_LANGUAGES] = {
+
+ { false, "English", 0, 0 },
+ { false, "French", 0, 0 },
+ { false, "German", 0, 0 },
+ { false, "Italian", 0, 0 },
+ { false, "Spanish", 0, 0 },
+ { false, "Hebrew", 0, 0 },
+ { false, "Magyar", 0, 0 },
+ { false, "Japanese",0, 0 },
+ { false, "US", 0, 0 }
};
+
// Set if we're handling 2-byte characters.
bool bMultiByte = false;
+LANGUAGE textLanguage, sampleLanguage = TXT_ENGLISH;
+
+//----------------- LOCAL DEFINES ----------------------------
+
+#define languageExtension ".txt"
+#define indexExtension ".idx"
+#define sampleExtension ".smp"
+
+//----------------- FUNCTIONS --------------------------------
+
/**
* Called to load a resource file for a different language
* @param newLang The new language
@@ -59,6 +80,9 @@ bool bMultiByte = false;
void ChangeLanguage(LANGUAGE newLang) {
Common::File f;
uint32 textLen = 0; // length of buffer
+
+ textLanguage = newLang;
+ sampleLanguage = newLang;
if (textBuffer) {
// free the previous buffer
@@ -69,9 +93,9 @@ void ChangeLanguage(LANGUAGE newLang) {
// Try and open the specified language file. If it fails, and the language
// isn't English, try falling back on opening 'english.txt' - some foreign
// language versions reused it rather than their proper filename
- if (!f.open(languageFiles[newLang])) {
- if ((newLang == TXT_ENGLISH) || !f.open(languageFiles[TXT_ENGLISH]))
- error("Cannot find file %s", languageFiles[newLang]);
+ if (!f.open(_vm->getTextFile(newLang))) {
+ if ((newLang == TXT_ENGLISH) || !f.open(_vm->getTextFile(TXT_ENGLISH)))
+ error(CANNOT_FIND_FILE, _vm->getTextFile(newLang));
}
// Check whether the file is compressed or not - for compressed files the
@@ -79,7 +103,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// identifier
textLen = f.readUint32LE();
if (f.ioFailed())
- error("File %s is corrupt", languageFiles[newLang]);
+ error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
if (textLen == CHUNK_STRING || textLen == CHUNK_MBSTRING) {
// the file is uncompressed
@@ -101,7 +125,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// load data
if (f.read(textBuffer, textLen) != textLen)
// file must be corrupt if we get to here
- error("File %s is corrupt", languageFiles[newLang]);
+ error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
// close the file
f.close();
@@ -111,19 +135,11 @@ void ChangeLanguage(LANGUAGE newLang) {
}
/**
- * Loads a string resource identified by id.
- * @param id identifier of string to be loaded
- * @param pBuffer points to buffer that receives the string
- * @param bufferMax maximum number of chars to be copied to the buffer
+ * FindStringBase
*/
-int LoadStringRes(int id, char *pBuffer, int bufferMax) {
-#ifdef DEBUG
- // For diagnostics
- newestString = id;
-#endif
-
+static byte *FindStringBase(int id) {
// base of string resource table
- uint8 *pText = textBuffer;
+ byte *pText = textBuffer;
// index into text resource file
uint32 index = 0;
@@ -134,20 +150,14 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
// number of strings to skip when in the correct chunk
int strSkip = id % STRINGS_PER_CHUNK;
- // length of string
- int len;
-
// skip to the correct chunk
while (chunkSkip-- != 0) {
// make sure chunk id is correct
assert(READ_LE_UINT32(pText + index) == CHUNK_STRING || READ_LE_UINT32(pText + index) == CHUNK_MBSTRING);
if (READ_LE_UINT32(pText + index + sizeof(uint32)) == 0) {
- // TEMPORARY DIRTY BODGE
- strcpy(pBuffer, "!! HIGH STRING !!");
-
// string does not exist
- return 0;
+ return NULL;
}
// get index to next chunk
@@ -163,17 +173,114 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
// skip to the correct string
while (strSkip-- != 0) {
// skip to next string
- pText += *pText + 1;
+
+ if (!TinselV2 || ((*pText & 0x80) == 0)) {
+ // Tinsel 1, or string of length < 128
+ pText += *pText + 1;
+ } else if (*pText == 0x80) {
+ // string of length 128 - 255
+ pText++; // skip control byte
+ pText += *pText + 1;
+ } else if (*pText == 0x90) {
+ // string of length 256 - 511
+ pText++; // skip control byte
+ pText += *pText + 1 + 256;
+ } else { // multiple string
+ int subCount;
+
+ subCount = *pText & ~0x80;
+ pText++; // skip control byte
+
+ // skip prior sub-strings
+ while (subCount--) {
+ // skip control byte, if there is one
+ if (*pText == 0x80) {
+ pText++;
+ pText += *pText + 1;
+ } else if (*pText == 0x90) {
+ pText++;
+ pText += *pText + 1 + 256;
+ } else
+ pText += *pText + 1;
+ }
+ }
}
- // get length of string
- len = *pText;
+ return pText;
+}
- if (len) {
+
+/**
+ * Loads a string resource identified by id.
+ * @param id identifier of string to be loaded
+ * @param pBuffer points to buffer that receives the string
+ * @param bufferMax maximum number of chars to be copied to the buffer
+ */
+int LoadStringResource(int id, int sub, char *pBuffer, int bufferMax) {
+ int len; // length of string
+
+ byte *pText = FindStringBase(id);
+
+ if (pText == NULL) {
+ strcpy(pBuffer, "!! HIGH STRING !!");
+ return 0;
+ }
+
+ if (!TinselV2 || ((*pText & 0x80) == 0)) {
+ // get length of string
+ len = *pText;
+ } else if (*pText == 0x80) {
+ // string of length 128 - 255
+ pText++; // skip control byte
+
+ // get length of string
+ len = *pText;
+ } else if (*pText == 0x90) {
+ // string of length 128 - 255
+ pText++; // skip control byte
+
+ // get length of string
+ len = *pText + 256;
+ } else {
+ // multiple string
+ pText++; // skip control byte
+
+ // skip prior sub-strings
+ while (sub--) {
+ // skip control byte, if there is one
+ if (*pText == 0x80) {
+ pText++;
+ pText += *pText + 1;
+ } else if (*pText == 0x90) {
+ pText++;
+ pText += *pText + 1 + 256;
+ } else
+ pText += *pText + 1;
+ }
+ // skip control byte, if there is one
+ if (*pText == 0x80) {
+ pText++;
+
+ // get length of string
+ len = *pText;
+ } else if (*pText == 0x90) {
+ pText++;
+
+ // get length of string
+ len = *pText + 256;
+ } else {
+ // get length of string
+ len = *pText;
+ }
+ }
+
+ if (len)
+ {
// the string exists
// copy the string to the buffer
- if (len < bufferMax) {
+ if (len < bufferMax)
+ {
memcpy(pBuffer, pText + 1, len);
// null terminate
@@ -199,6 +306,42 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
return 0;
}
+int LoadStringRes(int id, char *pBuffer, int bufferMax) {
+ return LoadStringResource(id, 0, pBuffer, bufferMax);
+}
+
+/**
+ * Loads a string resource identified by id
+ * @param id identifier of string to be loaded
+ * @param sub sub-string number
+ * @param pBuffer points to buffer that receives the string
+ * @param bufferMax maximum number of chars to be copied to the buffer
+ */
+int LoadSubString(int id, int sub, char *pBuffer, int bufferMax) {
+ return LoadStringResource(id, sub, pBuffer, bufferMax);
+}
+
+/**
+ * SubStringCount
+ * @param id Identifier of string to be tested
+ */
+int SubStringCount(int id) {
+ byte *pText;
+
+ pText = FindStringBase(id);
+
+ if (pText == NULL)
+ return 0;
+
+ if ((*pText & 0x80) == 0 || *pText == 0x80 || *pText == 0x90) {
+ // string of length < 128 or string of length 128 - 255
+ // or of length 256 - 511
+ return 1;
+ } else
+ return (*pText & ~0x80);
+}
+
+
void FreeTextBuffer() {
if (textBuffer) {
free(textBuffer);
@@ -206,4 +349,81 @@ void FreeTextBuffer() {
}
}
+/**
+ * Called from TINLIB.C from DeclareLanguage().
+ */
+
+void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
+ assert(language >= 0 && language < NUM_LANGUAGES);
+
+ languages[language].hDescription = hDescription;
+ languages[language].hFlagFilm = hFlagFilm;
+}
+
+/**
+ * Gets the current subtitles language
+ */
+LANGUAGE TextLanguage(void) {
+ return textLanguage;
+}
+
+/**
+ * Gets the current voice language
+ */
+LANGUAGE SampleLanguage(void) {
+ return sampleLanguage;
+}
+
+int NumberOfLanguages(void) {
+ int i, count;
+
+ for (i = 0, count = 0; i < NUM_LANGUAGES; i++) {
+ if (languages[i].bPresent)
+ count++;
+ }
+ return count;
+}
+
+LANGUAGE NextLanguage(LANGUAGE thisOne) {
+ int i;
+
+ for (i = thisOne+1; i < NUM_LANGUAGES; i++) {
+ if (languages[i].bPresent)
+ return (LANGUAGE)i;
+ }
+
+ for (i = 0; i < thisOne; i++) {
+ if (languages[i].bPresent)
+ return (LANGUAGE)i;
+ }
+
+ // No others!
+ return thisOne;
+}
+
+LANGUAGE PrevLanguage(LANGUAGE thisOne) {
+ int i;
+
+ for (i = thisOne-1; i >= 0; i--) {
+ if (languages[i].bPresent)
+ return (LANGUAGE)i;
+ }
+
+ for (i = NUM_LANGUAGES-1; i > thisOne; i--) {
+ if (languages[i].bPresent)
+ return (LANGUAGE)i;
+ }
+
+ // No others!
+ return thisOne;
+}
+
+SCNHANDLE LanguageDesc(LANGUAGE thisOne) {
+ return languages[thisOne].hDescription;
+}
+
+SCNHANDLE LanguageFlag(LANGUAGE thisOne) {
+ return languages[thisOne].hFlagFilm;
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/strres.h b/engines/tinsel/strres.h
index fac287492b..c996317b21 100644
--- a/engines/tinsel/strres.h
+++ b/engines/tinsel/strres.h
@@ -59,10 +59,43 @@ void ChangeLanguage(LANGUAGE newLang);
int LoadStringRes(int id, char *pBuffer, int bufferMax);
/**
+ * Loads a string resource identified by id
+ * @param id identifier of string to be loaded
+ * @param sub sub-string number
+ * @param pBuffer points to buffer that receives the string
+ * @param bufferMax maximum number of chars to be copied to the buffer
+ */
+int LoadSubString(int id, int sub, char *pBuffer, int bufferMax);
+
+int SubStringCount(int id); // identifier of string to be tested
+
+/**
* Frees the text buffer allocated from ChangeLanguage()
*/
void FreeTextBuffer();
+/**
+ * Called from TINLIB.C from DeclareLanguage().
+ */
+
+void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm);
+
+/**
+ * Gets the current subtitles language
+ */
+LANGUAGE TextLanguage(void);
+
+/**
+ * Gets the current voice language
+ */
+LANGUAGE SampleLanguage(void);
+
+int NumberOfLanguages(void);
+LANGUAGE NextLanguage(LANGUAGE thisOne);
+LANGUAGE PrevLanguage(LANGUAGE thisOne);
+SCNHANDLE LanguageDesc(LANGUAGE thisOne);
+SCNHANDLE LanguageFlag(LANGUAGE thisOne);
+
} // end of namespace Tinsel
#endif
diff --git a/engines/tinsel/sysvar.cpp b/engines/tinsel/sysvar.cpp
new file mode 100644
index 0000000000..7196e7597d
--- /dev/null
+++ b/engines/tinsel/sysvar.cpp
@@ -0,0 +1,218 @@
+/* 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$
+ *
+ * System variable handling.
+ */
+
+#include "tinsel/dw.h"
+#include "tinsel/graphics.h"
+#include "tinsel/dialogs.h"
+#include "tinsel/strres.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/tinsel.h"
+
+namespace Tinsel {
+
+// Return for SYS_Platform
+typedef enum { DOS_PC, WIN_PC, APPLE_MAC, SONY_PSX, SEGA_SATURN } platform;
+
+//----------------- GLOBAL GLOBAL DATA --------------------
+
+// To prevent assembler from needing to call SysVar()
+uint8 ghostColour;
+
+extern int NewestSavedGame(void);
+
+//----------------- LOCAL GLOBAL DATA --------------------
+
+static int systemVars[SV_TOPVALID] = {
+
+ INV_1, // Default inventory
+
+ 10, // Y-offset of Conversation(TOP)
+ 320, // Y-offset of Conversation(BOT)
+ 15, // Minimum distance from side
+ 10, // Minimum distance from top
+ 115, // Distance above actor
+ 10, // Distance below actor
+
+ 0, // Current language **READ ONLY**
+ 0, // Sample language **READ ONLY**
+ 0, // Current state **READ ONLY**
+ 0, // Saved Game Exists **READ ONLY**
+
+ true, // Should Conversation() wait for scroll? [TRUE]
+ true, // Should Talk()/Say() wait for scroll? [TRUE]
+
+ true, // Enable PointTag()
+ true, // Enable cursor with PrintCursor()
+
+ 100, // SV_SCROLL_XTRIGGER
+ 0, // SV_SCROLL_XDISTANCE
+ 16, // SV_SCROLL_XSPEED
+ 40, // SV_SCROLL_YTRIGGERTOP
+ 40, // SV_SCROLL_YTRIGGERBOT
+ 0, // SV_SCROLL_YDISTANCE
+ 16, // SV_SCROLL_YSPEED
+
+ 2, // Speech Delay
+ 2, // Music dim factor
+
+ 0, // if set, default actor's text colour gets poked in here
+
+ 0, // user 1
+ 0, // user 2
+ 0, // user 3
+ 0, // user 4
+ 0, // user 5
+ 0, // user 6
+
+ 0, // SYS_MinimumXoffset
+ 0, // SYS_MaximumXoffset
+ 0, // SYS_MinimumYoffset
+ 0, // SYS_MaximumYoffset
+
+ 0, // SYS_DefaultFxDimFactor
+ 0, // SYS_SceneFxDimFactor
+
+ 0x606060, // SYS_HighlightRGB
+ WIN_PC, // SYS_Platform,
+ 0, // SYS_Debug
+
+ 0, // ISV_DIVERT_ACTOR
+ false, // ISV_NO_BLOCKING
+
+ 0, // ISV_GHOST_ACTOR
+ 0, // ISV_GHOST_BASE
+ 0 // ISV_GHOST_COLOUR
+};
+
+static SCNHANDLE systemStrings[SS_MAX_VALID];
+
+//static bool bFlagNoBlocking = false;
+
+//----------------- FUNCTIONS --------------------------------
+
+/**
+ * Initialises the system variable list
+ */
+
+void InitSysVars() {
+ systemVars[SV_SCROLL_XDISTANCE] = SCREEN_WIDTH / 2;
+ systemVars[SV_SCROLL_YDISTANCE] = SCREEN_BOX_HEIGHT1 / 2;
+}
+
+/**
+ * SetSysVar
+ */
+
+void SetSysVar(int varId, int newValue) {
+ if (varId < 0 || varId >= SV_TOPVALID)
+ error("SetSystemVar(): out of range identifier");
+
+ switch (varId) {
+ case SV_LANGUAGE:
+ case SV_SAMPLE_LANGUAGE:
+ case SV_SUBTITLES:
+ case SV_SAVED_GAME_EXISTS:
+ case SYS_Platform:
+ case SYS_Debug:
+ error("SetSystemVar(): read only identifier");
+
+ default:
+ systemVars[varId] = newValue;
+
+ if (varId == ISV_GHOST_COLOUR) {
+ ghostColour = (uint8)newValue;
+ }
+ }
+}
+
+int SysVar(int varId) {
+ if (varId < 0 || varId >= SV_TOPVALID)
+ error("SystemVar(): out of range identifier");
+
+ switch (varId) {
+ case SV_LANGUAGE:
+ return TextLanguage();
+
+ case SV_SAMPLE_LANGUAGE:
+ return SampleLanguage();
+
+ case SV_SUBTITLES:
+ // FIXME: This isn't currently defined
+ return false;
+ //return bSubtitles;
+
+ case SV_SAVED_GAME_EXISTS:
+ return NewestSavedGame() != -1;
+
+ case SYS_Debug:
+ // FIXME: This isn't currently defined
+ return false;
+ //return bDebuggingAllowed;
+
+ default:
+ return systemVars[varId];
+ }
+}
+
+void SaveSysVars(int *pSv) {
+ memcpy(pSv, systemVars, sizeof(systemVars));
+}
+
+void RestoreSysVars(int *pSv) {
+ memcpy(systemVars, pSv, sizeof(systemVars));
+
+ ghostColour = (uint8)SysVar(ISV_GHOST_COLOUR);
+}
+
+void SetSysString(int number, SCNHANDLE hString) {
+ assert(number >= 0 && number < SS_MAX_VALID);
+
+ systemStrings[number] = hString;
+}
+
+SCNHANDLE SysString(int number) {
+ assert(number >= 0 && number < SS_MAX_VALID);
+
+ return systemStrings[number];
+}
+
+/**
+ * Gets the no blocking flag. Note that for convenience, the systemVars arrray entry is
+ * used even for Tinsel 1, which used a separate boolean variable
+ */
+bool GetNoBlocking(void) {
+ return SysVar(ISV_NO_BLOCKING);
+}
+
+/**
+ * Sets the no blocking flag. Note that for convenience, the systemVars arrray entry is
+ * used even for Tinsel 1, which used a separate boolean variable
+ */
+void SetNoBlocking(bool flag) {
+ SetSysVar(ISV_NO_BLOCKING, flag);
+}
+
+} // end of namespace Tinsel
diff --git a/engines/tinsel/sysvar.h b/engines/tinsel/sysvar.h
new file mode 100644
index 0000000000..869a99caa3
--- /dev/null
+++ b/engines/tinsel/sysvar.h
@@ -0,0 +1,149 @@
+/* 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$
+ *
+ * System variable handling.
+ */
+
+#ifndef TINSEL_SYSVAR_H // prevent multiple includes
+#define TINSEL_SYSVAR_H
+
+namespace Tinsel {
+
+typedef enum { SV_DEFAULT_INV,
+
+ SV_CONV_TOPY, // Y-offset of Conversation(TOP)
+ SV_CONV_BOTY, // Y-offset of Conversation(BOT)
+ SV_CONV_MINX, // Minimum distance from side
+ SV_CONV_MINY, // Minimum distance from top
+ SV_CONV_ABOVE_Y, // Distance above actor
+ SV_CONV_BELOW_Y, // Distance below actor
+
+ SV_LANGUAGE,
+ SV_SAMPLE_LANGUAGE,
+ SV_SUBTITLES,
+ SV_SAVED_GAME_EXISTS,
+
+ SV_CONVERSATIONWAITS, // } Do they wait for
+ SV_SPEECHWAITS, // } scrolls to complete?
+
+ SV_ENABLEPOINTTAG, // Enable PointTag()
+ SV_ENABLEPRINTCURSOR, // Enable cursor with PrintCursor()
+
+ SV_SCROLL_XTRIGGER, // }
+ SV_SCROLL_XDISTANCE, // }
+ SV_SCROLL_XSPEED, // } Scroll parameters!
+ SV_SCROLL_YTRIGGERTOP, // }
+ SV_SCROLL_YTRIGGERBOT, // }
+ SV_SCROLL_YDISTANCE, // }
+ SV_SCROLL_YSPEED, // }
+
+ SV_SPEECHDELAY, // Delay 'twixt text/animation and sample
+ SV_MUSICDIMFACTOR, // dimVolume = volume - volume/SV_MDF
+
+ SV_TAGCOLOUR, // if set, default actor's text colour gets poked in here
+
+ SV_USER1,
+ SV_USER2,
+ SV_USER3,
+ SV_USER4,
+ SV_USER5,
+ SV_USER6,
+
+ SV_MinimumXoffset,
+ SV_MaximumXoffset,
+ SV_MinimumYoffset,
+ SV_MaximumYoffset,
+ // dimVolume = volume - volume/DF
+ SYS_DefaultFxDimFactor, // To this at start of scene
+ SYS_SceneFxDimFactor, // Alter within scene
+
+ SYS_HighlightRGB,
+ SYS_Platform, // Hardware platform **READ ONLY**
+ SYS_Debug, // TRUE for debug build/'cheat'**READ ONLY**
+
+ ISV_DIVERT_ACTOR,
+ ISV_NO_BLOCKING,
+ ISV_GHOST_ACTOR,
+ ISV_GHOST_BASE,
+ ISV_GHOST_COLOUR,
+
+
+ SV_TOPVALID } SYSVARS;
+
+typedef enum {
+
+ // Main Menu
+ SS_LOAD_OPTION, //
+ SS_SAVE_OPTION, //
+ SS_RESTART_OPTION, //
+ SS_SOUND_OPTION, //
+ SS_CONTROL_OPTION, //
+ SS_SUBTITLES_OPTION, //
+ SS_QUIT_OPTION, //
+ SS_RESUME_OPTION, //
+
+ SS_LOAD_HEADING,
+ SS_SAVE_HEADING,
+ SS_RESTART_HEADING,
+ SS_QUIT_HEADING,
+
+ SS_MVOL_SLIDER,
+ SS_SVOL_SLIDER,
+ SS_VVOL_SLIDER,
+
+ SS_DCLICK_SLIDER,
+ SS_DCLICK_TEST,
+ SS_SWAP_TOGGLE,
+
+ SS_TSPEED_SLIDER,
+ SS_STITLE_TOGGLE,
+
+ SS_HOPPER1, // Hopper scene menu heading
+
+ SS_SOUND_HEADING,
+ SS_CONTROLS_HEADING,
+ SS_LANGUAGE_SELECT,
+
+ SS_MAX_VALID
+} BOLLOX;
+
+void InitSysVars();
+
+void SetSysVar(int varId, int newValue);
+
+int SysVar(int varId);
+
+void SaveSysVars(int *pSv);
+void RestoreSysVars(int *pSv);
+
+void SetSysString(int number, SCNHANDLE hString);
+
+SCNHANDLE SysString(int number);
+
+bool GetNoBlocking(void);
+
+void SetNoBlocking(bool flag);
+
+} // end of namespace Tinsel
+
+#endif
diff --git a/engines/tinsel/text.cpp b/engines/tinsel/text.cpp
index dab97f7fdf..7e36e5236f 100644
--- a/engines/tinsel/text.cpp
+++ b/engines/tinsel/text.cpp
@@ -105,9 +105,10 @@ int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
* @param yPos Y position of string
* @param hFont Which font to use
* @param mode Mode flags for the string
+ * @param sleepTime Sleep time between each character (if non-zero)
*/
-OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour, int xPos, int yPos,
- SCNHANDLE hFont, int mode) {
+OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
+ int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
int xJustify; // x position of text after justification
int yOffset; // offset to next line of text
OBJECT *pFirst; // head of multi-object text list
@@ -130,7 +131,7 @@ OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour, int xPos, int yPos
pImg = (const IMAGE *)LockMem(FROM_LE_32(pFont->fontDef[(int)'W']));
// get height of capital W for offset to next line
- yOffset = FROM_LE_16(pImg->imgHeight);
+ yOffset = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK;
while (*szStr) {
// x justify the text according to the mode flags
@@ -175,7 +176,7 @@ OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour, int xPos, int yPos
// fill in character object
pChar->hImg = hImg; // image def
pChar->width = FROM_LE_16(pImg->imgWidth); // width of chars bitmap
- pChar->height = FROM_LE_16(pImg->imgHeight); // height of chars bitmap
+ pChar->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK; // height of chars bitmap
pChar->hBits = FROM_LE_32(pImg->hImgBits); // bitmap
// check for absolute positioning
diff --git a/engines/tinsel/text.h b/engines/tinsel/text.h
index 78998831a1..0fcc965ff9 100644
--- a/engines/tinsel/text.h
+++ b/engines/tinsel/text.h
@@ -27,6 +27,7 @@
#ifndef TINSEL_TEXT_H // prevent multiple includes
#define TINSEL_TEXT_H
+#include "tinsel/coroutine.h"
#include "tinsel/object.h" // object manager defines
namespace Tinsel {
@@ -42,6 +43,10 @@ enum {
/** maximum number of characters in a font */
#define MAX_FONT_CHARS 256
+#define C16_240 0x4000
+#define C16_224 0x8000
+#define C16_MAP 0xC000
+#define C16_FLAG_MASK (C16_240 | C16_224 | C16_MAP)
#include "common/pack-start.h" // START STRUCT PACKING
@@ -80,14 +85,21 @@ struct TEXTOUT {
|* Text Function Prototypes *|
\*----------------------------------------------------------------------*/
-OBJECT *ObjectTextOut( // output a string of text
- OBJECT *pList, // object list to add text to
- char *szStr, // string to output
- int colour, // colour for monochrome text
- int xPos, // x position of string
- int yPos, // y position of string
- SCNHANDLE hFont, // which font to use
- int mode); // mode flags for the string
+/**
+ * Main text outputting routine. If a object list is specified a
+ * multi-object is created for the whole text and a pointer to the head
+ * of the list is returned.
+ * @param pList object list to add text to
+ * @param szStr string to output
+ * @param colour colour for monochrome text
+ * @param xPos x position of string
+ * @param yPos y position of string
+ * @param hFont which font to use
+ * @param mode mode flags for the string
+ * @param sleepTime Sleep time between each character (if non-zero)
+ */
+OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
+ int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime = 0);
OBJECT *ObjectTextOutIndirect( // output a string of text
TEXTOUT *pText); // pointer to TextOut struct with all parameters
diff --git a/engines/tinsel/timers.cpp b/engines/tinsel/timers.cpp
index c7b9d3708b..92abf4c028 100644
--- a/engines/tinsel/timers.cpp
+++ b/engines/tinsel/timers.cpp
@@ -151,7 +151,7 @@ void FettleTimers(void) {
/**
* Start a timer up.
*/
-void DwSetTimer(int num, int sval, bool up, bool frame) {
+void StartTimer(int num, int sval, bool up, bool frame) {
TIMER *pt;
assert(num); // zero is not allowed as a timer number
diff --git a/engines/tinsel/timers.h b/engines/tinsel/timers.h
index 75eb87ee2b..2260074f1d 100644
--- a/engines/tinsel/timers.h
+++ b/engines/tinsel/timers.h
@@ -44,7 +44,7 @@ void syncTimerInfo(Serializer &s);
void FettleTimers(void);
-void DwSetTimer(int num, int sval, bool up, bool frame);
+void StartTimer(int num, int sval, bool up, bool frame);
int Timer(int num);
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 07c1b22b2a..2313f343e8 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -36,12 +36,16 @@
#include "tinsel/config.h"
#include "tinsel/coroutine.h"
#include "tinsel/cursor.h"
+#include "tinsel/drives.h"
#include "tinsel/dw.h"
+#include "tinsel/events.h"
+#include "tinsel/faders.h"
#include "tinsel/film.h"
#include "tinsel/font.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
-#include "tinsel/inventory.h"
+#include "tinsel/dialogs.h"
+#include "tinsel/mareels.h"
#include "tinsel/move.h"
#include "tinsel/multiobj.h"
#include "tinsel/music.h"
@@ -49,6 +53,7 @@
#include "tinsel/palette.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h"
+#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/savescn.h"
@@ -57,6 +62,7 @@
#include "tinsel/scroll.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
+#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h"
@@ -72,49 +78,59 @@ namespace Tinsel {
extern bool bRestart; // restart flag - set to restart the game
extern bool bHasRestarted; // Set after a restart
+// In PCODE.CPP
+extern bool bNoPause;
+
// In DOS_MAIN.C
// TODO/FIXME: From dos_main.c: "Only used on PSX so far"
int clRunMode = 0;
//----------------- EXTERNAL FUNCTIONS ---------------------
-// in BG.C
-extern void startupBackground(SCNHANDLE bfilm);
+// in BG.CPP
extern void ChangePalette(SCNHANDLE hPal);
-extern int BackgroundWidth(void);
-extern int BackgroundHeight(void);
+extern int BgWidth(void);
+extern int BgHeight(void);
-// in DOS_DW.C
-extern void SetHookScene(SCNHANDLE scene, int entrance, int transition);
-extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
-extern void UnHookScene(void);
-extern void SuspendHook(void);
-extern void UnSuspendHook(void);
+// in BMV.CPP
+void PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape);
+bool MoviePlaying(void);
+void AbortMovie(void);
-// in PDISPLAY.C
+// in PDISPLAY.CPP
extern void EnableTags(void);
extern void DisableTags(void);
bool DisableTagsIfEnabled(void);
extern void setshowstring(void);
-// in PLAY.C
-extern void playFilm(SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact, bool escOn, int myescEvent, bool bTop);
-extern void playFilmc(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact, bool escOn, int myescEvent, bool bTop);
+// in SAVELOAD.CPP
+extern int NewestSavedGame(void);
-// in SCENE.C
+// in SCENE.CPP
extern void setshowpos(void);
+// in TINSEL.CPP
+extern void SetCdChangeScene(SCNHANDLE hScene);
+extern void SetHookScene(SCNHANDLE scene, int entrance, int transition);
+extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
+extern void UnHookScene(void);
+extern void SuspendHook(void);
+extern void UnSuspendHook(void);
+
#ifdef BODGE
-// In DOS_HAND.C
+// In HANDLE.CPP
bool ValidHandle(SCNHANDLE offset);
-// In SCENE.C
+// In SCENE.CPP
SCNHANDLE GetSceneHandle(void);
#endif
//----------------- GLOBAL GLOBAL DATA --------------------
-bool bEnableF1;
+bool bEnableMenu;
+
+static bool bInstantScroll = false;
+static bool bEscapedCdPlay = false;
//----------------- LOCAL DEFINES --------------------
@@ -125,70 +141,257 @@ bool bEnableF1;
|* Library Procedure and Function codes *|
\*----------------------------------------------------------------------*/
-enum LIB_CODE {
- ACTORATTR = 0, ACTORDIRECTION, ACTORREF, ACTORSCALE, ACTORXPOS = 4,
- ACTORYPOS, ADDICON, ADDINV1, ADDINV2, ADDOPENINV, AUXSCALE = 10,
- BACKGROUND, CAMERA, CLOSEINVENTORY, CONTROL, CONVERSATION = 15,
- CONVICON, CURSORXPOS, CURSORYPOS, DEC_CONVW, DEC_CURSOR = 20,
- DEC_INV1, DEC_INV2, DEC_INVW, DEC_LEAD, DEC_TAGFONT = 25,
- DEC_TALKFONT, DELICON, DELINV, EFFECTACTOR, ESCAPE, EVENT = 31,
- GETINVLIMIT, HELDOBJECT, HIDE, ININVENTORY, INVDEPICT = 36,
- INVENTORY, KILLACTOR, KILLBLOCK, KILLEXIT, KILLTAG, LEFTOFFSET = 42,
- MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, PAUSE = 48,
- PLAY, PLAYMIDI, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ = 54,
- PRINTTAG, RANDOM, RESTORE_SCENE, SAVE_SCENE, SCALINGREELS = 59,
- SCANICON, SCROLL, SETACTOR, SETBLOCK, SETEXIT, SETINVLIMIT = 65,
- SETPALETTE, SETTAG, SETTIMER, SHOWPOS, SHOWSTRING, SPLAY = 71,
- STAND, STANDTAG, STOP, SWALK, TAGACTOR, TALK, TALKATTR, TIMER = 79,
- TOPOFFSET, TOPPLAY, TOPWINDOW, UNTAGACTOR, VIBRATE, WAITKEY = 85,
- WAITTIME, WALK, WALKED, WALKINGACTOR, WALKPOLY, WALKTAG = 91,
- WHICHINVENTORY = 92,
- ACTORSON, CUTSCENE, HOOKSCENE, IDLETIME, RESETIDLETIME = 97,
- TALKAT, UNHOOKSCENE, WAITFRAME, DEC_CSTRINGS, STOPMIDI, STOPSAMPLE = 103,
- TALKATS = 104,
- DEC_FLAGS, FADEMIDI, CLEARHOOKSCENE, SETINVSIZE, INWHICHINV = 109,
- NOBLOCKING, SAMPLEPLAYING, TRYPLAYSAMPLE, ENABLEF1 = 113,
- RESTARTGAME, QUITGAME, FRAMEGRAB, PLAYRTF, CDPLAY, CDLOAD = 119,
- HASRESTARTED, RESTORE_CUT, RUNMODE, SUBTITLES, SETLANGUAGE = 124
+enum MASTER_LIB_CODES {
+ ACTORATTR, ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY, ACTORREF,
+ ACTORRGB, ACTORSCALE, ACTORSON, ACTORXPOS, ACTORYPOS, ADDHIGHLIGHT,
+ ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC, AUXSCALE, BACKGROUND, BLOCKING,
+ CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT, CALLPROCESS, CALLSCENE, CALLTAG,
+ CAMERA, CDCHANGESCENE, CDDOCHANGE, CDENDACTOR, CDLOAD, CDPLAY, CLEARHOOKSCENE,
+ CLOSEINVENTORY, CONTROL, CONVERSATION, CONVTOPIC, CURSOR, CURSORXPOS, CURSORYPOS,
+ CUTSCENE, DECCONVW, DECCSTRINGS, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
+ DECLARELANGUAGE, DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELICON,
+ DELINV, DELTOPIC, DIMMUSIC, DROP, DROPEVERYTHING, DROPOUT, EFFECTACTOR, ENABLEMENU,
+ ENDACTOR, ESCAPE, ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEMIDI,
+ FADEOUT, FRAMEGRAB, FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE, HAILSCENE,
+ HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
+ HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, ININVENTORY, INSTANTSCROLL, INVDEPICT,
+ INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLBLOCK, KILLEXIT, KILLGLOBALPROCESS,
+ KILLPROCESS, KILLTAG, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
+ NOBLOCKING, NOPAUSE, NOSCROLL, OBJECTHELD, OFFSET, OTHEROBJECT, PAUSE, PLAY, PLAYMIDI,
+ PLAYMOVIE, PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS,
+ POSTOBJECT, POSTPROCESS, POSTTAG, PREPARESCENE, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
+ QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE, RESTORE_CUT,
+ RESUMELASTGAME, RUNMODE, SAMPLEPLAYING, SAVESCENE, SAY, SAYAT, SCALINGREELS,
+ SCANICON, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
+ SENDOBJECT, SENDPROCESS, SENDTAG, SETACTOR, SETBLOCK, SETBRIGHTNESS, SETEXIT, SETINVLIMIT,
+ SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMREEL, SETSYSTEMSTRING, SETSYSTEMVAR,
+ SETTAG, SETTIMER, SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWMENU, SHOWPATH,
+ SHOWPOS, SHOWREFER, SHOWSTRING, SHOWTAG, SPLAY, STAND, STANDTAG, STARTGLOBALPROCESS,
+ STARTPROCESS, STARTTIMER, STOPMIDI, STOPSAMPLE, STOPWALK, SUBTITLES, SWALK, SWALKZ,
+ SYSTEMVAR, TAGACTOR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT,
+ TALKATS, TALKATTR, TALKPALETTEINDEX, TALKRGB, TALKVIA, TEMPTAGFONT, TEMPTALKFONT,
+ THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
+ TRYPLAYSAMPLE, UNDIMMUSIC, UNHOOKSCENE, UNTAGACTOR, VIBRATE, WAITFRAME, WAITKEY,
+ WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
+ WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY, ZZZZZZ,
+ HIGHEST_LIBCODE
};
+const MASTER_LIB_CODES DW1_CODES[] = {
+ ACTORATTR, ACTORDIRECTION, ACTORREF, ACTORSCALE, ACTORXPOS,
+ ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, ADDOPENINV, AUXSCALE,
+ BACKGROUND, CAMERA, CLOSEINVENTORY, CONTROL, CONVERSATION,
+ CONVTOPIC, CURSORXPOS, CURSORYPOS, DECCONVW, DECCURSOR,
+ DECINV1, DECINV2, DECINVW, DECLEAD, DECTAGFONT,
+ DECTALKFONT, DELICON, DELINV, EFFECTACTOR, ESCAPE, EVENT,
+ GETINVLIMIT, HELDOBJECT, HIDEACTOR, ININVENTORY, INVDEPICT,
+ INVENTORY, KILLACTOR, KILLBLOCK, KILLEXIT, KILLTAG, SCREENXPOS,
+ MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, PAUSE,
+ PLAY, PLAYMIDI, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ,
+ PRINTTAG, RANDOM, RESTORESCENE, SAVESCENE, SCALINGREELS,
+ SCANICON, SCROLL, SETACTOR, SETBLOCK, SETEXIT, SETINVLIMIT,
+ SETPALETTE, SETTAG, SETTIMER, SHOWPOS, SHOWSTRING, SPLAY,
+ STAND, STANDTAG, STOPWALK, SWALK, TAGACTOR, TALK, TALKATTR, TIMER,
+ SCREENYPOS, TOPPLAY, TOPWINDOW, UNTAGACTOR, VIBRATE, WAITKEY,
+ WAITTIME, WALK, WALKED, WALKINGACTOR, WALKPOLY, WALKTAG,
+ WHICHINVENTORY, ACTORSON, CUTSCENE, HOOKSCENE, IDLETIME,
+ RESETIDLETIME, TALKAT, UNHOOKSCENE, WAITFRAME, DECCSTRINGS,
+ STOPMIDI, STOPSAMPLE, TALKATS, DECFLAGS, FADEMIDI,
+ CLEARHOOKSCENE, SETINVSIZE, INWHICHINV, NOBLOCKING,
+ SAMPLEPLAYING, TRYPLAYSAMPLE, ENABLEMENU, RESTARTGAME, QUITGAME,
+ FRAMEGRAB, PLAYRTF, CDPLAY, CDLOAD, HASRESTARTED, RESTORE_CUT,
+ RUNMODE, SUBTITLES, SETLANGUAGE,
+ HIGHEST_LIBCODE
+};
+const MASTER_LIB_CODES DW2_CODES[] = {
+ ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
+ ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
+ ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
+ BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
+ CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
+ CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
+ CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
+ DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
+ DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
+ DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
+ ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
+ FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE,
+ HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT,
+ HIDEPATH, HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME,
+ INSTANTSCROLL, INVENTORY, INVPLAY, INWHICHINV, KILLACTOR,
+ KILLGLOBALPROCESS, KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG,
+ MOVETAGTO, NEWSCENE, NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET,
+ OTHEROBJECT, PAUSE, PLAY, PLAYMUSIC, PLAYRTF, PLAYSAMPLE,
+ POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS, POSTOBJECT,
+ POSTPROCESS, POSTTAG, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
+ QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE,
+ RUNMODE, SAVESCENE, SAY, SAYAT, SCALINGREELS, SCREENXPOS,
+ SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
+ SENDOBJECT, SENDPROCESS, SENDTAG, SETBRIGHTNESS, SETINVLIMIT,
+ SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMSTRING, SETSYSTEMVAR,
+ SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWPATH, SHOWREFER,
+ SHOWTAG, STAND, STANDTAG, STARTGLOBALPROCESS, STARTPROCESS,
+ STARTTIMER, STOPWALK, SUBTITLES, SWALK, SYSTEMVAR, TAGTAGXPOS,
+ TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT, TALKPALETTEINDEX,
+ TALKRGB, TALKVIA, THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY,
+ TOPWINDOW, TRANSLUCENTINDEX, UNDIMMUSIC, UNHOOKSCENE, WAITFRAME,
+ WAITKEY, WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG,
+ WALKINGACTOR, WALKPOLY, WALKTAG, WALKXPOS, WALKYPOS, WHICHCD,
+ WHICHINVENTORY, ZZZZZZ, SWALKZ, DROPEVERYTHING, BLOCKING, STOPSAMPLE,
+ CDENDACTOR, DECLARELANGUAGE, RESUMELASTGAME, SHOWMENU, TEMPTALKFONT,
+ TEMPTAGFONT, PLAYMOVIE, HAILSCENE, SETSYSTEMREEL,
+ HIGHEST_LIBCODE
+};
//----------------- LOCAL GLOBAL DATA --------------------
// Saved cursor co-ordinates for control(on) to restore cursor position
// as it was at control(off).
-// They are global so that movecursor(..) has a net effect if it
+// They are global so that MoveCursor(..) has a net effect if it
// precedes control(on).
static int controlX = 0, controlY = 0;
-static int offtype = 0; // used by control()
-static uint32 lastValue = 0; // used by dw_random()
-static int scrollCount = 0; // used by scroll()
+static int offtype = 0; // used by Control()
+static uint32 lastValue = 0; // used by RandomFn()
+static int scrollNumber = 0; // used by scroll()
-static bool NotPointedRunning = false; // Used in printobj and printobjPointed
+static bool bNotPointedRunning = false; // Used in Printobj and PrintObjPointed
static COLORREF s_talkfontColor = 0;
//----------------- FORWARD REFERENCES --------------------
-void resetidletime(void);
-void stopmidi(void);
-void stopsample(void);
-void walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath, bool escOn, int myescTime);
+static int HeldObject(void);
+void Offset(EXTREME extreme, int x, int y);
+static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape);
+void ResetIdleTime(void);
+static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result);
+static void StandTag(int actor, HPOLYGON hp);
+void StopMidiFn(void);
+void StopSample(int sample = -1);
+static void StopWalk(int actor);
+static void WaitScroll(CORO_PARAM, int myescEvent);
+void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath,
+ int zOverride, bool escOn, int myescTime);
+
+//----------------- SUPPORT FUNCTIONS --------------------
+
+/**
+ * For Scroll() and Offset(), work out top left for a
+ * given screen position.
+ */
+static void DecodeExtreme(EXTREME extreme, int *px, int *py) {
+ int Loffset, Toffset;
+
+ PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+
+ switch (extreme) {
+ case EX_BOTTOM:
+ *px = Loffset;
+ *py = BgHeight() - SCREEN_HEIGHT;
+ break;
+ case EX_BOTTOMLEFT:
+ *px = 0;
+ *py = BgHeight() - SCREEN_HEIGHT;
+ break;
+ case EX_BOTTOMRIGHT:
+ *px = BgWidth() - SCREEN_WIDTH;
+ *py = BgHeight() - SCREEN_HEIGHT;
+ break;
+ case EX_LEFT:
+ *px = 0;
+ *py = Toffset;
+ break;
+ case EX_RIGHT:
+ *px = BgWidth() - SCREEN_WIDTH;
+ *py = Toffset;
+ break;
+ case EX_TOP:
+ *px = Loffset;
+ *py = 0;
+ break;
+ case EX_TOPLEFT:
+ *px = *py = 0;
+ break;
+ case EX_TOPRIGHT:
+ *px = BgWidth() - SCREEN_WIDTH;
+ *py = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static void KillSelf(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+struct SCROLL_MONITOR {
+ int x;
+ int y;
+ int thisScroll;
+ int myEscape;
+};
+typedef SCROLL_MONITOR *PSCROLL_MONITOR;
+
+/**
+ * Monitor a scrolling, allowing Escape to interrupt it
+ */
+static void ScrollMonitorProcess(CORO_PARAM, const void *param) {
+ int Loffset, Toffset;
+ const SCROLL_MONITOR *psm = (const SCROLL_MONITOR *)param;;
+
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ do {
+ CORO_SLEEP(1);
+
+ // give up if have been superseded
+ if (psm->thisScroll != scrollNumber)
+ break;
+
+ // If ESCAPE is pressed...
+ if (psm->myEscape != GetEscEvents())
+ {
+ // Instant completion!
+ Offset(EX_USEXY, psm->x, psm->y);
+ break;
+ }
+
+ PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+
+ } while(Loffset != psm->x || Toffset != psm->y);
+
+ CORO_END_CODE;
+}
/**
* NOT A LIBRARY FUNCTION
*
- * Poke supplied colours into the DAC queue.
+ * Poke supplied colour into the DAC queue.
*/
-static void setTextPal(COLORREF col) {
+void SetTextPal(COLORREF col) {
s_talkfontColor = col;
- UpdateDACqueue(TALKFONT_COL, 1, &s_talkfontColor);
+ SetTalkColourRef(col);
+ UpdateDACqueue(TalkColour(), 1, &s_talkfontColor);
}
-
+/**
+ * Work out a time depending on length of string and
+ * subtitle speed modification.
+ */
static int TextTime(char *pTstring) {
if (isJapanMode())
return JAP_TEXT_TIME;
@@ -198,45 +401,148 @@ static int TextTime(char *pTstring) {
return strlen(pTstring) + ONE_SECOND + (speedText * 5 * ONE_SECOND) / 100;
}
-/*--------------------------------------------------------------------------*/
+/**
+ * KeepOnScreen
+ */
+void KeepOnScreen(POBJECT pText, int *pTextX, int *pTextY) {
+ int shift;
+
+ // Not off the left
+ shift = MultiLeftmost(pText);
+ if (shift < 0) {
+ MultiMoveRelXY(pText, - shift, 0);
+ *pTextX -= shift;
+ }
+
+ // Not off the right
+ shift = MultiRightmost(pText);
+ if (shift > SCREEN_WIDTH) {
+ MultiMoveRelXY(pText, SCREEN_WIDTH - shift, 0);
+ *pTextX += SCREEN_WIDTH - shift;
+ }
+
+ // Not off the top
+ shift = MultiHighest(pText);
+ if (shift < 0) {
+ MultiMoveRelXY(pText, 0, - shift);
+ *pTextY -= shift;
+ }
+
+ // Not off the bottom
+ shift = MultiLowest(pText);
+ if (shift > SCREEN_BOX_HEIGHT2) {
+ MultiMoveRelXY(pText, 0, SCREEN_BOX_HEIGHT2 - shift);
+ *pTextX += SCREEN_WIDTH - shift;
+ }
+}
+
+/**
+ * Waits until the specified process is finished
+ */
+static void FinishWaiting(CORO_PARAM, const INT_CONTEXT *pic, bool *result = NULL) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ while (pic->resumeCode == RES_WAITING)
+ CORO_SLEEP(1);
+
+ if (result)
+ *result = pic->resumeCode == RES_FINISHED;
+ CORO_END_CODE;
+}
+
+void TinGetVersion(WHICH_VER which, char *buffer, int length) {
+
+ if (length > VER_LEN)
+ length = VER_LEN;
+
+ char *cptr = (char *)FindChunk(MASTER_SCNHANDLE, CHUNK_TIME_STAMPS);
+
+ switch (which) {
+ case VER_GLITTER:
+ memcpy(buffer, cptr, length);
+ break;
+
+ case VER_COMPILE:
+ memcpy(buffer, cptr + VER_LEN, length);
+ break;
+ }
+}
+/********************************************************************\
+|***** Library functions *****|
+\********************************************************************/
/**
* Set actor's attributes.
* - currently only the text colour.
*/
-void actorattr(int actor, int r1, int g1, int b1) {
+static void ActorAttr(int actor, int r1, int g1, int b1) {
storeActorAttr(actor, r1, g1, b1);
}
/**
- * Return the actor's direction.
+ * Behave as if actor has walked into a polygon with given brughtness.
*/
-int actordirection(int actor) {
- PMACTOR pActor;
+void ActorBrightness(int actor, int brightness) {
+ PMOVER pMover = GetMover(actor);
- pActor = GetMover(actor);
- assert(pActor != NULL); // not a moving actor
+ assert(pMover != NULL);
+ assert(brightness >= 0 && brightness <= 10);
- return (int)GetMActorDirection(pActor);
+ MoverBrightness(pMover, brightness);
}
/**
- * Return the actor's scale.
+ * Return a moving actor's current direction.
+ */
+static int ActorDirection(int actor) {
+ PMOVER pMover = GetMover(actor);
+ assert(pMover);
+
+ return (int)GetMoverDirection(pMover);
+}
+
+/**
+ * Set actor's palette details for path brightnesses
+ */
+void ActorPalette(int actor, int startColour, int length) {
+ PMOVER pMover = GetMover(actor);
+ assert(pMover);
+
+ StoreMoverPalette(pMover, startColour, length);
+}
+
+/**
+ * Set actor's Z-factor.
*/
-int actorscale(int actor) {
- PMACTOR pActor;
+static void ActorPriority(int actor, int zFactor) {
+ SetActorZfactor(actor, zFactor);
+}
- pActor = GetMover(actor);
- assert(pActor != NULL); // not a moving actor
+/**
+ * Set actor's text colour.
+ */
+static void ActorRGB(int actor, COLORREF colour) {
+ SetActorRGB(actor, colour);
+}
- return (int)GetMActorScale(pActor);
+/**
+ * Return the actor's scale.
+ */
+static int ActorScale(int actor) {
+ PMOVER pMover = GetMover(actor);
+ assert(pMover);
+
+ return (int)GetMoverScale(pMover);
}
/**
* Returns the x or y position of an actor.
*/
-int actorpos(int xory, int actor) {
+static int ActorPos(int xory, int actor) {
int x, y;
GetActorPos(actor, &x, &y);
@@ -246,22 +552,23 @@ int actorpos(int xory, int actor) {
/**
* Make all actors alive at the start of each scene.
*/
-void actorson(void) {
+static void ActorsOn(void) {
setactorson();
}
/**
* Adds an icon to the conversation window.
*/
-void addicon(int icon) {
+static void AddTopic(int icon) {
AddToInventory(INV_CONV, icon, false);
}
/**
* Place the object in inventory 1 or 2.
*/
-void addinv(int invno, int object) {
- assert(invno == INV_1 || invno == INV_2 || invno == INV_OPEN); // illegal inventory number
+static void AddInv(int invno, int object) {
+ // illegal inventory number
+ assert(invno == INV_1 || invno == INV_2 || invno == INV_OPEN || invno == INV_DEFAULT);
AddToInventory(invno, object, false);
}
@@ -269,58 +576,104 @@ void addinv(int invno, int object) {
/**
* Define an actor's walk and stand reels for an auxilliary scale.
*/
-void auxscale(int actor, int scale, SCNHANDLE *rp) {
- PMACTOR pActor;
-
- pActor = GetMover(actor);
- assert(pActor); // Can't set aux scale for a non-moving actor
+static void AuxScale(int actor, int scale, SCNHANDLE *rp) {
+ PMOVER pMover = GetMover(actor);
+ assert(pMover);
int j;
for (j = 0; j < 4; ++j)
- pActor->WalkReels[scale-1][j] = *rp++;
+ pMover->walkReels[scale-1][j] = *rp++;
for (j = 0; j < 4; ++j)
- pActor->StandReels[scale-1][j] = *rp++;
+ pMover->standReels[scale-1][j] = *rp++;
for (j = 0; j < 4; ++j)
- pActor->TalkReels[scale-1][j] = *rp++;
+ pMover->talkReels[scale-1][j] = *rp++;
}
/**
* Defines the background image for a scene.
*/
-void background(SCNHANDLE bfilm) {
- startupBackground(bfilm);
+static void Background(CORO_PARAM, SCNHANDLE bfilm) {
+ StartupBackground(coroParam, bfilm);
+}
+
+/**
+ * Disable dynamic blocking for current scene.
+ */
+void Blocking(bool onOrOff) {
+ SetSysVar(ISV_NO_BLOCKING, !onOrOff);
}
/**
* Sets focus of the scroll process.
*/
-void camera(int actor) {
+static void Camera(int actor) {
ScrollFocus(actor);
}
/**
+ * Sets the CD Change Scene
+ */
+
+static void CdChangeScene(SCNHANDLE hScene) {
+ SetCdChangeScene(hScene);
+}
+
+/**
+ * CdDoChange
+ */
+void CdDoChange(CORO_PARAM) {
+ if (!GotoCD())
+ return;
+
+ CdCD(coroParam);
+ CdHasChanged();
+}
+
+/**
+ * CdEndActor("actor")
+ */
+void CdEndActor(int actor, int myEscape) {
+ PMOVER pMover; // for if it's a moving actor
+
+ // Only do it if escaped!
+ if (myEscape && myEscape != GetEscEvents()) {
+ // End current graphic
+ dwEndActor(actor);
+
+ // un-hide movers
+ pMover = GetMover(actor);
+ if (pMover)
+ UnHideMover(pMover);
+ }
+}
+
+/**
* A CDPLAY() is imminent.
*/
-void cdload(SCNHANDLE start, SCNHANDLE next) {
+static void CDload(SCNHANDLE start, SCNHANDLE next, int myEscape) {
assert(start && next && start != next); // cdload() fault
-// TODO/FIXME
-// LoadExtraGraphData(start, next);
+ if (TinselV2) {
+ if (myEscape && myEscape != GetEscEvents()) {
+ bEscapedCdPlay = true;
+ return;
+ }
+
+ LoadExtraGraphData(start, next);
+ }
}
/**
* Clear the hooked scene (if any)
*/
-
-void clearhookscene() {
- SetHookScene(0, 0, 0);
+static void ClearHookScene() {
+ SetHookScene(0, 0, TRANS_DEF);
}
/**
* Guess what.
*/
-
-void closeinventory(void) {
+static void CloseInventory(void) {
KillInventory();
}
@@ -328,9 +681,29 @@ void closeinventory(void) {
* Turn off cursor and take control from player - and variations on the theme.
* OR Restore cursor and return control to the player.
*/
+void Control(int param) {
+ if (TinselV2) {
+ if (param)
+ ControlOn();
+ else {
+ ControlOff();
+
+ switch (WhichInventoryOpen()) {
+ case INV_1:
+ case INV_2:
+ case INV_MENU:
+ KillInventory();
+ break;
+ default:
+ break;
+ }
+ }
-void control(int param) {
- bEnableF1 = false;
+ return;
+ }
+
+ // Tinsel 1 handling code
+ bEnableMenu = false;
switch (param) {
case CONTROL_STARTOFF:
@@ -385,46 +758,82 @@ void control(int param) {
/**
* Open or close the conversation window.
*/
-
-void conversation(int fn, HPOLYGON hp, bool escOn, int myescEvent) {
+static void Conversation(CORO_PARAM, int fn, HPOLYGON hp, int actor, bool escOn, int myEscape) {
assert(hp != NOPOLY); // conversation() must (currently) be called from a polygon code block
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
- switch (fn) {
- case CONV_END: // Close down conversation
+ CORO_BEGIN_CODE(_ctx);
+
+ if (fn == CONV_END) {
+ // Close down conversation
CloseDownConv();
- break;
+ } else if ((fn == CONV_TOP) || (fn == CONV_DEF) || (fn == CONV_BOTTOM)) {
+ // TOP of screen, Default (i.e. TOP of screen), or BOTTOM of screen
+
+ // If waiting is enabled, wait for ongoing scroll
+ if (TinselV2 && SysVar(SV_CONVERSATIONWAITS))
+ CORO_INVOKE_1(WaitScroll, myEscape);
- case CONV_DEF: // Default (i.e. TOP of screen)
- case CONV_BOTTOM: // BOTTOM of screen
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
- break;
+ if (escOn && myEscape != GetEscEvents())
+ return;
+ // Don't do it if already in a conversation
if (IsConvWindow())
- break;
+ return;
KillInventory();
- convPos(fn);
- ConvPoly(hp);
+
+ if (TinselV2) {
+ // If this is from a tag polygon, get the associated
+ // actor (the one the polygon is named after), if any.
+ if (!actor) {
+ actor = GetTagPolyId(hp);
+ if (actor & ACTORTAG_KEY)
+ actor &= ~ACTORTAG_KEY;
+ else
+ actor = 0;
+ }
+
+ // Top or bottom; tag polygon or tagged actor
+ SetConvDetails((CONV_PARAM)fn, hp, actor);
+ } else {
+ convPos(fn);
+ ConvPoly(hp);
+ }
+
PopUpInventory(INV_CONV); // Conversation window
ConvAction(INV_OPENICON); // CONVERSATION event
- break;
}
+
+ CORO_END_CODE;
}
/**
* Add icon to conversation window's permanent default list.
*/
+static void ConvTopic(int icon) {
+ PermaConvIcon(icon);
+}
-void convicon(int icon) {
- AddIconToPermanentDefaultList(icon);
+/**
+ * Cursor(on/off)
+ */
+void Cursor(int onoff) {
+ if (onoff) {
+ // Re-instate cursor
+ UnHideCursor();
+ } else {
+ // Blank out cursor
+ DwHideCursor();
+ }
}
/**
* Returns the x or y position of the cursor.
*/
-
-int cursorpos(int xory) {
+static int CursorPos(int xory) {
int x, y;
GetCursorXY(&x, &y, true);
@@ -434,8 +843,7 @@ int cursorpos(int xory) {
/**
* Declare conversation window.
*/
-
-void dec_convw(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
+static void DecConvW(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
idec_convw(text, MaxContents, MinWidth, MinHeight,
StartWidth, StartHeight, MaxWidth, MaxHeight);
@@ -444,32 +852,28 @@ void dec_convw(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
/**
* Declare config strings.
*/
-
-void dec_cstrings(SCNHANDLE *tp) {
+static void DecCStrings(SCNHANDLE *tp) {
setConfigStrings(tp);
}
/**
* Declare cursor's reels.
*/
-
-void dec_cursor(SCNHANDLE bfilm) {
- DwInitCursor(bfilm);
+static void DecCursor(SCNHANDLE hFilm) {
+ DwInitCursor(hFilm);
}
/**
* Declare the language flags.
*/
-
-void dec_flags(SCNHANDLE hf) {
- setFlagFilms(hf);
+static void DecFlags(SCNHANDLE hFilm) {
+ setFlagFilms(hFilm);
}
/**
* Declare inventory 1's parameters.
*/
-
-void dec_inv1(SCNHANDLE text, int MaxContents,
+static void DecInv1(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
int MaxWidth, int MaxHeight) {
@@ -480,8 +884,7 @@ void dec_inv1(SCNHANDLE text, int MaxContents,
/**
* Declare inventory 2's parameters.
*/
-
-void dec_inv2(SCNHANDLE text, int MaxContents,
+static void DecInv2(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
int MaxWidth, int MaxHeight) {
@@ -492,77 +895,104 @@ void dec_inv2(SCNHANDLE text, int MaxContents,
/**
* Declare the bits that the inventory windows are constructed from.
*/
-
-void dec_invw(SCNHANDLE hf) {
+static void DecInvW(SCNHANDLE hf) {
setInvWinParts(hf);
}
/**
- * Declare lead actor.
- * - the actor's id, walk and stand reels for all the regular scales,
- * and the tag text.
+ * DeclareLanguage
*/
+static void DeclareLanguage(int languageId, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
+ LanguageFacts(languageId, hDescription, hFlagFilm);
+}
-void dec_lead(uint32 id, SCNHANDLE *rp, SCNHANDLE text) {
- PMACTOR pActor; // Moving actor structure
+/**
+ * Declare lead actor.
+ * @param id Actor Id
+ * @param rp Walk and stand reels for all the regular scales (v1 only)
+ * @param text Tag text (v1 only)
+ */
+static void DecLead(uint32 id, SCNHANDLE *rp = 0, SCNHANDLE text = 0) {
+ PMOVER pMover; // Moving actor structure
- Tag_Actor(id, text, TAG_DEF); // The lead actor is automatically tagged
- setleadid(id); // Establish this as the lead
- SetMover(id); // Establish as a moving actor
+ if (TinselV2) {
+ // Tinsel 2 only specifies the lead actor Id
+ SetLeadId(id);
+ RegisterMover(id);
- pActor = GetMover(id); // Get moving actor structure
- assert(pActor);
+ } else {
- // Store all those reels
- int i, j;
- for (i = 0; i < 5; ++i) {
- for (j = 0; j < 4; ++j)
- pActor->WalkReels[i][j] = *rp++;
- for (j = 0; j < 4; ++j)
- pActor->StandReels[i][j] = *rp++;
- for (j = 0; j < 4; ++j)
- pActor->TalkReels[i][j] = *rp++;
- }
+ Tag_Actor(id, text, TAG_DEF); // The lead actor is automatically tagged
+ SetLeadId(id); // Establish this as the lead
+ RegisterMover(id); // Establish as a moving actor
+
+ pMover = GetMover(id); // Get moving actor structure
+ assert(pMover);
+
+ // Store all those reels
+ int i, j;
+ for (i = 0; i < 5; ++i) {
+ for (j = 0; j < 4; ++j)
+ pMover->walkReels[i][j] = *rp++;
+ for (j = 0; j < 4; ++j)
+ pMover->standReels[i][j] = *rp++;
+ for (j = 0; j < 4; ++j)
+ pMover->talkReels[i][j] = *rp++;
+ }
- for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
- for (j = 0; j < 4; ++j) {
- pActor->WalkReels[i][j] = pActor->WalkReels[4][j];
- pActor->StandReels[i][j] = pActor->StandReels[2][j];
- pActor->TalkReels[i][j] = pActor->TalkReels[4][j];
+ for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
+ for (j = 0; j < 4; ++j) {
+ pMover->walkReels[i][j] = pMover->walkReels[4][j];
+ pMover->standReels[i][j] = pMover->standReels[2][j];
+ pMover->talkReels[i][j] = pMover->talkReels[4][j];
+ }
}
}
}
/**
- * Declare the text font.
+ * DecScale("actor", scale, 12*"reel")
+ * Define an actor's walk and stand reels for a scale.
*/
-
-void dec_tagfont(SCNHANDLE hf) {
- TagFontHandle(hf); // Store the font handle
+static void DecScale(int actor, int scale,
+ SCNHANDLE wkl, SCNHANDLE wkr, SCNHANDLE wkf, SCNHANDLE wka,
+ SCNHANDLE stl, SCNHANDLE str, SCNHANDLE stf, SCNHANDLE sta,
+ SCNHANDLE tal, SCNHANDLE tar, SCNHANDLE taf, SCNHANDLE taa)
+{
+ PMOVER pMover = GetMover(actor);
+ assert(pMover);
+
+ SetWalkReels(pMover, scale, wkl, wkr, wkf, wka);
+ SetStandReels(pMover, scale, stl, str, stf, sta);
+ SetTalkReels(pMover, scale, tal, tar, taf, taa);
}
/**
* Declare the text font.
*/
+static void DecTagFont(SCNHANDLE hf) {
+ SetTagFontHandle(hf); // Store the font handle
+}
-void dec_talkfont(SCNHANDLE hf) {
- TalkFontHandle(hf); // Store the font handle
+/**
+ * Declare the text font.
+ */
+static void DecTalkFont(SCNHANDLE hf) {
+ SetTalkFontHandle(hf); // Store the font handle
}
/**
* Remove an icon from the conversation window.
*/
-
-void delicon(int icon) {
+static void DelIcon(int icon) {
RemFromInventory(INV_CONV, icon);
}
/**
* Delete the object from inventory 1 or 2.
*/
-
-void delinv(int object) {
+static void DelInv(int object) {
if (!RemFromInventory(INV_1, object)) // Remove from inventory 1...
RemFromInventory(INV_2, object); // ...or 2 (whichever)
@@ -570,18 +1000,119 @@ void delinv(int object) {
}
/**
- * enablef1
+ * DelTopic
*/
+static void DelTopic(int icon) {
+ RemFromInventory(INV_CONV, icon);
+}
-void enablef1(void) {
- bEnableF1 = true;
+/**
+ * DimMusic
+ */
+static void DimMusic(void) {
+ _vm->_pcmMusic->dim(true);
}
/**
- * fademidi(in/out)
+ * Delete the object from inventory 1 or 2.
+ */
+static void Drop(int object) {
+ if (object == -1)
+ object = HeldObject();
+
+ if (!RemFromInventory(INV_1, object)) // Remove from inventory 1...
+ RemFromInventory(INV_2, object); // ...or 2 (whichever)
+
+ DropItem(object); // Stop holding it
+}
+
+/**
+ * Delete all objects from inventory 1 and 2.
+ */
+static void DropEverything(void) {
+ HoldItem(NOOBJECT, false);
+
+ ClearInventory(INV_1);
+ ClearInventory(INV_2);
+}
+
+/**
+ * EnableMenu
+ */
+static void EnableMenu(void) {
+ bEnableMenu = true;
+}
+
+/**
+ * Kill an actor's current graphics.
*/
+static void EndActor(int actor) {
+ dwEndActor(actor);
+}
+
+/**
+ * Get the actor to look at the polygon.
+ * If the actor is at the tag, do a StandTag().
+ */
+static void FaceTag(int actor, HPOLYGON hp) {
+ PMOVER pMover; // Moving actor structure
+ int nowx, nowy;
+ int nodex, nodey;
+
+ assert(hp != NOPOLY);
-void fademidi(CORO_PARAM, int inout) {
+ /*
+ * Get which moving actor it is
+ */
+ pMover = GetMover(actor);
+ assert(pMover);
+ if (MoverHidden(pMover))
+ return;
+
+ /*
+ * Stop the actor
+ */
+ StopWalk(actor);
+
+ /*
+ * Face the tag
+ */
+ // See where node is and where actor is
+ GetPolyNode(hp, &nodex, &nodey);
+ GetActorPos(actor, &nowx, &nowy);
+
+ if (nowx == nodex && nowy == nodey) {
+ // Stood at the tag, don't face in silly direction
+ StandTag(actor, hp);
+ } else {
+ // Look towards polygon
+ GetPolyMidBottom(hp, &nodex, &nodey);
+ SetMoverDirection(pMover, GetDirection(nowx, nowy,
+ nodex, nodey,
+ GetMoverDirection(pMover),
+ NOPOLY, YB_X1_5));
+ SetMoverStanding(pMover);
+ }
+}
+
+/**
+ * FadeIn
+ */
+static void FadeIn(void) {
+ FadeInMedium(NULL);
+}
+
+/**
+ * FadeOut
+ */
+static void FadeOut(void) {
+ FadeOutMedium(NULL);
+}
+
+/**
+ * FadeMidi(in/out)
+ */
+static void FadeMidi(CORO_PARAM, int inout) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
@@ -595,82 +1126,159 @@ void fademidi(CORO_PARAM, int inout) {
}
/**
- * Guess what.
+ * Freeze the cursor, or not.
*/
+static void FreezeCursor(bool bFreeze) {
+ DoFreezeCursor(bFreeze);
+}
-int getinvlimit(int invno) {
+/**
+ * Guess what.
+ */
+static int GetInvLimit(int invno) {
return InvGetLimit(invno);
}
/**
+ * Ghost
+ */
+static void Ghost(int actor, int tColour, int tPalOffset) {
+ SetSysVar(ISV_GHOST_ACTOR, actor);
+ SetSysVar(ISV_GHOST_COLOUR, tColour);
+ SetSysVar(ISV_GHOST_BASE, tPalOffset);
+ CreateGhostPalette(BgPal());
+}
+
+/**
+ *
+ */
+static void HailScene(SCNHANDLE scene) {
+ DoHailScene(scene);
+}
+
+/**
* Returns TRUE if the game has been restarted, FALSE if not.
*/
-bool hasrestarted(void) {
+static bool HasRestarted(void) {
return bHasRestarted;
}
/**
- * Returns which object is currently held.
+ * See if an object is in the inventory.
*/
+int Have(int object) {
+ return (InventoryPos(object) != NOOBJECT);
+}
-int heldobject(void) {
+/**
+ * Returns which object is currently held.
+ */
+static int HeldObject(void) {
return WhichItemHeld();
}
/**
- * Removes a player from the screen, probably when he's about to be
- * replaced by an animation.
- *
- * Not believed to work anymore! (hide() is not used).
+ * Hides the specified actor
*/
+static void HideActorFn(CORO_PARAM, int ano) {
+ HideActor(coroParam, ano);
+}
-void hide(int actor) {
- HideActor(actor);
+/**
+ * Turn a blocking polygon off.
+ */
+static void HideBlock(int block) {
+ DisableBlock(block);
+}
+
+/**
+ * Turn an effect polygon off.
+ */
+static void HideEffect(int effect) {
+ DisableEffect(effect);
+}
+
+/**
+ * Turn a path polygon off.
+ */
+static void HidePath(int path) {
+ DisablePath(path);
+}
+
+/**
+ * Turn a refer polygon off.
+ */
+static void HideRefer(int refer) {
+ DisableRefer(refer);
+}
+
+/**
+ * Turn a tag polygon off.
+ */
+static void HideTag(CORO_PARAM, int tag, HPOLYGON hp) {
+ // Tag could be zero, meaning calling tag
+ DisableTag(coroParam, tag ? tag : GetTagPolyId(hp));
}
/**
- * hookscene(scene, entrance, transition)
+ * Hold the specified object.
*/
+static void Hold(int object) {
+ HoldItem(object, false);
+}
-void hookscene(SCNHANDLE scene, int entrance, int transition) {
+/**
+ * HookScene(scene, entrance, transition)
+ */
+void HookScene(SCNHANDLE scene, int entrance, int transition) {
SetHookScene(scene, entrance, transition);
}
/**
- * idletime
+ * IdleTime
*/
+static int IdleTime(void) {
+ // If control is off, system is not idle
+ if (!ControlIsOn()) {
+ // Player doesn't currently have control
+ ResetIdleTime();
-int idletime(void) {
- uint32 x;
+ return 0;
+ } else {
+ // Player has control - return time since last event
+ int x = getUserEventTime() / ONE_SECOND;
- x = getUserEventTime() / ONE_SECOND;
-
- if (!TestToken(TOKEN_CONTROL))
- resetidletime();
+ return x;
+ }
+}
- return (int)x;
+/**
+ * Set flag if InstantScroll(on), reset if InstantScroll(off)
+ */
+void InstantScroll(int onoff) {
+ bInstantScroll = (onoff != 0);
}
/**
* invdepict
*/
-void invdepict(int object, SCNHANDLE hFilm) {
- invObjectFilm(object, hFilm);
+static void InvDepict(int object, SCNHANDLE hFilm) {
+ SetObjectFilm(object, hFilm);
}
/**
* See if an object is in the inventory.
*/
-int ininventory(int object) {
+int InInventory(int object) {
return (InventoryPos(object) != INV_NOICON);
}
/**
* Open an inventory.
*/
-void inventory(int invno, bool escOn, int myescEvent) {
+static void Inventory(int invno, bool escOn, int myEscape) {
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
assert((invno == INV_1 || invno == INV_2)); // Trying to open illegal inventory
@@ -679,9 +1287,16 @@ void inventory(int invno, bool escOn, int myescEvent) {
}
/**
+ * Alter inventory object's icon.
+ */
+static void InvPlay(int object, SCNHANDLE hFilm) {
+ SetObjectFilm(object, hFilm);
+}
+
+/**
* See if an object is in the inventory.
*/
-int inwhichinv(int object) {
+static int InWhichInv(int object) {
if (WhichItemHeld() == object)
return 0;
@@ -697,45 +1312,59 @@ int inwhichinv(int object) {
/**
* Kill an actor.
*/
-void killactor(int actor) {
+static void KillActor(int actor) {
DisableActor(actor);
}
/**
* Turn a blocking polygon off.
*/
-void killblock(int block) {
+static void KillBlock(int block) {
DisableBlock(block);
}
/**
* Turn an exit off.
*/
-void killexit(int exit) {
+static void KillExit(int exit) {
DisableExit(exit);
}
/**
* Turn a tag off.
*/
-void killtag(int tagno) {
- DisableTag(tagno);
+static void KillTag(CORO_PARAM, int tagno) {
+ DisableTag(coroParam, tagno);
+}
+
+/**
+ * Kills the specified global process
+ */
+static void KillGlobalProcess(uint32 procID) {
+ xKillGlobalProcess(procID);
+}
+
+/**
+ * Kills the specified process
+ */
+static void KillProcess(uint32 procID) {
+ KillSceneProcess(procID);
}
/**
* Returns the left or top offset of the screen.
*/
-int ltoffset(int lort) {
+static int LToffset(int lort) {
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- return (lort == LEFTOFFSET) ? Loffset : Toffset;
+ return (lort == SCREENXPOS) ? Loffset : Toffset;
}
/**
* Set new cursor position.
*/
-void movecursor(int x, int y) {
+static void MoveCursor(int x, int y) {
SetCursorXY(x, y);
controlX = x; // Save these values so that
@@ -743,29 +1372,45 @@ void movecursor(int x, int y) {
}
/**
+ * MoveTag(tag, x, y)
+ */
+void MoveTag(int tag, int x, int y, HPOLYGON hp) {
+ // Tag could be zero, meaning calling tag
+ MovePolygon(TAG, tag ? tag : GetTagPolyId(hp), x, y);
+}
+
+/**
+ * MoveTagTo(tag, x, y)
+ */
+void MoveTagTo(int tag, int x, int y, HPOLYGON hp) {
+ // Tag could be zero, meaning calling tag
+ MovePolygonTo(TAG, tag ? tag : GetTagPolyId(hp), x, y);
+}
+
+/**
* Triggers change to a new scene.
*/
-void newscene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
+void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
-#ifdef BODGE
- if (!ValidHandle(scene)) {
- scene = GetSceneHandle();
- entrance = 1;
+ if (TinselV2) {
+ if (MoviePlaying()) {
+ AbortMovie();
+ CORO_SLEEP(2);
+ }
}
- assert(scene); // Non-existant first scene!
-#endif
SetNewScene(scene, entrance, transition);
-#if 1
// Prevent tags and cursor re-appearing
- GetControl(CONTROL_STARTOFF);
-#endif
+ if (TinselV2)
+ ControlStartOff();
+ else
+ GetControl(CONTROL_STARTOFF);
// Prevent code subsequent to this call running before scene changes
if (g_scheduler->getCurrentPID() != PID_MASTER_SCR)
@@ -776,47 +1421,78 @@ void newscene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
/**
* Disable dynamic blocking for current scene.
*/
-void noblocking(void) {
- bNoBlocking = true;
+static void NoBlocking(void) {
+ SetNoBlocking(true);
}
/**
* Define a no-scroll boundary for the current scene.
*/
-void noscroll(int x1, int y1, int x2, int y2) {
+static void NoScroll(int x1, int y1, int x2, int y2) {
SetNoScroll(x1, y1, x2, y2);
}
/**
* Hold the specified object.
*/
-void objectheld(int object) {
+static void ObjectHeld(int object) {
HoldItem(object);
}
/**
* Set the top left offset of the screen.
*/
-void offset(int x, int y) {
+void Offset(EXTREME extreme, int x, int y) {
KillScroll();
+
+ if (TinselV2)
+ DecodeExtreme(extreme, &x, &y);
+
PlayfieldSetPos(FIELD_WORLD, x, y);
}
/**
+ * OtherObject()
+ */
+int OtherObject(INV_OBJECT *pinvo) {
+ assert(pinvo != NULL);
+
+ // return held object or object clicked on - whichever is not the calling object
+
+ // pinvo->id is the calling object
+ // WhichItemHeld() gives the held object
+ // GetIcon() gives the object clicked on
+
+ assert(GetIcon() == pinvo->id || WhichItemHeld() == pinvo->id);
+
+ if (GetIcon() == pinvo->id)
+ return WhichItemHeld();
+ else
+ return GetIcon();
+}
+
+/**
* Play a film.
*/
-void play(CORO_PARAM, SCNHANDLE film, int x, int y, int compit, int actorid, bool splay, int sfact,
- bool escOn, int myescEvent, bool bTop) {
+static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int compit, int actorid, bool splay, int sfact,
+ bool escOn, int myEscape, bool bTop) {
+ assert(hFilm != 0); // play(): Trying to play NULL film
+
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- assert(film != 0); // play(): Trying to play NULL film
+
+ // Don't do CDPlay() for now if already escaped
+ if (bEscapedCdPlay) {
+ bEscapedCdPlay = false;
+ return;
+ }
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
// If this actor is dead, call a stop to the calling process
@@ -825,24 +1501,75 @@ void play(CORO_PARAM, SCNHANDLE film, int x, int y, int compit, int actorid, boo
// 7/4/95
if (!escOn)
- myescEvent = GetEscEvents();
+ myEscape = GetEscEvents();
if (compit == 1) {
// Play to completion before returning
- CORO_INVOKE_ARGS(playFilmc, (CORO_SUBCTX, film, x, y, actorid, splay, sfact, escOn, myescEvent, bTop));
+ CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop));
} else if (compit == 2) {
error("play(): compit == 2 - please advise John");
} else {
// Kick off the play and return.
- playFilm(film, x, y, actorid, splay, sfact, escOn, myescEvent, bTop);
+ CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop));
+ }
+ CORO_END_CODE;
+}
+
+/**
+ * Play a film
+ */
+static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myEscape,
+ bool bTop, TINSEL_EVENT event, HPOLYGON hPoly, int taggedActor) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ assert(hFilm != 0);
+
+ // Don't do CdPlay() for now if already escaped
+ if (bEscapedCdPlay) {
+ bEscapedCdPlay = false;
+ return;
+ }
+
+ if (event == TALKING) {
+ int actor;
+ if (hPoly == NOPOLY) {
+ // Must be a tagged actor
+
+ assert(taggedActor && IsTaggedActor(taggedActor));
+ actor = taggedActor;
+ } else if (taggedActor == 0) {
+ // Must be a polygon with an actor ID
+ actor = GetTagPolyId(hPoly);
+ assert(actor & ACTORTAG_KEY);
+ actor &= ~ACTORTAG_KEY;
+ }
+ else {
+ return;
+ }
+
+ SetActorTalking(actor, true);
+ SetActorTalkFilm(actor, hFilm);
+ }
+
+ if (bComplete) {
+ // Play to completion before returning
+ CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, 0, false, false, myEscape != 0, myEscape, bTop));
+ } else {
+ // Kick off the play and return.
+ CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, myEscape, bTop));
}
+
CORO_END_CODE;
}
+
/**
* Play a midi file.
*/
-void playmidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
+static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
// FIXME: This is a workaround for the FIXME below
if (GetMidiVolume() == 0)
return;
@@ -877,9 +1604,48 @@ void playmidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
}
/**
+ * Plays a movie
+ */
+
+static void PlayMovie(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (myEscape && myEscape != GetEscEvents())
+ return;
+
+ // Get rid of the cursor
+ for (_ctx->i = 0; _ctx->i < 3; _ctx->i++) {
+ DwHideCursor();
+ DropCursor();
+ CORO_SLEEP(1);
+ }
+
+ // They claim to be getting "Can't play two movies at once!" error
+ while (MoviePlaying())
+ CORO_SLEEP(1);
+
+ // Play the movie
+ CORO_INVOKE_2(PlayBMV, hFileStem, myEscape);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Play some music
+ */
+static void PlayMusic(int tune) {
+ _vm->_pcmMusic->startPlay(tune);
+}
+
+/**
* Play a sample.
+ * Tinsel 1 version
*/
-void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEvent) {
+static void PlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
Audio::SoundHandle handle;
CORO_END_CONTEXT(_ctx);
@@ -890,7 +1656,7 @@ void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEven
return;
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents()) {
+ if (escOn && myEscape != GetEscEvents()) {
_vm->_sound->stopAllSamples(); // Stop any currently playing sample
return;
}
@@ -898,10 +1664,10 @@ void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEven
if (volSound != 0 && _vm->_sound->sampleExists(sample)) {
_vm->_sound->playSample(sample, Audio::Mixer::kSFXSoundType, &_ctx->handle);
- if (complete) {
+ if (bComplete) {
while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
// Abort if escapable and ESCAPE is pressed
- if (escOn && myescEvent != GetEscEvents()) {
+ if (escOn && myEscape != GetEscEvents()) {
_vm->_mixer->stopHandle(_ctx->handle);
break;
}
@@ -917,28 +1683,153 @@ void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEven
}
/**
- * Play a sample.
+ * Play a sample
+ * Tinsel 2 version
*/
-void tryplaysample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEvent) {
+static void PlaySample(CORO_PARAM, int sample, int x, int y, int flags, int myEscape) {
+ int priority;
CORO_BEGIN_CONTEXT;
+ Audio::SoundHandle handle;
+ int myEscape;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- // Don't do it if it's not appropriate
- if (_vm->_sound->sampleIsPlaying()) {
- // return, but prevent Glitter lock-up
- CORO_SLEEP(1);
+
+ _ctx->myEscape = myEscape;
+
+ // Not escapable if PlaySample(..., s)
+ if (flags & PS_SUSTAIN) {
+ _ctx->myEscape = 0;
+ priority = PRIORITY_SPLAY2;
+ } else {
+ priority = PRIORITY_SPLAY1;
+ }
+
+ // Don't do anything if it's already been escaped
+ if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
return;
+
+ if (volSound != 0 && _vm->_sound->sampleExists(sample)) {
+ if (x == 0)
+ x = -1;
+
+ _vm->_sound->playSample(sample, 0, false, x, y, priority, Audio::Mixer::kSFXSoundType,
+ &_ctx->handle);
+
+ if (flags & PS_COMPLETE) {
+ while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
+ // Abort if escapable and ESCAPE is pressed
+ if (_ctx->myEscape && _ctx->myEscape != GetEscEvents()) {
+ _vm->_mixer->stopHandle(_ctx->handle);
+ break;
+ }
+
+ CORO_SLEEP(1);
+ }
+ }
+ } else {
+ // Prevent Glitter lock-up
+ CORO_SLEEP(1);
}
- CORO_INVOKE_ARGS(playsample, (CORO_SUBCTX, sample, complete, escOn, myescEvent));
CORO_END_CODE;
}
/**
+ * Move the cursor to the tagged actor's tag point.
+ */
+void PointActor(int actor) {
+ int x, y;
+
+ // Only do this if the function is enabled
+ if (!SysVar(SV_ENABLEPOINTTAG))
+ return;
+
+ assert(IsTaggedActor(actor));
+
+ GetActorTagPos(actor, &x, &y, true);
+
+ _vm->setMousePosition(Common::Point(x, y));
+}
+
+/**
+ * Move the cursor to the tag's tag point.
+ */
+static void PointTag(int tagno, HPOLYGON hp) {
+ int x, y;
+ SCNHANDLE junk;
+
+ // Only do this if the function is enabled
+ if (!SysVar(SV_ENABLEPOINTTAG))
+ return;
+
+ // Tag could be zero, meaning calling tag
+ if (tagno == 0)
+ tagno = GetTagPolyId(hp);
+
+ GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
+
+ _vm->setMousePosition(Common::Point(x, y));
+}
+
+/**
+ * PostActor("actor", event)
+ */
+static void PostActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp,
+ int taggedActor, int myEscape) {
+ if (actor == -1) {
+ actor = taggedActor;
+ assert(hp == NOPOLY && taggedActor);
+ assert(IsTaggedActor(actor));
+ }
+
+ if (IsTaggedActor(actor)) {
+ assert(actor);
+ ActorEvent(coroParam, actor, event, false, myEscape);
+ } else {
+ PostTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape);
+ }
+}
+
+/**
+ * PostGlobalProcess(process#, event)
+ */
+static void PostGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
+ GlobalProcessEvent(coroParam, procId, event, false, myEscape);
+}
+
+/**
+ * PostObject(object, event)
+ */
+static void PostObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
+ ObjectEvent(coroParam, object, event, false, myEscape);
+}
+
+/**
+ * PostProcess(process#, event)
+ */
+static void PostProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
+ SceneProcessEvent(coroParam, procId, event, false, myEscape);
+}
+
+/**
+ * Posts an event to a specified tag
+ */
+static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
+ // Tag could be zero, meaning calling tag
+ if (tagno == 0) {
+ assert(hp != NOPOLY);
+ PolygonEvent(coroParam, hp, event, 0, false, myEscape);
+ } else {
+ assert(IsTagPolygon(tagno));
+ PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, false, myEscape);
+ }
+}
+
+/**
* Trigger pre-loading of a scene's data.
*/
-void preparescene(SCNHANDLE scene) {
+static void PrepareScene(SCNHANDLE scene) {
#ifdef BODGE
if (!ValidHandle(scene))
return;
@@ -947,11 +1838,11 @@ void preparescene(SCNHANDLE scene) {
/**
* Print the given text at the given place for the given time.
- *
- * Print(....., h) -> hold = 1 (not used)
- * Print(....., s) -> hold = 2 (sustain)
*/
-void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool escOn, int myescEvent) {
+static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSustain, bool escOn, int myEscape) {
+ if (TinselV2)
+ escOn = myEscape != 0;
+
CORO_BEGIN_CONTEXT;
OBJECT *pText; // text object pointer
int myleftEvent;
@@ -969,37 +1860,55 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es
_ctx->bSample = false;
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
- // Kick off the voice sample
- if (volVoice != 0 && _vm->_sound->sampleExists(text)) {
- _vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
- _ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
+ if (!TinselV2) {
+ // Kick off the voice sample
+ if (volVoice != 0 && _vm->_sound->sampleExists(text)) {
+ _vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
+ _ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
+ }
}
+ // Get the string
+ LoadStringRes(text, TextBufferAddr(), TBUFSZ);
+
// Calculate display time
- LoadStringRes(text, tBufferAddr(), TBUFSZ);
bJapDoPrintText = false;
if (time == 0) {
// This is a 'talky' print
- _ctx->time = TextTime(tBufferAddr());
+ _ctx->time = TextTime(TextBufferAddr());
// Cut short-able if sustain was not set
- _ctx->myleftEvent = (hold == 2) ? 0 : GetLeftEvents();
+ _ctx->myleftEvent = bSustain ? 0 : GetLeftEvents();
} else {
_ctx->time = time * ONE_SECOND;
- _ctx->myleftEvent = 0;
+ _ctx->myleftEvent = (TinselV2 && !bSustain) ? GetLeftEvents() : 0;
if (isJapanMode())
bJapDoPrintText = true;
}
// Print the text
- if (bJapDoPrintText || (!isJapanMode() && (bSubtitles || !_ctx->bSample))) {
+ if (TinselV2) {
+ int Loffset, Toffset;
+ PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+ _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, x - Loffset, y - Toffset, GetTagFontHandle(),
+ TXT_CENTRE, 0);
+ assert(_ctx->pText);
+
+ // Adjust x, y, or z if necessary
+ KeepOnScreen(_ctx->pText, &x, &y);
+ if (IsTopWindow())
+ MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
+
+ } else if (bJapDoPrintText || (!isJapanMode() && (bSubtitles || !_ctx->bSample))) {
int Loffset, Toffset; // Screen position
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
- 0, x - Loffset, y - Toffset, hTalkFontHandle(), TXT_CENTRE);
+ _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ 0, x - Loffset, y - Toffset,
+ TinselV2 ? GetTagFontHandle() : GetTalkFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // string produced NULL text
if (IsTopWindow())
MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
@@ -1009,14 +1918,14 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es
*/
int shift;
shift = MultiRightmost(_ctx->pText) + 2;
- if (shift >= BackgroundWidth()) // Not off right
- MultiMoveRelXY(_ctx->pText, BackgroundWidth() - shift, 0);
+ if (shift >= BgWidth()) // Not off right
+ MultiMoveRelXY(_ctx->pText, BgWidth() - shift, 0);
shift = MultiLeftmost(_ctx->pText) - 1;
if (shift <= 0) // Not off left
MultiMoveRelXY(_ctx->pText, -shift, 0);
shift = MultiLowest(_ctx->pText);
- if (shift > BackgroundHeight()) // Not off bottom
- MultiMoveRelXY(_ctx->pText, 0, BackgroundHeight() - shift);
+ if (shift > BgHeight()) // Not off bottom
+ MultiMoveRelXY(_ctx->pText, 0, BgHeight() - shift);
}
// Give up if nothing printed and no sample
@@ -1024,37 +1933,50 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es
return;
// Leave it up until time runs out or whatever
- _ctx->timeout = SAMPLETIMEOUT;
- do {
- CORO_SLEEP(1);
+ if (TinselV2) {
+ do {
+ CORO_SLEEP(1);
- // Abort if escapable and ESCAPE is pressed
- // Abort if left click - hardwired feature for talky-print!
- // Will be ignored if myleftevent happens to be 0!
- // Abort if sample times out
- if ((escOn && myescEvent != GetEscEvents())
- || (_ctx->myleftEvent && _ctx->myleftEvent != GetLeftEvents())
- || (_ctx->bSample && --_ctx->timeout <= 0))
- break;
+ // Cancelled?
+ if ( (myEscape && myEscape != GetEscEvents())
+ || (!bSustain && LeftEventChange(_ctx->myleftEvent)))
+ break;
- if (_ctx->bSample) {
- // Wait for sample to end whether or not
- if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
- if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) {
- // No text or speed modification - just depends on sample
- break;
- } else {
- // Must wait for time
- _ctx->bSample = false;
+ } while (_ctx->time-- >= 0);
+
+ } else {
+ _ctx->timeout = SAMPLETIMEOUT;
+ do {
+ CORO_SLEEP(1);
+
+ // Abort if escapable and ESCAPE is pressed
+ // Abort if left click - hardwired feature for talky-print!
+ // Will be ignored if myleftevent happens to be 0!
+ // Abort if sample times out
+ if ((escOn && myEscape != GetEscEvents())
+ || (_ctx->myleftEvent && _ctx->myleftEvent != GetLeftEvents())
+ || (_ctx->bSample && --_ctx->timeout <= 0))
+ break;
+
+ if (_ctx->bSample) {
+ // Wait for sample to end whether or not
+ if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
+ if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) {
+ // No text or speed modification - just depends on sample
+ break;
+ } else {
+ // Must wait for time
+ _ctx->bSample = false;
+ }
}
+ } else {
+ // No sample - just depends on time
+ if (_ctx->time-- <= 0)
+ break;
}
- } else {
- // No sample - just depends on time
- if (_ctx->time-- <= 0)
- break;
- }
- } while (1);
+ } while (1);
+ }
// Delete the text
if (_ctx->pText != NULL)
@@ -1065,84 +1987,228 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es
}
-static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
-static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText);
+static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
+static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText);
/**
* Print the given inventory object's name or whatever.
*/
-void printobj(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, const int event) {
+static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo, const int event, int myEscape) {
CORO_BEGIN_CONTEXT;
OBJECT *pText; // text object pointer
int textx, texty;
int item;
+ bool bSample;
+ int sub;
+ Audio::SoundHandle handle;
+ int ticks;
+ int timeout;
+ bool bTookControl;
+ int myEscape;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- assert(pinvo != 0); // printobj() may only be called from an object code block
+ assert(pinvo != 0); // PrintObj() may only be called from an object code block
+ _ctx->myEscape = myEscape;
- if (text == (SCNHANDLE)-1) { // 'OFF'
- NotPointedRunning = true;
+ if (hText == (SCNHANDLE)-1) { // 'OFF'
+ bNotPointedRunning = true;
return;
}
- if (text == (SCNHANDLE)-2) { // 'ON'
- NotPointedRunning = false;
+ if (hText == (SCNHANDLE)-2) { // 'ON'
+ bNotPointedRunning = false;
return;
}
+ // Don't do it if it's not wanted
+ if (TinselV2 && (myEscape) && (myEscape != GetEscEvents()))
+ return;
+
+ /*
+ * Find out which icon the cursor is over, and where to put the text.
+ */
GetCursorXY(&_ctx->textx, &_ctx->texty, false); // Cursor position..
_ctx->item = InvItem(&_ctx->textx, &_ctx->texty, true); // ..to text position
-
if (_ctx->item == INV_NOICON)
return;
+ /*
+ * POINT/other event PrintObj() arbitration...
+ */
if (event != POINTED) {
- NotPointedRunning = true; // Get POINTED text to die
+ bNotPointedRunning = true; // Get POINTED text to die
CORO_SLEEP(1); // Give it chance to
- } else
- NotPointedRunning = false; // There may have been an OFF without an ON
+ } else if (!TinselV2)
+ bNotPointedRunning = false; // There may have been an OFF without an ON
- // Display the text and set it's Z position
- if (event == POINTED || (!isJapanMode() && (bSubtitles || !_vm->_sound->sampleExists(text)))) {
- int xshift;
+ // Make multi-ones escape
+ if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->myEscape)
+ _ctx->myEscape = GetEscEvents();
- LoadStringRes(text, tBufferAddr(), TBUFSZ); // The text string
- _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
- 0, _ctx->textx, _ctx->texty, hTagFontHandle(), TXT_CENTRE);
- assert(_ctx->pText); // printobj() string produced NULL text
- MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
+ // Loop once for Tinsel 1 strings, and for Tinsel 2 however many lines are needed
+ for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
+ if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
+ break;
- // Don't go off the side of the screen
- xshift = MultiLeftmost(_ctx->pText);
- if (xshift < 0) {
- MultiMoveRelXY(_ctx->pText, - xshift, 0);
- _ctx->textx -= xshift;
+ if (!_vm->_sound->sampleExists(hText))
+ _ctx->bSample = false;
+ else {
+ // Kick off the voice sample
+ _vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK,
+ Audio::Mixer::kSpeechSoundType, &_ctx->handle);
+ _ctx->bSample = true;
}
- xshift = MultiRightmost(_ctx->pText);
- if (xshift > SCREEN_WIDTH) {
- MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
- _ctx->textx += SCREEN_WIDTH - xshift;
+
+ // Display the text and set it's Z position
+ if (event == POINTED || (!isJapanMode() && (bSubtitles || !_ctx->bSample))) {
+ int xshift;
+
+ // Get the text string
+ if (TinselV2)
+ LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
+ else
+ LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
+
+ _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ 0, _ctx->textx, _ctx->texty, GetTagFontHandle(), TXT_CENTRE);
+ assert(_ctx->pText); // PrintObj() string produced NULL text
+
+ MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
+
+ if (TinselV2)
+ KeepOnScreen(_ctx->pText, &_ctx->textx, &_ctx->texty);
+ else {
+ // Don't go off the side of the screen
+ xshift = MultiLeftmost(_ctx->pText);
+ if (xshift < 0) {
+ MultiMoveRelXY(_ctx->pText, - xshift, 0);
+ _ctx->textx -= xshift;
+ }
+ xshift = MultiRightmost(_ctx->pText);
+ if (xshift > SCREEN_WIDTH) {
+ MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
+ _ctx->textx += SCREEN_WIDTH - xshift;
+ }
+ }
+ } else
+ _ctx->pText = NULL;
+
+ if (TinselV2) {
+ if (event == POINTED) {
+ /*
+ * PrintObj() called from POINT event
+ */
+ // Have to give way to non-POINTED-generated text
+ // and go away if the item gets picked up
+ int x, y;
+ do {
+ // Give up if this item gets picked up
+ if (WhichItemHeld() == pinvo->id)
+ break;
+
+ // Give way to non-POINTED-generated text
+ if (bNotPointedRunning) {
+ // Delete the text, and wait for the all-clear
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
+ _ctx->pText = NULL;
+
+ while (bNotPointedRunning)
+ CORO_SLEEP(1);
+
+ GetCursorXY(&x, &y, false);
+ if (InvItem(&x, &y, false) != _ctx->item)
+ break;
+
+ // Re-display in the same place
+ LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
+ _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, _ctx->textx, _ctx->texty, GetTagFontHandle(),
+ TXT_CENTRE, 0);
+ assert(_ctx->pText);
+
+ KeepOnScreen(_ctx->pText, &_ctx->textx, &_ctx->texty);
+ MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
+ }
+
+ CORO_SLEEP(1);
+
+ // Carry on until the cursor leaves this icon
+ GetCursorXY(&x, &y, false);
+
+ } while(InvItemId(x, y) == pinvo->id);
+ } else {
+ /*
+ * PrintObj() called from other event
+ */
+ _ctx->myEscape = GetLeftEvents();
+ _ctx->bTookControl = GetControl();
+
+ // Display for a time, but abort if conversation gets hidden
+ if (_ctx->pText)
+ _ctx->ticks = TextTime(TextBufferAddr());
+ _ctx->timeout = SAMPLETIMEOUT;
+
+ for (;;) {
+ CORO_SLEEP(1);
+
+ // Abort if left click - hardwired feature for talky-print!
+ // Abort if sample times out
+ // Abort if conversation hidden
+ if (LeftEventChange(_ctx->myEscape)
+ || --_ctx->timeout <= 0
+ || ConvIsHidden())
+ break;
+
+ if (_ctx->bSample) {
+ // Wait for sample to end whether or not
+ if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
+ if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) {
+ // No text or speed modification - just depends on sample
+ break;
+ } else {
+ // Must wait for time
+ _ctx->bSample = false;
+ }
+ }
+ } else {
+ // No sample - just depends on time
+ if (_ctx->ticks-- <= 0)
+ break;
+ }
+ }
+
+ if (_ctx->bTookControl)
+ ControlOn(); // Free control if we took it
+ }
+
+ } else {
+ if (event == POINTED) {
+ // FIXME: Is there ever an associated sound if in POINTED mode???
+ assert(!_vm->_sound->sampleExists(hText));
+ CORO_INVOKE_ARGS(PrintObjPointed, (CORO_SUBCTX, hText, pinvo, _ctx->pText, _ctx->textx, _ctx->texty, _ctx->item));
+ } else {
+ CORO_INVOKE_2(PrintObjNonPointed, hText, _ctx->pText);
+ }
}
- } else
- _ctx->pText = NULL;
- if (event == POINTED) {
- // FIXME: Is there ever an associated sound if in POINTED mode???
- assert(!_vm->_sound->sampleExists(text));
- CORO_INVOKE_ARGS(printobjPointed, (CORO_SUBCTX, text, pinvo, _ctx->pText, _ctx->textx, _ctx->texty, _ctx->item));
- } else {
- CORO_INVOKE_2(printobjNonPointed, text, _ctx->pText);
+ // Delete the text, if haven't already
+ if (_ctx->pText)
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
+
+ // If it hasn't already finished, stop sample
+ if (_ctx->bSample)
+ _vm->_mixer->stopHandle(_ctx->handle);
}
- // Delete the text, if haven't already
- if (_ctx->pText)
- MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
+ // Let POINTED text back in if this is the last
+ if (event != POINTED)
+ bNotPointedRunning = false;
CORO_END_CODE;
}
-static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
+static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
@@ -1156,11 +2222,11 @@ static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
break;
// Give way to non-POINTED-generated text
- if (NotPointedRunning) {
+ if (bNotPointedRunning) {
// Delete the text, and wait for the all-clear
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), pText);
pText = NULL;
- while (NotPointedRunning)
+ while (bNotPointedRunning)
CORO_SLEEP(1);
GetCursorXY(&x, &y, false);
@@ -1168,10 +2234,10 @@ static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
break;
// Re-display in the same place
- LoadStringRes(text, tBufferAddr(), TBUFSZ);
- pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
- 0, textx, texty, hTagFontHandle(), TXT_CENTRE);
- assert(pText); // printobj() string produced NULL text
+ LoadStringRes(text, TextBufferAddr(), TBUFSZ);
+ pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ 0, textx, texty, GetTagFontHandle(), TXT_CENTRE);
+ assert(pText); // PrintObj() string produced NULL text
MultiSetZPosition(pText, Z_INV_ITEXT);
}
@@ -1184,7 +2250,7 @@ static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
CORO_END_CODE;
}
-static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText) {
+static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText) {
CORO_BEGIN_CONTEXT;
bool bSample; // Set if a sample is playing
Audio::SoundHandle handle;
@@ -1210,7 +2276,7 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p
if (isJapanMode())
_ctx->ticks = JAP_TEXT_TIME;
else if (pText)
- _ctx->ticks = TextTime(tBufferAddr());
+ _ctx->ticks = TextTime(TextBufferAddr());
else
_ctx->ticks = 0;
@@ -1222,7 +2288,7 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p
// Abort if left click - hardwired feature for talky-print!
// Abort if sample times out
// Abort if conversation hidden
- if (_ctx->myleftEvent != GetLeftEvents() || _ctx->timeout <= 0 || convHid())
+ if (_ctx->myleftEvent != GetLeftEvents() || _ctx->timeout <= 0 || ConvIsHidden())
break;
if (_ctx->bSample) {
@@ -1243,10 +2309,10 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p
}
} while (1);
- NotPointedRunning = false; // Let POINTED text back in
+ bNotPointedRunning = false; // Let POINTED text back in
if (_ctx->took_control)
- control(CONTROL_ON); // Free control if we took it
+ Control(CONTROL_ON); // Free control if we took it
_vm->_mixer->stopHandle(_ctx->handle);
@@ -1256,28 +2322,38 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p
/**
* Register the fact that this poly would like its tag displayed.
*/
-void printtag(HPOLYGON hp, SCNHANDLE text) {
- assert(hp != NOPOLY); // printtag() may only be called from a polygon code block
-
- if (PolyTagState(hp) == TAG_OFF) {
- SetPolyTagState(hp, TAG_ON);
- SetPolyTagHandle(hp, text);
+static void PrintTag(HPOLYGON hp, SCNHANDLE text, int actor = 0, bool bCursor = false) {
+ // printtag() may only be called from a polygon code block in Tinsel 1, or
+ // additionally from a moving actor code block in Tinsel 2
+ assert((hp != NOPOLY) || (TinselV2 && (actor != 0)));
+
+ if (hp != NOPOLY) {
+ // Poly handling
+ if (TinselV2)
+ SetPolyTagWanted(hp, true, bCursor, text);
+ else if (PolyTagState(hp) == TAG_OFF) {
+ SetPolyTagState(hp, TAG_ON);
+ SetPolyTagHandle(hp, text);
+ }
+ } else {
+ // Moving actor handling
+ SetActorTagWanted(actor, true, bCursor, text);
}
}
/**
- * quitgame
+ * Quits the game
*/
-void quitgame(void) {
- stopmidi();
- stopsample();
+static void QuitGame(void) {
+ StopMidi();
+ StopSample();
_vm->quitGame();
}
/**
* Return a random number between optional limits.
*/
-int dw_random(int n1, int n2, int norpt) {
+static int RandomFn(int n1, int n2, int norpt) {
int i = 0;
uint32 value;
@@ -1290,44 +2366,71 @@ int dw_random(int n1, int n2, int norpt) {
}
/**
- * resetidletime
+ * ResetIdleTime
*/
-void resetidletime(void) {
+void ResetIdleTime(void) {
resetUserEventTime();
}
/**
* restartgame
*/
-void restartgame(void) {
- stopmidi();
- stopsample();
+static void RestartGame(void) {
+ // TODO: Tinsel 2 comments out the 2 calls, but I'm not sure that this should be done
+ StopMidi();
+ StopSample();
bRestart = true;
}
/**
* Restore saved scene.
*/
-void restore_scene(bool bFade) {
- UnSuspendHook();
- PleaseRestoreScene(bFade);
+static void RestoreScene(CORO_PARAM, TRANSITS transition) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (TinselV2) {
+ if (MoviePlaying()) {
+ AbortMovie();
+ CORO_SLEEP(2);
+ }
+
+ CuttingScene(false);
+
+ } else {
+ UnSuspendHook();
+ }
+
+ TinselRestoreScene(transition == TRANS_FADE);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Resumes the last game
+ */
+void ResumeLastGame(void) {
+ RestoreGame(NewestSavedGame());
}
/**
- * runmode
+ * Returns the current run mode
*/
-int runmode(void) {
+static int RunMode(void) {
return clRunMode;
}
/**
- * sampleplaying
+ * SamplePlaying
*/
-bool sampleplaying(bool escOn, int myescEvent) {
+static bool SamplePlaying(bool escOn, int myEscape) {
// escape effects introduced 14/12/95 to fix
// while (sampleplaying()) pause;
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return false;
return _vm->_sound->sampleIsPlaying();
@@ -1336,73 +2439,169 @@ bool sampleplaying(bool escOn, int myescEvent) {
/**
* Save current scene.
*/
-void save_scene(CORO_PARAM) {
- PleaseSaveScene(coroParam);
- SuspendHook();
+void SaveScene(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (TinselV2) {
+ CuttingScene(true);
+ SendSceneTinselProcess(LEAVE_T2);
+ CORO_GIVE_WAY;
+
+ CORO_INVOKE_0(TinselSaveScene);
+ } else {
+ CORO_INVOKE_0(TinselSaveScene);
+ SuspendHook();
+ }
+
+ CORO_END_CODE;
}
/**
- * scalingreels
+ * ScalingReels
*/
-void scalingreels(int actor, int scale, int direction,
+static void ScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
- setscalingreels(actor, scale, direction, left, right, forward, away);
+ SetScalingReels(actor, scale, direction, left, right, forward, away);
}
/**
* Return the icon that caused the CONVERSE event.
*/
-
-int scanicon(void) {
- return convIcon();
+static int ScanIcon(void) {
+ return GetIcon();
}
/**
* Scroll the screen to target co-ordinates.
*/
-
-void scroll(CORO_PARAM, int x, int y, int iter, bool comp, bool escOn, int myescEvent) {
+static void Scroll(CORO_PARAM, EXTREME extreme, int xp, int yp, int xIter, int yIter, bool bComp, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
- int mycount;
+ int thisScroll;
+ int x, y;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- if (escOn && myescEvent != GetEscEvents()) {
+
+ ++scrollNumber;
+ _ctx->x = xp;
+ _ctx->y = yp;
+
+ if ((TinselV2 && bInstantScroll) || (escOn && myEscape != GetEscEvents())) {
// Instant completion!
- offset(x, y);
+ Offset(extreme, _ctx->x, _ctx->y);
} else {
- _ctx->mycount = ++scrollCount;
+ _ctx->thisScroll = scrollNumber;
+ if (TinselV2)
+ DecodeExtreme(extreme, &_ctx->x, &_ctx->y);
- ScrollTo(x, y, iter);
+ ScrollTo(_ctx->x, _ctx->y, xIter, yIter);
- if (comp) {
+ if (bComp) {
int Loffset, Toffset;
do {
CORO_SLEEP(1);
// If escapable and ESCAPE is pressed...
- if (escOn && myescEvent != GetEscEvents()) {
+ if (escOn && myEscape != GetEscEvents()) {
// Instant completion!
- offset(x, y);
+ Offset(extreme, _ctx->x, _ctx->y);
break;
}
// give up if have been superseded
- if (_ctx->mycount != scrollCount)
+ if (_ctx->thisScroll != scrollNumber)
CORO_KILL_SELF();
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- } while (Loffset != x || Toffset != y);
+ } while (Loffset != _ctx->x || Toffset != _ctx->y);
+ } else if (TinselV2 && myEscape) {
+ static SCROLL_MONITOR sm;
+
+ // Scroll is escapable even though we're not waiting for it
+ sm.x = _ctx->x;
+ sm.y = _ctx->y;
+ sm.thisScroll = scrollNumber;
+ sm.myEscape = myEscape;
+ g_scheduler->createProcess(PID_TCODE, ScrollMonitorProcess, &sm, sizeof(sm));
}
}
CORO_END_CODE;
}
/**
+ * ScrollParameters
+ */
+static void ScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
+ int yTriggerBottom, int yDistance, int ySpeed) {
+ SetScrollParameters(xTrigger, xDistance, xSpeed,
+ yTriggerTop, yTriggerBottom, yDistance, ySpeed);
+}
+
+/**
+ * SendActor("actor", event)
+ */
+int SendActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
+ bool result;
+
+ if (IsTaggedActor(actor)) {
+ assert(actor);
+ ActorEvent(coroParam, actor, event, true, myEscape, &result);
+ } else {
+ SendTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape, &result);
+ }
+
+ return result;
+}
+
+/**
+ * SendGlobalProcess(process#, event)
+ */
+static int SendGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
+ return GlobalProcessEvent(coroParam, procId, event, true, myEscape);
+}
+
+/**
+ * SendObject(object, event)
+ */
+static int SendObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
+ bool result;
+ ObjectEvent(coroParam, object, event, true, myEscape, &result);
+ return result;
+}
+
+/**
+ * SendProcess(process#, event)
+ */
+static int SendProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
+ bool result;
+ SceneProcessEvent(coroParam, procId, event, true, myEscape, &result);
+ return result;
+}
+
+/**
+ * SendTag(tag#, event)
+ */
+static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result) {
+ // Tag could be zero, meaning calling tag
+ if (tagno == 0) {
+ assert(hp != NOPOLY);
+
+ PolygonEvent(coroParam, hp, event, 0, true, myEscape, result);
+ } else {
+ assert(IsTagPolygon(tagno));
+
+ PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, true, myEscape, result);
+ }
+}
+
+/**
* Un-kill an actor.
*/
-void setactor(int actor) {
+static void SetActor(int actor) {
EnableActor(actor);
}
@@ -1410,7 +2609,7 @@ void setactor(int actor) {
* Turn a blocking polygon on.
*/
-void setblock(int blockno) {
+static void SetBlock(int blockno) {
EnableBlock(blockno);
}
@@ -1418,21 +2617,21 @@ void setblock(int blockno) {
* Turn an exit on.
*/
-void setexit(int exitno) {
+static void SetExit(int exitno) {
EnableExit(exitno);
}
/**
* Guess what.
*/
-void setinvlimit(int invno, int n) {
+static void SetInvLimit(int invno, int n) {
InvSetLimit(invno, n);
}
/**
* Guess what.
*/
-void setinvsize(int invno, int MinWidth, int MinHeight,
+static void SetInvSize(int invno, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
InvSetSize(invno, MinWidth, MinHeight, StartWidth, StartHeight, MaxWidth, MaxHeight);
}
@@ -1440,7 +2639,7 @@ void setinvsize(int invno, int MinWidth, int MinHeight,
/**
* Guess what.
*/
-void setlanguage(LANGUAGE lang) {
+static void SetLanguage(LANGUAGE lang) {
assert(lang == TXT_ENGLISH || lang == TXT_FRENCH
|| lang == TXT_GERMAN || lang == TXT_ITALIAN
|| lang == TXT_SPANISH); // ensure language is valid
@@ -1451,191 +2650,332 @@ void setlanguage(LANGUAGE lang) {
/**
* Set palette
*/
-void setpalette(SCNHANDLE hPal, bool escOn, int myescEvent) {
+static void SetPalette(SCNHANDLE hPal, bool escOn, int myEscape) {
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
ChangePalette(hPal);
}
/**
+ * SetSystemString
+ */
+
+static void SetSystemString(int stringId, SCNHANDLE hString) {
+ SetSysString(stringId, hString);
+}
+
+/**
+ * Set a system variable
+ */
+static void SetSystemVar(int varId, int newValue) {
+ SetSysVar(varId, newValue);
+}
+
+/**
* Turn a tag on.
*/
-void settag(int tagno) {
- EnableTag(tagno);
+static void SetTag(CORO_PARAM, int tagno) {
+ EnableTag(coroParam, tagno);
}
/**
* Initialise a timer.
*/
-void settimer(int timerno, int start, bool up, bool frame) {
- DwSetTimer(timerno, start, up != 0, frame != 0);
+static void SetTimer(int timerno, int start, bool up, bool frame) {
+ StartTimer(timerno, start, up != 0, frame != 0);
+}
+
+/**
+ * Shell("cmdline")
+ */
+static void Shell(SCNHANDLE commandLine) {
+ LoadStringRes(commandLine, TextBufferAddr(), TBUFSZ);
+ error("Tried to execute shell command \"%s\"", TextBufferAddr());
+}
+
+/**
+ * Don't hide an actors graphics.
+ */
+static void ShowActorFn(CORO_PARAM, int actor) {
+ ShowActor(coroParam, actor);
+}
+
+/**
+ * Turn a blocking polygon on.
+ */
+void ShowBlock(int blockno) {
+ EnableBlock(blockno);
+}
+
+/**
+ * Turn an effect polygon on.
+ */
+void ShowEffect(int effect) {
+ EnableEffect(effect);
}
#ifdef DEBUG
/**
* Enable display of diagnostic co-ordinates.
*/
-void showpos(void) {
+static void showpos(void) {
setshowpos();
}
/**
* Enable display of diagnostic co-ordinates.
*/
-void showstring(void) {
+static void showstring(void) {
setshowstring();
}
#endif
/**
+ * Shows the main menu
+ */
+static void ShowMenu(void) {
+ OpenMenu(MAIN_MENU);
+}
+
+/**
+ * Turn a path on.
+ */
+static void ShowPath(int path) {
+ EnablePath(path);
+}
+
+/**
+ * Turn a refer on.
+ */
+void ShowRefer(int refer) {
+ EnableRefer(refer);
+}
+
+/**
+ * Turn a tag on.
+ */
+static void ShowTag(CORO_PARAM, int tag, HPOLYGON hp) {
+ // Tag could be zero, meaning calling tag
+ EnableTag(coroParam, tag ? tag : GetTagPolyId(hp));
+}
+
+/**
* Special play - slow down associated actor's movement while the play
* is running. After the play, position the actor where the play left
* it and continue walking, if the actor still is.
*/
-
-void splay(CORO_PARAM, int sf, SCNHANDLE film, int x, int y, bool complete, int actorid, bool escOn, int myescEvent) {
+static void SPlay(CORO_PARAM, int sf, SCNHANDLE film, int x, int y, bool complete, int actorid, bool escOn, int myEscape) {
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
- play(coroParam, film, x, y, complete, actorid, true, sf, escOn, myescEvent, false);
+ Play(coroParam, film, x, y, complete, actorid, true, sf, escOn, myEscape, false);
}
/**
* (Re)Position an actor.
* If moving actor is not around yet in this scene, start it up.
*/
+void Stand(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm) {
+ CORO_BEGIN_CONTEXT;
+ PMOVER pMover; // Moving actor structure
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
-void stand(int actor, int x, int y, SCNHANDLE film) {
- PMACTOR pActor; // Moving actor structure
+ _ctx->pMover = GetMover(actor);
+ assert(!TinselV2 || (_ctx->pMover != NULL));
+
+ if (_ctx->pMover) {
+ if (TinselV2) {
+ // New special. If no paths, just ignore this
+ if (PathCount() == 0)
+ return;
+
+ // Another new special.
+ // If lead actor, and TalkVia, ignore
+ if ((actor == GetLeadId() || actor == LEAD_ACTOR) && SysVar(ISV_DIVERT_ACTOR))
+ return;
+ }
- pActor = GetMover(actor);
- if (pActor) {
- if (pActor->MActorState == NO_MACTOR) {
+ if (!MoverIs(_ctx->pMover)) {
// create a moving actor process
- MActorProcessCreate(x, y, (actor == LEAD_ACTOR) ? LeadId() : actor, pActor);
+ MoverProcessCreate(x, y, (actor == LEAD_ACTOR) ? GetLeadId() : actor, _ctx->pMover);
- if (film == TF_NONE) {
- SetMActorStanding(pActor);
+ if (hFilm == TF_NONE) {
+ // Make sure there is an assigned actorObj
+ while (!_ctx->pMover->actorObj)
+ CORO_SLEEP(1);
+
+ SetMoverStanding(_ctx->pMover);
} else {
- switch (film) {
- case TF_NONE:
- break;
-
- case TF_UP:
- SetMActorDirection(pActor, AWAY);
- SetMActorStanding(pActor);
- break;
- case TF_DOWN:
- SetMActorDirection(pActor, FORWARD);
- SetMActorStanding(pActor);
- break;
- case TF_LEFT:
- SetMActorDirection(pActor, LEFTREEL);
- SetMActorStanding(pActor);
- break;
- case TF_RIGHT:
- SetMActorDirection(pActor, RIGHTREEL);
- SetMActorStanding(pActor);
- break;
-
- default:
- AlterMActor(pActor, film, AR_NORMAL);
- break;
+ // Check hFilm against certain constants. Note that a switch statement isn't
+ // used here because it would interfere with our co-routine implementation
+ if (hFilm == TF_UP) {
+ if (TinselV2) CORO_GIVE_WAY;
+ SetMoverDirection(_ctx->pMover, AWAY);
+ SetMoverStanding(_ctx->pMover);
+ } else if (hFilm == TF_DOWN) {
+ if (TinselV2) CORO_GIVE_WAY;
+ SetMoverDirection(_ctx->pMover, FORWARD);
+ SetMoverStanding(_ctx->pMover);
+ } else if (hFilm == TF_LEFT) {
+ if (TinselV2) CORO_GIVE_WAY;
+ SetMoverDirection(_ctx->pMover, LEFTREEL);
+ SetMoverStanding(_ctx->pMover);
+ } else if (hFilm == TF_RIGHT) {
+ if (TinselV2) CORO_GIVE_WAY;
+ SetMoverDirection(_ctx->pMover, RIGHTREEL);
+ SetMoverStanding(_ctx->pMover);
+ } else if (hFilm != TF_NONE) {
+ if (TinselV2) CORO_GIVE_WAY;
+ AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
}
}
} else {
- switch (film) {
+ switch (hFilm) {
case TF_NONE:
if (x != -1 && y != -1)
- MoveMActor(pActor, x, y);
+ PositionMover(_ctx->pMover, x, y);
break;
case TF_UP:
- SetMActorDirection(pActor, AWAY);
+ SetMoverDirection(_ctx->pMover, AWAY);
if (x != -1 && y != -1)
- MoveMActor(pActor, x, y);
- SetMActorStanding(pActor);
+ PositionMover(_ctx->pMover, x, y);
+ SetMoverStanding(_ctx->pMover);
break;
case TF_DOWN:
- SetMActorDirection(pActor, FORWARD);
+ SetMoverDirection(_ctx->pMover, FORWARD);
if (x != -1 && y != -1)
- MoveMActor(pActor, x, y);
- SetMActorStanding(pActor);
+ PositionMover(_ctx->pMover, x, y);
+ SetMoverStanding(_ctx->pMover);
break;
case TF_LEFT:
- SetMActorDirection(pActor, LEFTREEL);
+ SetMoverDirection(_ctx->pMover, LEFTREEL);
if (x != -1 && y != -1)
- MoveMActor(pActor, x, y);
- SetMActorStanding(pActor);
+ PositionMover(_ctx->pMover, x, y);
+ SetMoverStanding(_ctx->pMover);
break;
case TF_RIGHT:
- SetMActorDirection(pActor, RIGHTREEL);
+ SetMoverDirection(_ctx->pMover, RIGHTREEL);
if (x != -1 && y != -1)
- MoveMActor(pActor, x, y);
- SetMActorStanding(pActor);
+ PositionMover(_ctx->pMover, x, y);
+ SetMoverStanding(_ctx->pMover);
break;
default:
if (x != -1 && y != -1)
- MoveMActor(pActor, x, y);
- AlterMActor(pActor, film, AR_NORMAL);
+ PositionMover(_ctx->pMover, x, y);
+ AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
break;
}
}
} else if (actor == NULL_ACTOR) {
//
} else {
- assert(film != 0); // Trying to play NULL film
+ assert(hFilm != 0); // Trying to play NULL film
// Kick off the play and return.
- playFilm(film, x, y, actor, false, 0, false, 0, false);
+ CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actor, false, 0, false, 0, false));
}
+
+ CORO_END_CODE;
}
/**
* Position the actor at the polygon's tag node.
*/
-void standtag(int actor, HPOLYGON hp) {
- SCNHANDLE film;
+static void StandTag(int actor, HPOLYGON hp) {
+ SCNHANDLE hFilm;
int pnodex, pnodey;
- assert(hp != NOPOLY); // standtag() may only be called from a polygon code block
+ assert(hp != NOPOLY); // StandTag() may only be called from a polygon code block
+
+ // Where to stand
+ GetPolyNode(hp, &pnodex, &pnodey);
// Lead actor uses tag node film
- film = getPolyFilm(hp);
- getPolyNode(hp, &pnodex, &pnodey);
- if (film && (actor == LEAD_ACTOR || actor == LeadId()))
- stand(actor, pnodex, pnodey, film);
+ hFilm = GetPolyFilm(hp);
+
+ // other actors can use direction
+ if (TinselV2) {
+ if (actor != LEAD_ACTOR && actor != GetLeadId()
+ && hFilm != TF_UP && hFilm != TF_DOWN
+ && hFilm != TF_LEFT && hFilm != TF_RIGHT)
+ hFilm = 0;
+
+ Stand(nullContext, actor, pnodex, pnodey, hFilm);
+
+ } else if (hFilm && (actor == LEAD_ACTOR || actor == GetLeadId()))
+ Stand(nullContext, actor, pnodex, pnodey, hFilm);
else
- stand(actor, pnodex, pnodey, 0);
+ Stand(nullContext, actor, pnodex, pnodey, 0);
}
+
/**
- * Kill a moving actor's walk.
+ * StartGlobalProcess
*/
-void stop(int actor) {
- PMACTOR pActor;
+static void StartGlobalProcess(CORO_PARAM, uint32 procID) {
+ GlobalProcessEvent(coroParam, procID, STARTUP, false, 0);
+}
- pActor = GetMover(actor);
- assert(pActor); // Trying to stop a null actor
+/**
+ * StartProcess
+ */
+static void StartProcess(CORO_PARAM, uint32 procID) {
+ SceneProcessEvent(coroParam, procID, STARTUP, false, 0);
+}
- GetToken(pActor->actorToken); // Kill the walk process
- pActor->stop = true; // Cause the actor to stop
- FreeToken(pActor->actorToken);
+/**
+ * Initialise a timer.
+ */
+static void StartTimerFn(int timerno, int start, bool up, int fs) {
+ StartTimer(timerno, start, up, fs);
}
-void stopmidi(void) {
+void StopMidiFn(void) {
StopMidi(); // Stop any currently playing midi
}
-void stopsample(void) {
- _vm->_sound->stopAllSamples(); // Stop any currently playing sample
+/**
+ * Kill a specific sample, or all samples.
+ */
+void StopSample(int sample) {
+ if (sample == -1)
+ _vm->_sound->stopAllSamples(); // Stop any currently playing sample
+ else
+ _vm->_sound->stopSpecSample(sample, 0);
+}
+
+/**
+ * Kill a moving actor's walk.
+ */
+static void StopWalk(int actor) {
+ PMOVER pMover;
+
+ pMover = GetMover(actor);
+ assert(pMover);
+
+ if (TinselV2) {
+ if (MoverHidden(pMover))
+ return;
+
+ StopMover(pMover); // Cause the actor to stop
+ } else {
+ GetToken(pMover->actorToken); // Kill the walk process
+ pMover->bStop = true; // Cause the actor to stop
+ FreeToken(pMover->actorToken);
+ }
}
-void subtitles(int onoff) {
+/**
+ * Subtitles on/off
+ */
+static void Subtitles(int onoff) {
assert (onoff == ST_ON || onoff == ST_OFF);
if (isJapanMode())
@@ -1651,271 +2991,468 @@ void subtitles(int onoff) {
* Special walk.
* Walk into or out of a legal path.
*/
-void swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, bool escOn, int myescEvent) {
+static void Swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, int zOverride, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
- bool took_control; // Set if this function takes control
+ bool bTookControl; // Set if this function takes control
CORO_END_CONTEXT(_ctx);
+ HPOLYGON hPath;
+
CORO_BEGIN_CODE(_ctx);
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents()) {
+ if (TinselV2) {
+ if (x2 == -1 && y2 == -1)
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
+ else
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0));
+ }
+
return;
+ }
// For lead actor, lock out the user (if not already locked out)
- if (actor == LeadId() || actor == LEAD_ACTOR)
- _ctx->took_control = GetControl(CONTROL_OFFV2);
- else
- _ctx->took_control = false;
-
- HPOLYGON hPath;
+ if (actor == GetLeadId() || actor == LEAD_ACTOR) {
+ _ctx->bTookControl = GetControl(CONTROL_OFFV2);
+ if (TinselV2 && _ctx->bTookControl)
+ RestoreMainCursor();
+ } else {
+ _ctx->bTookControl = false;
+ }
- hPath = InPolygon(x1, y1, PATH);
- if (hPath != NOPOLY) {
- // Walking out of a path
- stand(actor, x1, y1, 0);
+ if (TinselV2 && (x2 == -1) && (y2 == -1)) {
+ // First co-ordinates are the destination
+ x2 = x1;
+ y2 = y1;
} else {
- hPath = InPolygon(x2, y2, PATH);
- // One of them has to be in a path
- assert(hPath != NOPOLY); //one co-ordinate must be in a legal path
+ // Stand at the start co-ordinates
+ hPath = InPolygon(x1, y1, PATH);
+
+ if (hPath != NOPOLY) {
+ // Walking out of a path
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
+ } else {
+ hPath = InPolygon(x2, y2, PATH);
+ // One of them has to be in a path
+ assert(hPath != NOPOLY); //one co-ordinate must be in a legal path
- // Walking into a path
- stand(actor, x2, y2, 0); // Get path's characteristics
- stand(actor, x1, y1, 0);
+ // Walking into a path
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0)); // Get path's characteristics
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
+ }
+
+ if (TinselV2 && (zOverride != -1)) {
+ PMOVER pMover = GetMover(actor);
+ assert(pMover);
+
+ SetMoverZ(pMover, y1, zOverride);
+ }
}
- CORO_INVOKE_ARGS(walk, (CORO_SUBCTX, actor, x2, y2, film, 0, true, escOn, myescEvent));
+ CORO_INVOKE_ARGS(Walk, (CORO_SUBCTX, actor, x2, y2, film, 0, true, zOverride, escOn, myEscape));
// Free control if we took it
- if (_ctx->took_control)
- control(CONTROL_ON);
+ if (_ctx->bTookControl)
+ Control(CONTROL_ON);
CORO_END_CODE;
}
/**
- * Define a tagged actor.
+ * Gets a system variable
*/
+static int SystemVar(int varId) {
+ return SysVar(varId);
+}
-void tagactor(int actor, SCNHANDLE text, int tp) {
+/**
+ * Define a tagged actor.
+ */
+static void TagActor(int actor, SCNHANDLE text, int tp) {
Tag_Actor(actor, text, tp);
}
/**
+ * TagPos([tag #])
+ */
+static int TagPos(MASTER_LIB_CODES operand, int tagno, HPOLYGON hp) {
+ int x, y;
+
+ // Tag could be zero, meaning calling tag
+ if (tagno == 0)
+ tagno = GetTagPolyId(hp);
+
+ if (operand == TAGTAGXPOS || operand == TAGTAGYPOS) {
+ SCNHANDLE junk;
+
+ GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
+ } else {
+ GetPolyNode(GetTagHandle(tagno), &x, &y);
+ }
+
+ if (operand == TAGTAGXPOS || operand == TAGWALKXPOS)
+ return x;
+ else
+ return y;
+}
+
+/**
* Text goes over actor's head while actor plays the talk reel.
*/
+static void FinishTalkingReel(CORO_PARAM, PMOVER pMover, int actor) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
-void FinishTalkingReel(PMACTOR pActor, int actor) {
- if (pActor) {
- SetMActorStanding(pActor);
- AlterMActor(pActor, 0, AR_POPREEL);
+ if (pMover) {
+ SetMoverStanding(pMover);
+ AlterMover(pMover, 0, AR_POPREEL);
} else {
- setActorTalking(actor, false);
- playFilm(getActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false);
+ SetActorTalking(actor, false);
+ CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, GetActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false));
}
+
+ CORO_END_CODE;
}
-void talk(CORO_PARAM, SCNHANDLE film, const SCNHANDLE text, int actorid, bool escOn, int myescEvent) {
+static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x, int y,
+ SCNHANDLE hFilm, int actorId, bool bSustain, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int Loffset, Toffset; // Top left of display
int actor; // The speaking actor
- PMACTOR pActor; // For moving actors
- int myleftEvent;
+ PMOVER pActor; // For moving actors
+ int myLeftEvent;
+ int escEvents;
int ticks;
bool bTookControl; // Set if this function takes control
bool bTookTags; // Set if this function disables tags
OBJECT *pText; // text object pointer
bool bSample; // Set if a sample is playing
+ bool bSamples;
bool bTalkReel; // Set while talk reel is playing
Audio::SoundHandle handle;
int timeout;
+
+ SPEECH_TYPE whatSort;
+ TFTYPE direction;
+ int sub;
+ int x, y;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
+ _ctx->whatSort = speechType;
+ _ctx->escEvents = myEscape;
+ _ctx->x = x;
+ _ctx->y = y;
_ctx->Loffset = 0;
_ctx->Toffset = 0;
_ctx->ticks = 0;
+ _ctx->pText = NULL;
+
+ // If waiting is enabled, wait for ongoing scroll
+ if (TinselV2 && SysVar(SV_SPEECHWAITS))
+ CORO_INVOKE_1(WaitScroll, myEscape);
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
- _ctx->myleftEvent = GetLeftEvents();
+ _ctx->myLeftEvent = GetLeftEvents();
// If this actor is dead, call a stop to the calling process
- if (actorid && !actorAlive(actorid))
+ if (!TinselV2 && (actorId && !actorAlive(actorId)))
CORO_KILL_SELF();
- /*
- * Find out which actor is talking
- * and with which direction if no film supplied
- */
- TFTYPE direction;
- switch (film) {
- case TF_NONE:
- case TF_UP:
- case TF_DOWN:
- case TF_LEFT:
- case TF_RIGHT:
- _ctx->actor = LeadId(); // If no film, actor is lead actor
- direction = (TFTYPE)film;
- break;
+ if (!TinselV2 || (speechType == IS_TALK)) {
+ /*
+ * Find out which actor is talking
+ * and with which direction if no film supplied
+ */
+ switch (hFilm) {
+ case TF_NONE:
+ case TF_UP:
+ case TF_DOWN:
+ case TF_LEFT:
+ case TF_RIGHT:
+ _ctx->actor = GetLeadId(); // If no film, actor is lead actor
+ _ctx->direction = (TFTYPE)hFilm;
+ break;
- default:
- _ctx->actor = extractActor(film);
- assert(_ctx->actor); // talk() - no actor ID in the reel
- direction = TF_BOGUS;
- break;
- }
+ default:
+ _ctx->actor = ExtractActor(hFilm);
+ assert(_ctx->actor); // talk() - no actor ID in the reel
+ _ctx->direction = TF_FILM;
+ break;
+ }
+ assert(_ctx->actor);
+ } else if (TinselV2)
+ _ctx->actor = actorId;
/*
* Lock out the user (for lead actor, if not already locked out)
* May need to disable tags for other actors
*/
- if (_ctx->actor == LeadId())
+ if (_ctx->actor == GetLeadId() || (TinselV2 && (_ctx->actor == LEAD_ACTOR)))
_ctx->bTookControl = GetControl(CONTROL_OFF);
else
_ctx->bTookControl = false;
_ctx->bTookTags = DisableTagsIfEnabled();
+ if (TinselV2) {
+ /*
+ * Divert stuff
+ */
+ if (SysVar(ISV_DIVERT_ACTOR) && (_ctx->actor == GetLeadId() || _ctx->actor == LEAD_ACTOR)) {
+ _ctx->actor = SysVar(ISV_DIVERT_ACTOR);
+ if (_ctx->whatSort == IS_TALK)
+ _ctx->whatSort = IS_SAY;
+ else if (_ctx->whatSort == IS_TALKAT)
+ _ctx->whatSort = IS_SAYAT;
+ }
+ }
+
/*
* Kick off the voice sample
*/
- if (volVoice != 0 && _vm->_sound->sampleExists(text)) {
- _vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
- _ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
+ if (volVoice != 0 && _vm->_sound->sampleExists(hText)) {
+ if (!TinselV2) {
+ _vm->_sound->playSample(hText, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
+ _ctx->bSamples = _vm->_mixer->isSoundHandleActive(_ctx->handle);
+ } else
+ _ctx->bSamples = true;
} else
- _ctx->bSample = false;
+ _ctx->bSamples = false;
/*
* Replace actor with the talk reel, saving the current one
*/
_ctx->pActor = GetMover(_ctx->actor);
- if (_ctx->pActor) {
- if (direction != TF_BOGUS)
- film = GetMactorTalkReel(_ctx->pActor, direction);
- AlterMActor(_ctx->pActor, film, AR_PUSHREEL);
- } else {
- setActorTalking(_ctx->actor, true);
- setActorTalkFilm(_ctx->actor, film);
- playFilm(film, -1, -1, 0, false, 0, escOn, myescEvent, false);
+ if (_ctx->whatSort == IS_TALK) {
+ if (_ctx->pActor) {
+ if (_ctx->direction != TF_FILM)
+ hFilm = GetMoverTalkReel(_ctx->pActor, _ctx->direction);
+ AlterMover(_ctx->pActor, hFilm, AR_PUSHREEL);
+ } else {
+ SetActorTalking(_ctx->actor, true);
+ SetActorTalkFilm(_ctx->actor, hFilm);
+ CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, -1, -1, 0, false, 0, escOn, myEscape, false));
+ }
+ _ctx->bTalkReel = true;
+ CORO_SLEEP(1); // Allow the play to come in
+
+ } else if (_ctx->whatSort == IS_TALKAT) {
+ _ctx->bTalkReel = false;
+
+ } else if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
+ _ctx->bTalkReel = false;
+ if (IsTaggedActor(_ctx->actor)) {
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, TALKING, false, 0));
+ } else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY)) {
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, GetTagHandle(_ctx->actor | ACTORTAG_KEY),
+ TALKING, 0, false, 0));
+ }
+
+ if (TinselV2)
+ // Let it all kick in and position this 'waiting' process
+ // down the process list from the playing process(es)
+ // This ensures immediate return when the reel finishes
+ CORO_GIVE_WAY;
}
- _ctx->bTalkReel = true;
- CORO_SLEEP(1); // Allow the play to come in
- /*
- * Display the text.
- */
- _ctx->pText = NULL;
- if (isJapanMode()) {
- _ctx->ticks = JAP_TEXT_TIME;
- } else if (bSubtitles || !_ctx->bSample) {
- int aniX, aniY; // actor position
- int xshift, yshift;
- /*
- * Work out where to display the text
- */
- PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset);
- GetActorMidTop(_ctx->actor, &aniX, &aniY);
- aniY -= _ctx->Toffset;
-
- setTextPal(getActorTcol(_ctx->actor));
- LoadStringRes(text, tBufferAddr(), TBUFSZ);
- _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
- 0, aniX - _ctx->Loffset, aniY, hTalkFontHandle(), TXT_CENTRE);
- assert(_ctx->pText); // talk() string produced NULL text;
- if (IsTopWindow())
- MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
+ // Make multi-ones escape
+ if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->escEvents)
+ _ctx->escEvents = GetEscEvents();
- /*
- * Set bottom of text just above the speaker's head
- * But don't go off the top of the screen
- */
- yshift = aniY - MultiLowest(_ctx->pText) - 2; // Just above head
- MultiMoveRelXY(_ctx->pText, 0, yshift); //
- yshift = MultiHighest(_ctx->pText);
- if (yshift < 4)
- MultiMoveRelXY(_ctx->pText, 0, 4 - yshift); // Not off top
+ for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
+ if (TinselV2 && _ctx->escEvents && _ctx->escEvents != GetEscEvents())
+ break;
/*
- * Don't go off the side of the screen
+ * Display the text.
*/
- xshift = MultiRightmost(_ctx->pText) + 2;
- if (xshift >= SCREEN_WIDTH) // Not off right
- MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
- xshift = MultiLeftmost(_ctx->pText) - 1;
- if (xshift <= 0) // Not off left
- MultiMoveRelXY(_ctx->pText, -xshift, 0);
- /*
- * Work out how long to talk.
- * During this time, reposition the text if the screen scrolls.
- */
- _ctx->ticks = TextTime(tBufferAddr());
- }
+ _ctx->bSample = _ctx->bSamples;
+ _ctx->pText = NULL;
- _ctx->timeout = SAMPLETIMEOUT;
- do {
- // Keep text in place if scrolling
- if (_ctx->pText != NULL) {
- int nLoff, nToff;
+ if (isJapanMode()) {
+ _ctx->ticks = JAP_TEXT_TIME;
+ } else if (bSubtitles || !_ctx->bSample) {
+ /*
+ * Work out where to display the text
+ */
+ int xshift, yshift;
+
+ PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset);
+ if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK))
+ GetActorMidTop(_ctx->actor, &_ctx->x, &_ctx->y);
+
+ SetTextPal(GetActorRGB(_ctx->actor));
+ if (TinselV2)
+ LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
+ else {
+ LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
+
+ _ctx->y -= _ctx->Toffset;
+ }
- PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
- if (nLoff != _ctx->Loffset || nToff != _ctx->Toffset) {
- MultiMoveRelXY(_ctx->pText, _ctx->Loffset - nLoff, _ctx->Toffset - nToff);
- _ctx->Loffset = nLoff;
- _ctx->Toffset = nToff;
+ _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+ TextBufferAddr(), 0, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset,
+ GetTalkFontHandle(), TXT_CENTRE);
+ assert(_ctx->pText); // talk() string produced NULL text;
+
+ if (IsTopWindow())
+ MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
+
+ if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK)) {
+ /*
+ * Set bottom of text just above the speaker's head
+ * But don't go off the top of the screen
+ */
+ if (TinselV2)
+ MultiMoveRelXY(_ctx->pText, 0, _ctx->y - _ctx->Toffset - MultiLowest(_ctx->pText) - 2);
+ else {
+ yshift = _ctx->y - MultiLowest(_ctx->pText) - 2; // Just above head
+ MultiMoveRelXY(_ctx->pText, 0, yshift); //
+ yshift = MultiHighest(_ctx->pText);
+ if (yshift < 4)
+ MultiMoveRelXY(_ctx->pText, 0, 4 - yshift); // Not off top
+
+ /*
+ * Don't go off the side of the screen
+ */
+ xshift = MultiRightmost(_ctx->pText) + 2;
+ if (xshift >= SCREEN_WIDTH) // Not off right
+ MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
+ xshift = MultiLeftmost(_ctx->pText) - 1;
+ if (xshift <= 0) // Not off left
+ MultiMoveRelXY(_ctx->pText, -xshift, 0);
+ }
}
+
+ if (TinselV2)
+ // Don't go off the screen
+ KeepOnScreen(_ctx->pText, &_ctx->x, &_ctx->y);
+
+ /*
+ * Work out how long to talk.
+ * During this time, reposition the text if the screen scrolls.
+ */
+ _ctx->ticks = TextTime(TextBufferAddr());
}
- CORO_SLEEP(1);
- --_ctx->timeout;
+ if (TinselV2 && _ctx->bSample) {
+ // Kick off the sample now (perhaps with a delay)
+ if (bNoPause)
+ bNoPause = false;
+ else
+ CORO_SLEEP(SysVar(SV_SPEECHDELAY));
- // Abort if escapable and ESCAPE is pressed
- // Abort if left click - hardwired feature for talk!
- // Abort if sample times out
- if ((escOn && myescEvent != GetEscEvents())
- || (_ctx->myleftEvent != GetLeftEvents())
- || (_ctx->timeout <= 0))
- break;
+ //SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
+ _vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
+ }
+
+ _ctx->timeout = SAMPLETIMEOUT;
- if (_ctx->bSample) {
- // Wait for sample to end whether or not
- if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
- if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) {
- // No text or speed modification - just depends on sample
+ do {
+ // Keep text in place if scrolling
+ if (_ctx->pText != NULL) {
+ int nLoff, nToff;
+
+ PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
+ if (nLoff != _ctx->Loffset || nToff != _ctx->Toffset) {
+ MultiMoveRelXY(_ctx->pText, _ctx->Loffset - nLoff, _ctx->Toffset - nToff);
+ _ctx->Loffset = nLoff;
+ _ctx->Toffset = nToff;
+ }
+ }
+
+ CORO_SLEEP(1);
+
+ // Handle timeout decrementing and Escape presses
+ if (TinselV2) {
+ if ((_ctx->escEvents && _ctx->escEvents != GetEscEvents()) ||
+ (!bSustain && LeftEventChange(_ctx->myLeftEvent)) ||
+ (--_ctx->timeout <= 0)) {
+ // Left event only kills current sub-string
+ _ctx->myLeftEvent = GetLeftEvents();
break;
- } else {
- // Talk reel stops at end of speech
- FinishTalkingReel(_ctx->pActor, _ctx->actor);
- _ctx->bTalkReel = false;
- _ctx->bSample = false;
}
+ } else {
+ --_ctx->timeout;
+
+ // Abort if escapable and ESCAPE is pressed
+ // Abort if left click - hardwired feature for talk!
+ // Abort if sample times out
+ if ((escOn && myEscape != GetEscEvents())
+ || (_ctx->myLeftEvent != GetLeftEvents())
+ || (_ctx->timeout <= 0))
+ break;
}
- } else {
- // No sample - just depends on time
- if (_ctx->ticks-- <= 0)
- break;
+
+ if (_ctx->bSample) {
+ // Wait for sample to end whether or not
+ if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
+ if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) {
+ // No text or speed modification - just depends on sample
+ break;
+ } else {
+ // Talk reel stops at end of speech
+ if (!TinselV2 || (_ctx->bTalkReel && (_ctx->sub == SubStringCount(hText) - 1))) {
+ CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
+ _ctx->bTalkReel = false;
+ }
+ _ctx->bSample = false;
+ }
+ }
+ } else {
+ // No sample - just depends on time
+ if (_ctx->ticks-- <= 0)
+ break;
+ }
+ } while (1);
+
+ if (_ctx->pText != NULL) {
+ MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
+ _ctx->pText = NULL;
}
- } while (1);
+ if (TinselV2 && _ctx->bSample)
+ _vm->_sound->stopSpecSample(hText, _ctx->sub);
+ }
/*
* The talk is over now - dump the text
* Stop the sample
* Restore the actor's film or standing reel
*/
+ if (_ctx->bTalkReel)
+ CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
if (_ctx->pText != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
- _vm->_mixer->stopHandle(_ctx->handle);
- if (_ctx->bTalkReel)
- FinishTalkingReel(_ctx->pActor, _ctx->actor);
+
+ if (TinselV2) {
+ if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
+ SetActorTalking(_ctx->actor, false);
+ if (IsTaggedActor(_ctx->actor))
+ CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, ENDTALK, false, 0));
+ else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY))
+ CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX,
+ GetTagHandle(_ctx->actor | ACTORTAG_KEY), ENDTALK, 0, false, 0));
+
+ CORO_SLEEP(1);
+ }
+ } else {
+ _vm->_mixer->stopHandle(_ctx->handle);
+ }
/*
* Restore user control and tags, as appropriate
* And, finally, release the talk token.
*/
- if (_ctx->bTookControl)
- control(CONTROL_ON);
+ if (_ctx->bTookControl) {
+ if (TinselV2) ControlOn(); else Control(CONTROL_ON);
+ }
if (_ctx->bTookTags)
EnableTags();
@@ -1923,135 +3460,238 @@ void talk(CORO_PARAM, SCNHANDLE film, const SCNHANDLE text, int actorid, bool es
}
/**
- * talkat(actor, x, y, text)
+ * TalkAt(actor, x, y, text)
*/
-void talkat(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, bool escOn, int myescEvent) {
+static void TalkAt(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, bool escOn, int myEscape) {
if (!coroParam) {
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
if (!isJapanMode() && (bSubtitles || !_vm->_sound->sampleExists(text)))
- setTextPal(getActorTcol(actor));
+ SetTextPal(GetActorRGB(actor));
}
- print(coroParam, x, y, text, 0, 0, escOn, myescEvent);
+ Print(coroParam, x, y, text, 0, false, escOn, myEscape);
}
/**
- * talkats(actor, x, y, text, sustain)
+ * TalkAtS(actor, x, y, text, sustain)
*/
-void talkats(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, int sustain, bool escOn, int myescEvent) {
+static void TalkAtS(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, int sustain, bool escOn, int myEscape) {
if (!coroParam) {
assert(sustain == 2);
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
if (!isJapanMode())
- setTextPal(getActorTcol(actor));
+ SetTextPal(GetActorRGB(actor));
}
- print(coroParam, x, y, text, 0, sustain, escOn, myescEvent);
+ Print(coroParam, x, y, text, 0, sustain == 2, escOn, myEscape);
}
/**
* Set talk font's palette entry.
*/
-void talkattr(int r1, int g1, int b1, bool escOn, int myescEvent) {
+static void TalkAttr(int r1, int g1, int b1, bool escOn, int myEscape) {
if (isJapanMode())
return;
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
if (r1 > MAX_INTENSITY) r1 = MAX_INTENSITY; // } Ensure
if (g1 > MAX_INTENSITY) g1 = MAX_INTENSITY; // } within limits
if (b1 > MAX_INTENSITY) b1 = MAX_INTENSITY; // }
- setTextPal(RGB(r1, g1, b1));
+ SetTextPal(RGB(r1, g1, b1));
+}
+
+/**
+ * TalkPaletteIndex
+ */
+static void TalkPaletteIndex(unsigned index) {
+ assert(index);
+
+ SetTalkTextOffset(index);
+}
+
+/**
+ * Set talk font's palette entry.
+ */
+static void TalkRGB(COLORREF colour, int myescEvent) {
+ // Don't do it if it's not wanted
+ if (myescEvent && myescEvent != GetEscEvents())
+ return;
+
+ SetTextPal(colour);
+}
+
+/**
+ * TalkVia("actor"/off)
+ */
+static void TalkVia(int actor) {
+ SetSysVar(ISV_DIVERT_ACTOR, actor);
+}
+
+/**
+ * Declare a temporary text font.
+ */
+static void TempTagFont(SCNHANDLE hFilm) {
+ SetTempTagFontHandle(hFilm); // Store the font handle
+}
+
+/**
+ * Declare a temporary text font.
+ */
+static void TempTalkFont(SCNHANDLE hFilm) {
+ SetTempTalkFontHandle(hFilm); // Store the font handle
+}
+
+/**
+ * ThisObject
+ */
+static int ThisObject(INV_OBJECT *pinvo) {
+ assert(pinvo != NULL);
+
+ return pinvo->id;
+}
+
+/**
+ * ThisTag
+ */
+static int ThisTag(HPOLYGON hp) {
+ int tagno;
+
+ assert(hp != NOPOLY);
+
+ tagno = GetTagPolyId(hp);
+
+ assert(IsTagPolygon(tagno));
+ assert(tagno);
+
+ return tagno;
}
/**
* Get a timer's current count.
*/
-int timer(int timerno) {
+static int TimerFn(int timerno) {
return Timer(timerno);
}
/**
+ * Return the icon that caused the CONVERSE event.
+ */
+int Topic(void) {
+ return GetIcon();
+}
+
+/**
* topplay(film, x, y, actor, hold, complete)
*/
-void topplay(CORO_PARAM, SCNHANDLE film, int x, int y, int complete, int actorid, bool splay, int sfact, bool escOn, int myescTime) {
- play(coroParam, film, x, y, complete, actorid, splay, sfact, escOn, myescTime, true);
+static void TopPlay(CORO_PARAM, SCNHANDLE film, int x, int y, int complete, int actorid, bool splay, int sfact, bool escOn, int myescTime) {
+ Play(coroParam, film, x, y, complete, actorid, splay, sfact, escOn, myescTime, true);
+}
+static void TopPlay(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myescEvent, TINSEL_EVENT event) {
+ Play(coroParam, hFilm, x, y, bComplete, myescEvent, true, event, NOPOLY, 0);
}
/**
* Open or close the 'top window'
*/
-void topwindow(int bpos) {
- assert(bpos == TW_START || bpos == TW_END);
+static void TopWindow(int bpos) {
+ bool isStart = (TinselV2 && (bpos != 0)) || (!TinselV2 && (bpos == TW_START));
- switch (bpos) {
- case TW_END:
- KillInventory();
- break;
+ KillInventory();
- case TW_START:
- KillInventory();
- PopUpConf(TOPWIN);
- break;
+ if (isStart)
+ OpenMenu(TOP_WINDOW);
+}
+
+/**
+ * TranslucentIndex
+ */
+static void TranslucentIndex(unsigned index) {
+ assert(index <= 255);
+
+ SetTranslucencyOffset(index);
+}
+
+/**
+ * Play a sample.
+ */
+static void TryPlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+ // Don't do it if it's not appropriate
+ if (_vm->_sound->sampleIsPlaying()) {
+ // return, but prevent Glitter lock-up
+ CORO_SLEEP(1);
+ return;
}
+
+ CORO_INVOKE_ARGS(PlaySample, (CORO_SUBCTX, sample, bComplete, escOn, myEscape));
+ CORO_END_CODE;
+}
+
+/**
+ * UnDimMusic
+ */
+static void UnDimMusic(void) {
+ _vm->_pcmMusic->unDim(true);
}
/**
* unhookscene
*/
-void unhookscene(void) {
+static void UnHookSceneFn(void) {
UnHookScene();
}
/**
* Un-define an actor as tagged.
*/
-void untagactor(int actor) {
+static void UnTagActorFn(int actor) {
UnTagActor(actor);
}
/**
* vibrate
*/
-void vibrate(void) {
+static void Vibrate(void) {
}
/**
* waitframe(int actor, int frameNumber)
*/
-void waitframe(CORO_PARAM, int actor, int frameNumber, bool escOn, int myescEvent) {
+static void WaitFrame(CORO_PARAM, int actor, int frameNumber, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
- // Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
- return;
- while (getActorSteps(actor) < frameNumber) {
- CORO_SLEEP(1);
-
- // Abort if escapable and ESCAPE is pressed
- if (escOn && myescEvent != GetEscEvents())
+ while (GetActorSteps(actor) < frameNumber) {
+ // Don't do it if it's not wanted
+ if (escOn && myEscape != GetEscEvents())
break;
+
+ CORO_SLEEP(1);
}
+
CORO_END_CODE;
}
/**
* Return when a key pressed or button pushed.
*/
-void waitkey(CORO_PARAM, bool escOn, int myescEvent) {
+static void WaitKey(CORO_PARAM, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int startEvent;
int startX, startY;
@@ -2060,35 +3700,38 @@ void waitkey(CORO_PARAM, bool escOn, int myescEvent) {
CORO_BEGIN_CODE(_ctx);
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
- while (1) {
+ for (;;) {
_ctx->startEvent = getUserEvents();
- // Store cursor position
- while (!GetCursorXYNoWait(&_ctx->startX, &_ctx->startY, false))
- CORO_SLEEP(1);
+ if (!TinselV2) {
+ // Store cursor position
+ while (!GetCursorXYNoWait(&_ctx->startX, &_ctx->startY, false))
+ CORO_SLEEP(1);
+ }
while (_ctx->startEvent == getUserEvents()) {
CORO_SLEEP(1);
// Not necessary to monitor escape as it's an event anyway
+ if (!TinselV2) {
+ int curX, curY;
+ GetCursorXY(&curX, &curY, false); // Store cursor position
+ if (curX != _ctx->startX || curY != _ctx->startY)
+ break;
+ }
- int curX, curY;
- GetCursorXY(&curX, &curY, false); // Store cursor position
- if (curX != _ctx->startX || curY != _ctx->startY)
- break;
-
- if (IsConfWindow())
+ if (MenuActive())
break;
}
- if (!IsConfWindow())
+ if (!MenuActive())
return;
-
+
do {
CORO_SLEEP(1);
- } while (IsConfWindow());
+ } while (MenuActive());
CORO_SLEEP(ONE_SECOND / 2); // Let it die down
}
@@ -2096,16 +3739,38 @@ void waitkey(CORO_PARAM, bool escOn, int myescEvent) {
}
/**
+ * Return when no scrolling is going on.
+ */
+void WaitScroll(CORO_PARAM, int myescEvent) {
+ CORO_BEGIN_CONTEXT;
+ int time;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // wait for ongoing scroll
+ while (IsScrolling()) {
+ if (myescEvent && myescEvent != GetEscEvents())
+ break;
+
+ CORO_SLEEP(1);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
* Pause for requested time.
*/
-void waittime(CORO_PARAM, int time, bool frame, bool escOn, int myescEvent) {
+static void WaitTime(CORO_PARAM, int time, bool frame, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int time;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
+
// Don't do it if it's not wanted
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
return;
if (!frame)
@@ -2116,51 +3781,97 @@ void waittime(CORO_PARAM, int time, bool frame, bool escOn, int myescEvent) {
CORO_SLEEP(1);
// Abort if escapable and ESCAPE is pressed
- if (escOn && myescEvent != GetEscEvents())
+ if (escOn && myEscape != GetEscEvents())
break;
} while (_ctx->time--);
+
CORO_END_CODE;
}
/**
* Set a moving actor off on a walk.
*/
-void walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath, bool escOn, int myescEvent) {
+void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm, int hold, bool igPath,
+ int zOverride, bool escOn, int myescEvent) {
CORO_BEGIN_CONTEXT;
+ int thisWalk;
CORO_END_CONTEXT(_ctx);
- PMACTOR pActor = GetMover(actor);
- assert(pActor); // Can't walk a non-moving actor
+ bool bQuick = hold != 0;
+ PMOVER pMover = GetMover(actor);
+ assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
// Straight there if escaped
if (escOn && myescEvent != GetEscEvents()) {
- stand(actor, x, y, 0);
+ if (TinselV2)
+ StopMover(pMover);
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
return;
}
- assert(pActor->hCpath != NOPOLY); // moving actor not in path
+ if (TinselV2) {
+ if (MoverHidden(pMover))
+ return;
- GetToken(pActor->actorToken);
- SetActorDest(pActor, x, y, igPath, film);
- DontScrollCursor();
+ // Test 10/10/96
+ while (!MoverIs(pMover))
+ CORO_SLEEP(1);
+ }
+
+ assert(pMover->hCpath != NOPOLY); // moving actor not in path
+
+ // Croak if he is doing an SWalk()
+ if (TinselV2) {
+ // Croak if he is doing an SWalk()
+ if (MoverIsSWalking(pMover))
+ CORO_KILL_SELF();
+
+ _ctx->thisWalk = SetActorDest(pMover, x, y, igPath, hFilm);
+ SetMoverZoverride(pMover, zOverride);
+ DontScrollCursor();
+
+ if (!bQuick) {
+ while (MoverMoving(pMover)) {
+ // Straight there if escaped
+ if (escOn && myescEvent != GetEscEvents()) {
+ StopMover(pMover);
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
+ break;
+ }
+
+ CORO_SLEEP(1);
- if (hold == 2) {
- ;
+ // Die if superceded
+ if (_ctx->thisWalk != GetWalkNumber(pMover))
+ CORO_KILL_SELF();
+ }
+ }
} else {
- while (MAmoving(pActor)) {
- CORO_SLEEP(1);
- // Straight there if escaped
- if (escOn && myescEvent != GetEscEvents()) {
- stand(actor, x, y, 0);
- FreeToken(pActor->actorToken);
- return;
+ GetToken(pMover->actorToken);
+ SetActorDest(pMover, x, y, igPath, hFilm);
+ DontScrollCursor();
+
+ if (hold == 2) {
+ ;
+ } else {
+ while (MoverMoving(pMover)) {
+ CORO_SLEEP(1);
+
+ // Straight there if escaped
+ if (escOn && myescEvent != GetEscEvents()) {
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
+ FreeToken(pMover->actorToken);
+ return;
+ }
}
}
+
+ FreeToken(pMover->actorToken);
}
- FreeToken(pActor->actorToken);
+
CORO_END_CODE;
}
@@ -2168,56 +3879,65 @@ void walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool ig
* Set a moving actor off on a walk.
* Wait to see if its aborted or completed.
*/
-void walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myescEvent, bool &retVal) {
+static void Walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myEscape, bool &retVal) {
// COROUTINE
CORO_BEGIN_CONTEXT;
- int ticket;
+ int thisWalk;
CORO_END_CONTEXT(_ctx);
- PMACTOR pActor = GetMover(actor);
- assert(pActor); // Can't walk a non-moving actor
+ PMOVER pMover = GetMover(actor);
+ assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
// Straight there if escaped
- if (escOn && myescEvent != GetEscEvents()) {
- stand(actor, x, y, 0);
+ if (escOn && myEscape != GetEscEvents()) {
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
retVal = true;
return;
}
- CORO_SLEEP(ONE_SECOND);
-
- assert(pActor->hCpath != NOPOLY); // moving actor not in path
-
- // Briefly aquire token to kill off any other normal walk
- GetToken(pActor->actorToken);
- FreeToken(pActor->actorToken);
-
- SetActorDest(pActor, x, y, false, film);
- DontScrollCursor();
-
- _ctx->ticket = GetActorTicket(pActor);
-
- while (MAmoving(pActor)) {
- CORO_SLEEP(1);
+ if (TinselV2) {
+ if (MoverHidden(pMover) || !MoverIs(pMover)) {
+ retVal = false;
+ return;
+ }
+ assert(pMover->hCpath != NOPOLY); // moving actor not in path
- if (_ctx->ticket != GetActorTicket(pActor)) {
+ // Not if he is doing an SWalk()
+ if (MoverIsSWalking(pMover)) {
retVal = false;
return;
}
+ } else {
+ // Pause before starting the walk
+ CORO_SLEEP(ONE_SECOND);
+
+ assert(pMover->hCpath != NOPOLY); // moving actor not in path
+
+ // Briefly aquire token to kill off any other normal walk
+ GetToken(pMover->actorToken);
+ FreeToken(pMover->actorToken);
+ }
+
+ _ctx->thisWalk = SetActorDest(pMover, x, y, false, film);
+ DontScrollCursor();
+
+ while (MoverMoving(pMover) && (_ctx->thisWalk == GetWalkNumber(pMover))) {
// Straight there if escaped
- if (escOn && myescEvent != GetEscEvents()) {
- stand(actor, x, y, 0);
+ if (escOn && myEscape != GetEscEvents()) {
+ CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
retVal = true;
return;
}
+
+ CORO_SLEEP(1);
}
int endx, endy;
- GetMActorPosition(pActor, &endx, &endy);
- retVal = (_ctx->ticket == GetActorTicket(pActor) && endx == x && endy == y);
+ GetMoverPosition(pMover, &endx, &endy);
+ retVal = (_ctx->thisWalk == GetWalkNumber(pMover) && endx == x && endy == y);
CORO_END_CODE;
}
@@ -2225,10 +3945,15 @@ void walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int
/**
* Declare a moving actor.
*/
-void walkingactor(uint32 id, SCNHANDLE *rp) {
- PMACTOR pActor; // Moving actor structure
+static void WalkingActor(uint32 id, SCNHANDLE *rp = NULL) {
+ PMOVER pActor; // Moving actor structure
- SetMover(id); // Establish as a moving actor
+ if (TinselVersion == TINSEL_V2) {
+ RegisterMover(id);
+ return;
+ }
+
+ RegisterMover(id); // Establish as a moving actor
pActor = GetMover(id);
assert(pActor);
@@ -2236,16 +3961,16 @@ void walkingactor(uint32 id, SCNHANDLE *rp) {
int i, j;
for (i = 0; i < 5; ++i) {
for (j = 0; j < 4; ++j)
- pActor->WalkReels[i][j] = *rp++;
+ pActor->walkReels[i][j] = *rp++;
for (j = 0; j < 4; ++j)
- pActor->StandReels[i][j] = *rp++;
+ pActor->standReels[i][j] = *rp++;
}
for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
for (j = 0; j < 4; ++j) {
- pActor->WalkReels[i][j] = pActor->WalkReels[4][j];
- pActor->StandReels[i][j] = pActor->StandReels[2][j];
+ pActor->walkReels[i][j] = pActor->walkReels[4][j];
+ pActor->standReels[i][j] = pActor->standReels[2][j];
}
}
}
@@ -2254,60 +3979,75 @@ void walkingactor(uint32 id, SCNHANDLE *rp) {
* Walk a moving actor towards the polygon's tag, but return when the
* actor enters the polygon.
*/
-void walkpoly(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myescEvent) {
+static void WalkPoly(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
+ int pnodex, pnodey;
+
// COROUTINE
CORO_BEGIN_CONTEXT;
+ int thisWalk;
CORO_END_CONTEXT(_ctx);
- PMACTOR pActor = GetMover(actor);
- assert(pActor); // Can't walk a non-moving actor
+ assert(hp != NOPOLY); // WalkPoly() may only be called from a polygon code block
+ PMOVER pMover = GetMover(actor);
+ assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
- int aniX, aniY; // cursor/actor position
- int pnodex, pnodey;
-
- assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block
-
// Straight there if escaped
- if (escOn && myescEvent != GetEscEvents()) {
- standtag(actor, hp);
+ if (escOn && myEscape != GetEscEvents()) {
+ StandTag(actor, hp);
return;
}
- GetToken(pActor->actorToken);
- getPolyNode(hp, &pnodex, &pnodey);
- SetActorDest(pActor, pnodex, pnodey, false, film);
+ if (TinselV2) {
+ if (MoverHidden(pMover))
+ return;
+
+ // Croak if he is doing an SWalk()
+ if (MoverIsSWalking(pMover))
+ CORO_KILL_SELF();
+
+ } else {
+ GetToken(pMover->actorToken);
+ }
+
+ GetPolyNode(hp, &pnodex, &pnodey);
+ _ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
DoScrollCursor();
- do {
+ while (!MoverIsInPolygon(pMover, hp) && MoverMoving(pMover)) {
CORO_SLEEP(1);
- if (escOn && myescEvent != GetEscEvents()) {
+ if (escOn && myEscape != GetEscEvents()) {
// Straight there if escaped
- standtag(actor, hp);
- FreeToken(pActor->actorToken);
+ StandTag(actor, hp);
+ if (!TinselV2)
+ FreeToken(pMover->actorToken);
return;
}
- GetMActorPosition(pActor, &aniX, &aniY);
- } while (!MActorIsInPolygon(pActor, hp) && MAmoving(pActor));
+ // Die if superceded
+ if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
+ CORO_KILL_SELF();
+ }
- FreeToken(pActor->actorToken);
+ if (!TinselV2)
+ FreeToken(pMover->actorToken);
CORO_END_CODE;
}
/**
- * walktag(actor, reel, hold)
+ * WalkTag(actor, reel, hold)
*/
-void walktag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myescEvent) {
+static void WalkTag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
// COROUTINE
CORO_BEGIN_CONTEXT;
+ int thisWalk;
CORO_END_CONTEXT(_ctx);
- PMACTOR pActor = GetMover(actor);
- assert(pActor); // Can't walk a non-moving actor
+ PMOVER pMover = GetMover(actor);
+ assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
@@ -2316,67 +4056,102 @@ void walktag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int
assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block
// Straight there if escaped
- if (escOn && myescEvent != GetEscEvents()) {
- standtag(actor, hp);
+ if (escOn && myEscape != GetEscEvents()) {
+ StandTag(actor, hp);
return;
}
- GetToken(pActor->actorToken);
- getPolyNode(hp, &pnodex, &pnodey);
- SetActorDest(pActor, pnodex, pnodey, false, film);
- DoScrollCursor();
+ if (!TinselV2)
+ GetToken(pMover->actorToken);
+ else {
+ if (MoverHidden(pMover))
+ return;
+ }
- while (MAmoving(pActor)) {
- CORO_SLEEP(1);
+ GetPolyNode(hp, &pnodex, &pnodey);
+
+ _ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
+ DoScrollCursor();
- if (escOn && myescEvent != GetEscEvents()) {
+ while (MoverMoving(pMover)) {
+ if (escOn && myEscape != GetEscEvents()) {
// Straight there if escaped
- standtag(actor, hp);
- FreeToken(pActor->actorToken);
+ StandTag(actor, hp);
+ if (!TinselV2)
+ FreeToken(pMover->actorToken);
return;
}
+
+ CORO_SLEEP(1);
+
+ // Die if superceded
+ if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
+ CORO_KILL_SELF();
}
// Adopt the tag-related reel
- SCNHANDLE pfilm = getPolyFilm(hp);
+ SCNHANDLE pFilm = GetPolyFilm(hp);
- switch (pfilm) {
+ switch (pFilm) {
case TF_NONE:
break;
case TF_UP:
- SetMActorDirection(pActor, AWAY);
- SetMActorStanding(pActor);
+ SetMoverDirection(pMover, AWAY);
+ SetMoverStanding(pMover);
break;
case TF_DOWN:
- SetMActorDirection(pActor, FORWARD);
- SetMActorStanding(pActor);
+ SetMoverDirection(pMover, FORWARD);
+ SetMoverStanding(pMover);
break;
case TF_LEFT:
- SetMActorDirection(pActor, LEFTREEL);
- SetMActorStanding(pActor);
+ SetMoverDirection(pMover, LEFTREEL);
+ SetMoverStanding(pMover);
break;
case TF_RIGHT:
- SetMActorDirection(pActor, RIGHTREEL);
- SetMActorStanding(pActor);
+ SetMoverDirection(pMover, RIGHTREEL);
+ SetMoverStanding(pMover);
break;
default:
- if (actor == LEAD_ACTOR || actor == LeadId())
- AlterMActor(pActor, pfilm, AR_NORMAL);
+ if (actor == LEAD_ACTOR || actor == GetLeadId())
+ AlterMover(pMover, pFilm, AR_NORMAL);
else
- SetMActorStanding(pActor);
+ SetMoverStanding(pMover);
break;
}
- FreeToken(pActor->actorToken);
+ if (!TinselV2)
+ FreeToken(pMover->actorToken);
+
CORO_END_CODE;
}
/**
+ * Returns the X co-ordinateof lead actor's last walk.
+ */
+int WalkXPos(void) {
+ return GetLastLeadXdest();
+}
+
+/**
+ * Returns the Y co-ordinateof lead actor's last walk.
+ */
+int WalkYPos(void) {
+ return GetLastLeadYdest();
+}
+
+/**
+ * Return which is the current CD, counting from 1.
+ */
+int WhichCd(void) {
+ return GetCurrentCD();
+}
+
+/**
* whichinventory
*/
-int whichinventory(void) {
+int WhichInventory(void) {
return WhichInventoryOpen();
}
@@ -2395,544 +4170,1350 @@ int whichinventory(void) {
* @param pp Top of parameter stack
*/
int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState) {
- debug(7, "CallLibraryRoutine op %d (escOn %d, myescEvent %d)", operand, pic->escOn, pic->myescEvent);
- switch (operand) {
+ int libCode = TinselV2 ? DW2_CODES[operand] : DW1_CODES[operand];
+
+ debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
+ switch (libCode) {
case ACTORATTR:
+ // DW1 only
pp -= 3; // 4 parameters
- actorattr(pp[0], pp[1], pp[2], pp[3]);
+ ActorAttr(pp[0], pp[1], pp[2], pp[3]);
return -4;
+ case ACTORBRIGHTNESS:
+ // DW2 only
+ pp -= 1;
+ ActorBrightness(pp[0], pp[1]);
+ return -2;
+
case ACTORDIRECTION:
- pp[0] = actordirection(pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = ActorDirection(pp[0]);
return 0;
+ case ACTORPALETTE:
+ // DW2 only
+ pp -= 2; // 3 parameters
+ ActorPalette(pp[0], pp[1], pp[2]);
+ return -3;
+
+ case ACTORPRIORITY:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ ActorPriority(pp[0], pp[1]);
+ return -2;
+
case ACTORREF:
+ // Common to both DW1 & DW2
error("actorref isn't a real function!");
+ case ACTORRGB:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ ActorRGB(pp[0], pp[1]);
+ return -2;
+
case ACTORSCALE:
- pp[0] = actorscale(pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = ActorScale(pp[0]);
return 0;
case ACTORSON:
- actorson();
+ // DW1 only
+ ActorsOn();
return 0;
case ACTORXPOS:
- pp[0] = actorpos(ACTORXPOS, pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = ActorPos(ACTORXPOS, pp[0]);
return 0;
case ACTORYPOS:
- pp[0] = actorpos(ACTORYPOS, pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = ActorPos(ACTORYPOS, pp[0]);
return 0;
- case ADDICON:
- addicon(pp[0]);
+ case ADDHIGHLIGHT:
+ // DW2 only
+ // Command doesn't actually do anything
+ pp -= 1; // 2 parameters
+ return -2;
+
+ case ADDINV:
+ // DW2 only
+ AddInv(INV_DEFAULT, pp[0]);
return -1;
case ADDINV1:
- addinv(INV_1, pp[0]);
+ // Common to both DW1 & DW2
+ AddInv(INV_1, pp[0]);
return -1;
case ADDINV2:
- addinv(INV_2, pp[0]);
+ // Common to both DW1 & DW2
+ AddInv(INV_2, pp[0]);
return -1;
case ADDOPENINV:
- addinv(INV_OPEN, pp[0]);
+ // Common to both DW1 & DW2
+ AddInv(TinselV2 ? DW2_INV_OPEN : INV_OPEN, pp[0]);
+ return -1;
+
+ case ADDTOPIC:
+ // Common to both DW1 & DW2
+ AddTopic(pp[0]);
return -1;
case AUXSCALE:
+ // DW1 only
pp -= 13; // 14 parameters
- auxscale(pp[0], pp[1], (SCNHANDLE *)(pp+2));
+ AuxScale(pp[0], pp[1], (SCNHANDLE *)(pp+2));
return -14;
case BACKGROUND:
- background(pp[0]);
+ // Common to both DW1 & DW2
+ Background(coroParam, pp[0]);
+ return -1;
+
+ case BLOCKING:
+ // DW2 only
+ Blocking(pp[0]);
return -1;
+ case CALLACTOR:
+ case CALLGLOBALPROCESS:
+ case CALLOBJECT:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ bool result;
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic, &result);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+ pp[0] = result ? 1 : 0;
+ } else {
+ uint32 v;
+ if (libCode == CALLACTOR)
+ v = SendActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
+ else if (libCode == CALLGLOBALPROCESS)
+ v = SendGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
+ else
+ v = SendObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
+
+ if (coroParam)
+ return 0;
+ pp[0] = v;
+ }
+
+ if (!pp[0])
+ KillSelf(coroParam);
+ return -2;
+
+
+ case CALLPROCESS:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ bool result;
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic, &result);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+
+ pp[0] = result ? 1 : 0;
+ } else {
+ int result = SendProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
+ if (coroParam)
+ return 0;
+
+ pp[0] = result;
+ }
+ return -2;
+
+ case CALLSCENE:
+ // DW2 only
+ error("CallScene isn't a real function!");
+
+ case CALLTAG:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ bool result;
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic, &result);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+
+ pp[0] = result ? 1 : 0;
+ } else {
+ bool result;
+ SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
+ if (coroParam)
+ return 0;
+
+ pp[0] = result ? 1 : 0;
+ }
+
+ if (!pp[0])
+ KillSelf(coroParam);
+ return -2;
+
case CAMERA:
- camera(pp[0]);
+ // Common to both DW1 & DW2
+ Camera(pp[0]);
+ return -1;
+
+ case CDCHANGESCENE:
+ // DW2 only
+ CdChangeScene(pp[0]);
+ return -1;
+
+ case CDDOCHANGE:
+ // DW2 only
+ CdDoChange(coroParam);
+ return 0;
+
+ case CDENDACTOR:
+ // DW2 only
+ CdEndActor(pp[0], pic->myEscape);
return -1;
case CDLOAD:
- pp -= 1; // 2 parameters
- cdload(pp[0], pp[1]);
+ // Common to both DW1 & DW2
+ pp -= 1; // 2 parameters
+ CDload(pp[0], pp[1], pic->myEscape);
return -2;
case CDPLAY:
+ // Common to both DW1 & DW2
error("cdplay isn't a real function!");
case CLEARHOOKSCENE:
- clearhookscene();
+ // Common to both DW1 & DW2
+ ClearHookScene();
return 0;
case CLOSEINVENTORY:
- closeinventory();
+ // Common to both DW1 & DW2
+ CloseInventory();
return 0;
case CONTROL:
- control(pp[0]);
+ // Common to both DW1 & DW2
+ Control(pp[0]);
return -1;
case CONVERSATION:
- conversation(pp[0], pic->hpoly, pic->escOn, pic->myescEvent);
+ // Common to both DW1 & DW2
+ Conversation(coroParam, pp[0], pic->hPoly, pic->idActor, pic->escOn, pic->myEscape);
+ return -1;
+
+ case CONVTOPIC:
+ // Common to both DW1 & DW2
+ ConvTopic(pp[0]);
return -1;
- case CONVICON:
- convicon(pp[0]);
+ case CURSOR:
+ // DW2 only
+ Cursor(pp[0]);
return -1;
case CURSORXPOS:
- pp[0] = cursorpos(CURSORXPOS);
+ // Common to both DW1 & DW2
+ pp[0] = CursorPos(CURSORXPOS);
return 0;
case CURSORYPOS:
- pp[0] = cursorpos(CURSORYPOS);
+ // Common to both DW1 & DW2
+ pp[0] = CursorPos(CURSORYPOS);
return 0;
case CUTSCENE:
+ // DW1 only
error("cutscene isn't a real function!");
- case DEC_CONVW:
+ case DECCONVW:
+ // Common to both DW1 & DW2
pp -= 7; // 8 parameters
- dec_convw(pp[0], pp[1], pp[2], pp[3],
+ DecConvW(pp[0], pp[1], pp[2], pp[3],
pp[4], pp[5], pp[6], pp[7]);
return -8;
- case DEC_CSTRINGS:
+ case DECCSTRINGS:
+ // DW1 only
pp -= 19; // 20 parameters
- dec_cstrings((SCNHANDLE *)pp);
+ DecCStrings((SCNHANDLE *)pp);
return -20;
- case DEC_CURSOR:
- dec_cursor(pp[0]);
+ case DECCURSOR:
+ // Common to both DW1 & DW2
+ DecCursor(pp[0]);
return -1;
- case DEC_FLAGS:
- dec_flags(pp[0]);
+ case DECFLAGS:
+ // Common to both DW1 & DW2
+ if (TinselV2)
+ error("DecFlags() is obsolete!");
+
+ DecFlags(pp[0]);
return -1;
- case DEC_INV1:
+ case DECINV1:
+ // Common to both DW1 & DW2
pp -= 7; // 8 parameters
- dec_inv1(pp[0], pp[1], pp[2], pp[3],
+ DecInv1(pp[0], pp[1], pp[2], pp[3],
pp[4], pp[5], pp[6], pp[7]);
return -8;
- case DEC_INV2:
+ case DECINV2:
+ // Common to both DW1 & DW2
pp -= 7; // 8 parameters
- dec_inv2(pp[0], pp[1], pp[2], pp[3],
+ DecInv2(pp[0], pp[1], pp[2], pp[3],
pp[4], pp[5], pp[6], pp[7]);
return -8;
- case DEC_INVW:
- dec_invw(pp[0]);
+ case DECINVW:
+ // Common to both DW1 & DW2
+ DecInvW(pp[0]);
return -1;
- case DEC_LEAD:
- pp -= 61; // 62 parameters
- dec_lead(pp[0], (SCNHANDLE *)&pp[1], pp[61]);
- return -62;
+ case DECLARELANGUAGE:
+ // DW2 only
+ pp -= 2; // 3 parameters
+ DeclareLanguage(pp[0], pp[1], pp[2]);
+ return -3;
+
+ case DECLEAD:
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ DecLead(pp[0]);
+ return -1;
+ } else {
+ pp -= 61; // 62 parameters
+ DecLead(pp[0], (SCNHANDLE *)&pp[1], pp[61]);
+ return -62;
+ }
+
+ case DECSCALE:
+ // DW2 only
+ pp -= 13; // 14 parameters
+ DecScale(pp[0], pp[1], pp[2], pp[3], pp[4],
+ pp[5], pp[6], pp[7], pp[8], pp[9],
+ pp[10], pp[11], pp[12], pp[13]);
+ return -14;
- case DEC_TAGFONT:
- dec_tagfont(pp[0]);
+ case DECTAGFONT:
+ // Common to both DW1 & DW2
+ DecTagFont(pp[0]);
return -1;
- case DEC_TALKFONT:
- dec_talkfont(pp[0]);
+ case DECTALKFONT:
+ // Common to both DW1 & DW2
+ DecTalkFont(pp[0]);
return -1;
case DELICON:
- delicon(pp[0]);
+ // DW1 only
+ DelIcon(pp[0]);
return -1;
case DELINV:
- delinv(pp[0]);
+ // DW1 only
+ DelInv(pp[0]);
+ return -1;
+
+ case DELTOPIC:
+ // DW2 only
+ DelTopic(pp[0]);
return -1;
+ case DIMMUSIC:
+ // DW2 only
+ DimMusic();
+ return 0;
+
+ case DROP:
+ // DW2 only
+ Drop(pp[0]);
+ return -1;
+
+ case DROPEVERYTHING:
+ // DW2 only
+ DropEverything();
+ return 0;
+
+ case DROPOUT:
+ // DW1 only
+ error("DropOut (%d)\n", pp[0]);
+
case EFFECTACTOR:
- assert(pic->event == ENTER || pic->event == LEAVE); // effectactor() must be from effect poly code
+ // Common to both DW1 & DW2
+ assert(pic->event == WALKIN || pic->event == WALKOUT); // effectactor() must be from effect poly code
- pp[0] = pic->actorid;
+ pp[0] = pic->idActor;
return 0;
- case ENABLEF1:
- enablef1();
+ case ENABLEMENU:
+ // Common to both DW1 & DW2
+ EnableMenu();
return 0;
+ case ENDACTOR:
+ // DW2 only
+ EndActor(pp[0]);
+ return -1;
+
+ case ESCAPE:
+ case ESCAPEOFF:
+ case ESCAPEON:
+ // Common to both DW1 & DW2
+ error("Escape isn't a real function!");
+
case EVENT:
- pp[0] = pic->event;
+ // Common to both DW1 & DW2
+ if (TinselVersion == TINSEL_V2)
+ pp[0] = pic->event;
+ else
+ pp[0] = TINSEL1_EVENT_MAP[pic->event];
+ return 0;
+
+ case FACETAG:
+ // DW2 only
+ FaceTag(pp[0], pic->hPoly);
+ return -1;
+
+ case FADEIN:
+ // DW2 only
+ FadeIn();
return 0;
case FADEMIDI:
- fademidi(coroParam, pp[0]);
+ // DW1 only
+ FadeMidi(coroParam, pp[0]);
return -1;
+ case FADEOUT:
+ // DW1 only
+ FadeOut();
+ return 0;
+
case FRAMEGRAB:
+ // Common to both DW1 & DW2
+ return -1;
+
+ case FREEZECURSOR:
+ // DW2 only
+ FreezeCursor(pp[0]);
return -1;
case GETINVLIMIT:
- pp[0] = getinvlimit(pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = GetInvLimit(pp[0]);
return 0;
+ case GHOST:
+ // DW2 only
+ pp -= 2; // 3 parameters
+ Ghost(pp[0], pp[1], pp[2]);
+ return -3;
+
+ case GLOBALVAR:
+ // DW1 only
+ error("GlobalVar isn't a real function!");
+
+ case GRABMOVIE:
+ // DW2 only
+ return -1;
+
+ case HAILSCENE:
+ // DW2 only
+ HailScene(pp[0]);
+ return -1;
+
case HASRESTARTED:
- pp[0] = hasrestarted();
+ // Common to both DW1 & DW2
+ pp[0] = HasRestarted();
return 0;
+ case HAVE:
+ // DW2 only
+ pp[0] = Have(pp[0]);
+ return 0; // using return value
+
case HELDOBJECT:
- pp[0] = heldobject();
+ // Common to both DW1 & DW2
+ pp[0] = HeldObject();
return 0;
- case HIDE:
- hide(pp[0]);
+ case HIDEACTOR:
+ // Common to both DW1 & DW2
+ if (!TinselV2)
+ HideActorFn(coroParam, pp[0]);
+ else if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+ } else
+ HideActorFn(coroParam, pp[0]);
+ return -1;
+
+ case HIDEBLOCK:
+ // DW2 only
+ HideBlock(pp[0]);
+ return -1;
+
+ case HIDEEFFECT:
+ // DW2 only
+ HideEffect(pp[0]);
+ return -1;
+
+ case HIDEPATH:
+ // DW2 only
+ HidePath(pp[0]);
+ return -1;
+
+ case HIDEREFER:
+ // DW2 only
+ HideRefer(pp[0]);
+ return -1;
+
+ case HIDETAG:
+ // DW2 only
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+ } else {
+ HideTag(coroParam, pp[0], pic->hPoly);
+ if (coroParam)
+ return 0;
+ }
+ return -1;
+
+ case HOLD:
+ // DW2 only
+ Hold(pp[0]);
return -1;
case HOOKSCENE:
+ // Common to both DW1 & DW2
pp -= 2; // 3 parameters
- hookscene(pp[0], pp[1], pp[2]);
+ HookScene(pp[0], pp[1], pp[2]);
return -3;
case IDLETIME:
- pp[0] = idletime();
+ // Common to both DW1 & DW2
+ pp[0] = IdleTime();
return 0;
case ININVENTORY:
- pp[0] = ininventory(pp[0]);
+ // DW1 only
+ pp[0] = InInventory(pp[0]);
return 0; // using return value
+ case INSTANTSCROLL:
+ // DW2 only
+ InstantScroll(pp[0]);
+ return -1;
+
case INVDEPICT:
+ // DW1 only
pp -= 1; // 2 parameters
- invdepict(pp[0], pp[1]);
+ InvDepict(pp[0], pp[1]);
return -2;
case INVENTORY:
- inventory(pp[0], pic->escOn, pic->myescEvent);
+ // Common to both DW1 & DW2
+ Inventory(pp[0], pic->escOn, pic->myEscape);
return -1;
+ case INVPLAY:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ InvPlay(pp[0], pp[1]);
+ return -2;
+
case INWHICHINV:
- pp[0] = inwhichinv(pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = InWhichInv(pp[0]);
return 0; // using return value
case KILLACTOR:
- killactor(pp[0]);
+ // DW1 only
+ if (TinselV2)
+ error("KillActor() was not expected to be required!");
+
+ KillActor(pp[0]);
return -1;
case KILLBLOCK:
- killblock(pp[0]);
+ // DW1 only
+ KillBlock(pp[0]);
return -1;
case KILLEXIT:
- killexit(pp[0]);
+ // DW1 only
+ KillExit(pp[0]);
+ return -1;
+
+ case KILLGLOBALPROCESS:
+ // DW2 only
+ KillGlobalProcess(pp[0]);
+ return -1;
+
+ case KILLPROCESS:
+ // DW2 only
+ KillProcess(pp[0]);
return -1;
case KILLTAG:
- killtag(pp[0]);
+ // DW1 only
+ KillTag(coroParam, pp[0]);
return -1;
- case LEFTOFFSET:
- pp[0] = ltoffset(LEFTOFFSET);
- return 0;
+ case LOCALVAR:
+ // DW2 only
+ error("LocalVar isn't a real function!");
case MOVECURSOR:
+ // Common to both DW1 & DW2
pp -= 1; // 2 parameters
- movecursor(pp[0], pp[1]);
+ MoveCursor(pp[0], pp[1]);
return -2;
+ case MOVETAG:
+ // DW2 only
+ pp -= 2; // 3 parameters
+ MoveTag(pp[0], pp[1], pp[2], pic->hPoly);
+ return -3;
+
+ case MOVETAGTO:
+ // DW2 only
+ pp -= 2; // 3 parameters
+ MoveTagTo(pp[0], pp[1], pp[2], pic->hPoly);
+ return -3;
+
case NEWSCENE:
+ // Common to both DW1 & DW2
pp -= 2; // 3 parameters
if (*pResumeState == RES_2)
*pResumeState = RES_NOT;
else
- newscene(coroParam, pp[0], pp[1], pp[2]);
+ NewScene(coroParam, pp[0], pp[1], pp[2]);
return -3;
case NOBLOCKING:
- noblocking();
+ // Common to both DW1 & DW2
+ NoBlocking();
+ return 0;
+
+ case NOPAUSE:
+ // DW2 only
+ bNoPause = true;
return 0;
case NOSCROLL:
+ // Common to both DW1 & DW2
pp -= 3; // 4 parameters
- noscroll(pp[0], pp[1], pp[2], pp[3]);
+ NoScroll(pp[0], pp[1], pp[2], pp[3]);
return -4;
case OBJECTHELD:
- objectheld(pp[0]);
+ // DW1 only
+ ObjectHeld(pp[0]);
return -1;
case OFFSET:
- pp -= 1; // 2 parameters
- offset(pp[0], pp[1]);
- return -2;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 2; // 2 parameters
+ Offset((EXTREME)pp[0], pp[1], pp[2]);
+ return -3;
+ } else {
+ pp -= 1; // 2 parameters
+ Offset(EX_USEXY, pp[0], pp[1]);
+ return -2;
+ }
+
+ case OTHEROBJECT:
+ // DW2 only
+ pp[0] = OtherObject(pic->pinvo);
+ return 0;
+
+ case PAUSE:
+ // DW2 only
+ WaitTime(coroParam, 1, true, pic->escOn, pic->myEscape);
+ return 0;
case PLAY:
- pp -= 5; // 6 parameters
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 3; // 4 parameters
+ if (*pResumeState == RES_1 && IsCdPlayHandle(pp[0]))
+ *pResumeState = RES_NOT;
+ else {
+ Play(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, false,
+ pic->event, pic->hPoly, pic->idActor);
+
+ if (coroParam)
+ return 0;
+ }
+ return -4;
- if (pic->event == ENTER || pic->event == LEAVE)
- play(coroParam, pp[0], pp[1], pp[2], pp[5], 0, false, 0, pic->escOn, pic->myescEvent, false);
- else
- play(coroParam, pp[0], pp[1], pp[2], pp[5], pic->actorid, false, 0, pic->escOn, pic->myescEvent, false);
- return -6;
+ } else {
+ pp -= 5; // 6 parameters
+
+ if (pic->event == WALKIN || pic->event == WALKOUT)
+ Play(coroParam, pp[0], pp[1], pp[2], pp[5], 0, false, 0, pic->escOn, pic->myEscape, false);
+ else
+ Play(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape, false);
+ return -6;
+ }
case PLAYMIDI:
+ // Common to both DW1 & DW2
pp -= 2; // 3 parameters
- playmidi(coroParam, pp[0], pp[1], pp[2]);
+ PlayMidi(coroParam, pp[0], pp[1], pp[2]);
return -3;
+ case PLAYMOVIE:
+ // DW2 only
+ PlayMovie(coroParam, pp[0], pic->myEscape);
+ return -1;
+
+ case PLAYMUSIC:
+ // DW2 only
+ PlayMusic(pp[0]);
+ return -1;
+
case PLAYRTF:
+ // Common to both DW1 & DW2
error("playrtf only applies to cdi!");
case PLAYSAMPLE:
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 3; // 4 parameters
+ PlaySample(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape);
+ return -4;
+ } else {
+ pp -= 1; // 2 parameters
+ PlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
+ return -2;
+ }
+
+ case POINTACTOR:
+ // DW2 only
+ PointActor(pp[0]);
+ return -1;
+
+ case POINTTAG:
+ // DW2 only
+ PointTag(pp[0], pic->hPoly);
+ return -1;
+
+ case POSTACTOR:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ PostActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->idActor, pic->myEscape);
+ return -2;
+
+ case POSTGLOBALPROCESS:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ PostGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
+ return -2;
+
+ case POSTOBJECT:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ PostObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
+ return -2;
+
+ case POSTPROCESS:
+ // DW2 only
pp -= 1; // 2 parameters
- playsample(coroParam, pp[0], pp[1], pic->escOn, pic->myescEvent);
+ PostProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
return -2;
+ case POSTTAG:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ PostTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
+ return -2;
+
+
case PREPARESCENE:
- preparescene(pp[0]);
+ // DW1 only
+ PrepareScene(pp[0]);
return -1;
case PRINT:
- pp -= 5; // 6 parameters
- /* pp[2] was intended to be attribute */
- print(coroParam, pp[0], pp[1], pp[3], pp[4], pp[5], pic->escOn, pic->myescEvent);
- return -6;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 4; // 5 parameters
+ Print(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4] != 0, pic->escOn, pic->myEscape);
+ return -5;
+ } else {
+ pp -= 5; // 6 parameters
+ /* pp[2] was intended to be attribute */
+ Print(coroParam, pp[0], pp[1], pp[3], pp[4], pp[5] == 2, pic->escOn, pic->myEscape);
+ return -6;
+ }
+
+ case PRINTCURSOR:
+ // DW2 only
+ PrintTag(pic->hPoly, pp[0], pic->idActor, true);
+ return -1;
case PRINTOBJ:
- printobj(coroParam, pp[0], pic->pinvo, pic->event);
+ // Common to both DW1 & DW2
+ PrintObj(coroParam, pp[0], pic->pinvo, pic->event, pic->myEscape);
return -1;
case PRINTTAG:
- printtag(pic->hpoly, pp[0]);
+ // Common to both DW1 & DW2
+ PrintTag(pic->hPoly, pp[0], TinselV2 ? pic->idActor : 0, false);
return -1;
case QUITGAME:
- quitgame();
+ // Common to both DW1 & DW2
+ QuitGame();
return 0;
case RANDOM:
+ // Common to both DW1 & DW2
pp -= 2; // 3 parameters
- pp[0] = dw_random(pp[0], pp[1], pp[2]);
+ pp[0] = RandomFn(pp[0], pp[1], pp[2]);
return -2; // One holds return value
case RESETIDLETIME:
- resetidletime();
+ // Common to both DW1 & DW2
+ ResetIdleTime();
return 0;
case RESTARTGAME:
- restartgame();
+ // Common to both DW1 & DW2
+ RestartGame();
return 0;
+ case RESTORESCENE:
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ RestoreScene(coroParam, (TRANSITS)pp[0]);
+ return -1;
+ } else {
+ RestoreScene(coroParam, TRANS_FADE);
+ return 0;
+ }
+
case RESTORE_CUT:
- restore_scene(false);
+ // DW1 only
+ RestoreScene(coroParam, TRANS_CUT);
return 0;
- case RESTORE_SCENE:
- restore_scene(true);
+ case RESUMELASTGAME:
+ // DW2 only
+ ResumeLastGame();
return 0;
case RUNMODE:
- pp[0] = runmode();
+ // Common to both DW1 & DW2
+ pp[0] = RunMode();
return 0;
case SAMPLEPLAYING:
- pp[0] = sampleplaying(pic->escOn, pic->myescEvent);
+ // DW1 only
+ pp[0] = SamplePlaying(pic->escOn, pic->myEscape);
return 0;
- case SAVE_SCENE:
+ case SAVESCENE:
+ // Common to both DW1 & DW2
if (*pResumeState == RES_1)
*pResumeState = RES_2;
else
- save_scene(coroParam);
+ SaveScene(coroParam);
return 0;
+ case SAY:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ TalkOrSay(coroParam, IS_SAY, pp[1], 0, 0, 0, pp[0], false, pic->escOn, pic->myEscape);
+ return -2;
+
+ case SAYAT:
+ // DW2 only
+ pp -= 4; // 5 parameters
+ TalkOrSay(coroParam, IS_SAYAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
+ return -5;
+
case SCALINGREELS:
+ // Common to both DW1 & DW2
pp -= 6; // 7 parameters
- scalingreels(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
+ ScalingReels(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
return -7;
case SCANICON:
- pp[0] = scanicon();
+ // DW1 only
+ pp[0] = ScanIcon();
+ return 0;
+
+ case SCREENXPOS:
+ // Common to both DW1 & DW2
+ pp[0] = LToffset(SCREENXPOS);
+ return 0;
+
+ case SCREENYPOS:
+ // Common to both DW1 & DW2
+ pp[0] = LToffset(SCREENYPOS);
return 0;
case SCROLL:
- pp -= 3; // 4 parameters
- scroll(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myescEvent);
- return -4;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 5; // 6 parameters
+ Scroll(coroParam, (EXTREME)pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pic->escOn, pic->myEscape);
+ return -6;
+ } else {
+ pp -= 3; // 4 parameters
+ Scroll(coroParam, EX_USEXY, pp[0], pp[1], pp[2], pp[2], pp[3], pic->escOn, pic->myEscape);
+ return -4;
+ }
+
+ case SCROLLPARAMETERS:
+ // DW2 only
+ pp -= 6; // 7 parameters
+ ScrollParameters(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
+ return -7;
+
+ case SENDTAG:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ bool result;
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic, &result);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+ pp[0] = result ? 1 : 0;
+ } else {
+ bool result;
+ SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
+ if (coroParam)
+ return 0;
+
+ pp[0] = result;
+ }
+ return -1;
+
case SETACTOR:
- setactor(pp[0]);
+ // DW1 only
+ SetActor(pp[0]);
return -1;
case SETBLOCK:
- setblock(pp[0]);
+ // DW1 only
+ SetBlock(pp[0]);
return -1;
case SETEXIT:
- setexit(pp[0]);
+ // DW1 only
+ SetExit(pp[0]);
return -1;
case SETINVLIMIT:
+ // Common to both DW1 & DW2
pp -= 1; // 2 parameters
- setinvlimit(pp[0], pp[1]);
+ SetInvLimit(pp[0], pp[1]);
return -2;
case SETINVSIZE:
+ // Common to both DW1 & DW2
pp -= 6; // 7 parameters
- setinvsize(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
+ SetInvSize(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
return -7;
case SETLANGUAGE:
- setlanguage((LANGUAGE)pp[0]);
+ // Common to both DW1 & DW2
+ SetLanguage((LANGUAGE)pp[0]);
return -1;
case SETPALETTE:
- setpalette(pp[0], pic->escOn, pic->myescEvent);
- return -1;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ // Note: Although DW2 introduces parameters for start and length, it doesn't use them
+ pp -= 2;
+ SetPalette(pp[0], pic->escOn, pic->myEscape);
+ return -3;
+ } else {
+ SetPalette(pp[0], pic->escOn, pic->myEscape);
+ return -1;
+ }
+
+ case SETSYSTEMSTRING:
+ // DW2 only
+ pp -= 1; // 2 parameters
+ SetSystemString(pp[0], pp[1]);
+ return -2;
+
+ case SETSYSTEMVAR:
+ // DW1 only
+ pp -= 1; // 2 parameters
+ SetSystemVar(pp[0], pp[1]);
+ return -2;
case SETTAG:
- settag(pp[0]);
+ // DW1 only
+ SetTag(coroParam, pp[0]);
return -1;
case SETTIMER:
+ // DW1 only
pp -= 3; // 4 parameters
- settimer(pp[0], pp[1], pp[2], pp[3]);
+ SetTimer(pp[0], pp[1], pp[2], pp[3]);
return -4;
+ case SHELL:
+ // DW2 only
+ Shell(pp[0]);
+ return 0;
+
+ case SHOWACTOR:
+ // DW2 only
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic);
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+ } else
+ ShowActorFn(coroParam, pp[0]);
+ return -1;
+
+ case SHOWBLOCK:
+ // DW2 only
+ ShowBlock(pp[0]);
+ return -1;
+
+ case SHOWEFFECT:
+ // DW2 only
+ ShowEffect(pp[0]);
+ return -1;
+
+ case SHOWMENU:
+ // DW2 only
+ ShowMenu();
+ return 0;
+
+ case SHOWPATH:
+ // DW2 only
+ ShowPath(pp[0]);
+ return -1;
+
case SHOWPOS:
+ // DW1 only
#ifdef DEBUG
showpos();
#endif
return 0;
+ case SHOWREFER:
+ // DW2 only
+ ShowRefer(pp[0]);
+ return -1;
+
case SHOWSTRING:
#ifdef DEBUG
showstring();
#endif
return 0;
+ case SHOWTAG:
+ // DW2 only
+ if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
+ *pResumeState = RES_NOT;
+ FinishWaiting(coroParam, pic);
+
+ if (coroParam) {
+ *pResumeState = RES_1;
+ return 0;
+ }
+ } else {
+ ShowTag(coroParam, pp[0], pic->hPoly);
+ }
+ return -1;
+
case SPLAY:
+ // DW1 only
pp -= 6; // 7 parameters
- if (pic->event == ENTER || pic->event == LEAVE)
- splay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], 0, pic->escOn, pic->myescEvent);
+ if (pic->event == WALKIN || pic->event == WALKOUT)
+ SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], 0, pic->escOn, pic->myEscape);
else
- splay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], pic->actorid, pic->escOn, pic->myescEvent);
+ SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], pic->idActor, pic->escOn, pic->myEscape);
return -7;
case STAND:
+ // Common to both DW1 & DW2
pp -= 3; // 4 parameters
- stand(pp[0], pp[1], pp[2], pp[3]);
+ Stand(coroParam, pp[0], pp[1], pp[2], pp[3]);
return -4;
case STANDTAG:
- standtag(pp[0], pic->hpoly);
+ // Common to both DW1 & DW2
+ StandTag(pp[0], pic->hPoly);
return -1;
- case STOP:
- stop(pp[0]);
+ case STARTGLOBALPROCESS:
+ // DW2 only
+ StartGlobalProcess(coroParam, pp[0]);
return -1;
+ case STARTPROCESS:
+ // DW2 only
+ StartProcess(coroParam, pp[0]);
+ return -1;
+
+ case STARTTIMER:
+ // DW2 only
+ pp -= 3; // 4 parameters
+ StartTimerFn(pp[0], pp[1], pp[2], pp[3]);
+ return -4;
+
case STOPMIDI:
- stopmidi();
+ // DW1 only
+ StopMidiFn();
return 0;
case STOPSAMPLE:
- stopsample();
- return 0;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ StopSample(pp[0]);
+ return -1;
+ } else {
+ StopSample();
+ return 0;
+ }
+
+ case STOPWALK:
+ // Common to both DW1 & DW2 only
+ StopWalk(pp[0]);
+ return -1;
case SUBTITLES:
- subtitles(pp[0]);
+ // Common to both DW1 & DW2
+ Subtitles(pp[0]);
return -1;
case SWALK:
+ // Common to both DW1 & DW2
pp -= 5; // 6 parameters
- swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pic->escOn, pic->myescEvent);
+ Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], -1, pic->escOn, pic->myEscape);
return -6;
+ case SWALKZ:
+ // DW2 only
+ pp -= 6; // 7 parameters
+ Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pic->escOn, pic->myEscape);
+ return -7;
+
+ case SYSTEMVAR:
+ // DW2 only
+ pp[0] = SystemVar(pp[0]);
+ return 0;
+
case TAGACTOR:
pp -= 2; // 3 parameters
- tagactor(pp[0], pp[1], pp[2]);
+ TagActor(pp[0], pp[1], pp[2]);
return -3;
+ case TAGTAGXPOS:
+ case TAGTAGYPOS:
+ case TAGWALKXPOS:
+ case TAGWALKYPOS:
+ // DW2 only
+ pp[0] = TagPos((MASTER_LIB_CODES)libCode, pp[0], pic->hPoly);
+ return 0;
+
case TALK:
+ // Common to both DW1 & DW2
pp -= 1; // 2 parameters
- if (pic->event == ENTER || pic->event == LEAVE)
- talk(coroParam, pp[0], pp[1], 0, pic->escOn, pic->myescEvent);
+ if (TinselV2)
+ TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
+ else if (pic->event == WALKIN || pic->event == WALKOUT)
+ TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
else
- talk(coroParam, pp[0], pp[1], pic->actorid, pic->escOn, pic->myescEvent);
+ TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], pic->idActor, false, pic->escOn, pic->myEscape);
return -2;
case TALKAT:
- pp -= 3; // 4 parameters
- talkat(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myescEvent);
- return -4;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 4; // 5 parameters
+ TalkOrSay(coroParam, IS_TALKAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
+ return -5;
+ } else {
+ pp -= 3; // 4 parameters
+ TalkAt(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape);
+ return -4;
+ }
case TALKATS:
+ // DW1 only
pp -= 4; // 5 parameters
- talkats(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pic->escOn, pic->myescEvent);
+ TalkAtS(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pic->escOn, pic->myEscape);
return -5;
case TALKATTR:
+ // DW1 only
pp -= 2; // 3 parameters
- talkattr(pp[0], pp[1], pp[2], pic->escOn, pic->myescEvent);
+ TalkAttr(pp[0], pp[1], pp[2], pic->escOn, pic->myEscape);
+ return -3;
+
+ case TALKPALETTEINDEX:
+ // DW1 only
+ TalkPaletteIndex(pp[0]);
+ return -1;
+
+ case TALKRGB:
+ // DW2 only
+ TalkRGB(pp[0], pic->myEscape);
return -3;
+ case TALKVIA:
+ // DW2 only
+ TalkVia(pp[0]);
+ return -1;
+
+ case TEMPTAGFONT:
+ // DW2 only
+ TempTagFont(pp[0]);
+ return -1;
+
+ case TEMPTALKFONT:
+ // DW2 only
+ TempTalkFont(pp[0]);
+ return -1;
+
+ case THISOBJECT:
+ // DW2 only
+ pp[0] = ThisObject(pic->pinvo);
+ return 0;
+
+ case THISTAG:
+ // DW2 only
+ pp[0] = ThisTag(pic->hPoly);
+ return 0;
+
case TIMER:
- pp[0] = timer(pp[0]);
+ // Common to both DW1 & DW2
+ pp[0] = TimerFn(pp[0]);
return 0;
- case TOPOFFSET:
- pp[0] = ltoffset(TOPOFFSET);
+ case TOPIC:
+ // DW2 only
+ pp[0] = Topic();
return 0;
case TOPPLAY:
- pp -= 5; // 6 parameters
- topplay(coroParam, pp[0], pp[1], pp[2], pp[5], pic->actorid, false, 0, pic->escOn, pic->myescEvent);
- return -6;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 3; // 4 parameters
+ TopPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, pic->event);
+ return -4;
+ } else {
+ pp -= 5; // 6 parameters
+ TopPlay(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape);
+ return -6;
+ }
case TOPWINDOW:
- topwindow(pp[0]);
+ // Common to both DW1 & DW2
+ TopWindow(pp[0]);
+ return -1;
+
+ case TRANSLUCENTINDEX:
+ // DW2 only
+ TranslucentIndex(pp[0]);
return -1;
case TRYPLAYSAMPLE:
+ // DW1 only
pp -= 1; // 2 parameters
- tryplaysample(coroParam, pp[0], pp[1], pic->escOn, pic->myescEvent);
+ TryPlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
return -2;
+ case UNDIMMUSIC:
+ // DW2 only
+ UnDimMusic();
+ return 0;
+
case UNHOOKSCENE:
- unhookscene();
+ // Common to both DW1 & DW2
+ UnHookSceneFn();
return 0;
case UNTAGACTOR:
- untagactor(pp[0]);
+ // DW1 only
+ UnTagActorFn(pp[0]);
return -1;
case VIBRATE:
- vibrate();
- return 0;
-
- case WAITKEY:
- waitkey(coroParam, pic->escOn, pic->myescEvent);
+ // DW1 only
+ Vibrate();
return 0;
case WAITFRAME:
+ // Common to both DW1 & DW2
pp -= 1; // 2 parameters
- waitframe(coroParam, pp[0], pp[1], pic->escOn, pic->myescEvent);
+ WaitFrame(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
return -2;
+ case WAITKEY:
+ // Common to both DW1 & DW2
+ WaitKey(coroParam, pic->escOn, pic->myEscape);
+ return 0;
+
+ case WAITSCROLL:
+ // DW2 only
+ WaitScroll(coroParam, pic->myEscape);
+ return 0;
+
case WAITTIME:
+ // Common to both DW1 & DW2
pp -= 1; // 2 parameters
- waittime(coroParam, pp[0], pp[1], pic->escOn, pic->myescEvent);
+ WaitTime(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
return -2;
case WALK:
+ // Common to both DW1 & DW2
pp -= 4; // 5 parameters
- walk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], false, pic->escOn, pic->myescEvent);
+ Walk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], false, -1, pic->escOn, pic->myEscape);
return -5;
case WALKED: {
+ // Common to both DW1 & DW2
pp -= 3; // 4 parameters
bool tmp;
- walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myescEvent, tmp);
+ Walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape, tmp);
if (!coroParam) {
// Only write the result to the stack if walked actually completed running.
pp[0] = tmp;
@@ -2941,24 +5522,65 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
return -3;
case WALKINGACTOR:
- pp -= 40; // 41 parameters
- walkingactor(pp[0], (SCNHANDLE *)&pp[1]);
- return -41;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ // DW2 doesn't use a second parameter to WalkingActor
+ WalkingActor(pp[0]);
+ return -1;
+ } else {
+ pp -= 40; // 41 parameters
+ WalkingActor(pp[0], (SCNHANDLE *)&pp[1]);
+ return -41;
+ }
case WALKPOLY:
- pp -= 2; // 3 parameters
- walkpoly(coroParam, pp[0], pp[1], pic->hpoly, pic->escOn, pic->myescEvent);
- return -3;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 1; // 2 parameters
+ WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
+ return -2;
+ } else {
+ pp -= 2; // 3 parameters
+ WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
+ return -3;
+ }
case WALKTAG:
- pp -= 2; // 3 parameters
- walktag(coroParam, pp[0], pp[1], pic->hpoly, pic->escOn, pic->myescEvent);
- return -3;
+ // Common to both DW1 & DW2
+ if (TinselV2) {
+ pp -= 1; // 2 parameters
+ WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
+ return -2;
+ } else {
+ pp -= 2; // 3 parameters
+ WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
+ return -3;
+ }
+
+ case WALKXPOS:
+ // DW2 only
+ pp[0] = WalkXPos();
+ return 0;
+
+ case WALKYPOS:
+ // DW2 only
+ pp[0] = WalkYPos();
+ return 0;
+
+ case WHICHCD:
+ // DW2 only
+ pp[0] = WhichCd();
+ return 0;
case WHICHINVENTORY:
- pp[0] = whichinventory();
+ // Common to both DW1 & DW2
+ pp[0] = WhichInventory();
return 0;
+ case ZZZZZZ:
+ // DW2 only - dummy routine used during debugging
+ return -1;
+
default:
error("Unsupported library function");
}
@@ -2966,5 +5588,4 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
error("Can't possibly get here");
}
-
} // end of namespace Tinsel
diff --git a/engines/tinsel/tinlib.h b/engines/tinsel/tinlib.h
index 001de70896..06562c541d 100644
--- a/engines/tinsel/tinlib.h
+++ b/engines/tinsel/tinlib.h
@@ -28,13 +28,40 @@
#define TINSEL_TINLIB_H
#include "tinsel/dw.h"
+#include "tinsel/object.h"
+#include "tinsel/palette.h"
namespace Tinsel {
+enum EXTREME {
+ EX_USEXY, EX_BOTTOM, EX_BOTTOMLEFT,
+ EX_BOTTOMRIGHT, EX_LEFT, EX_RIGHT,
+ EX_TOP, EX_TOPLEFT, EX_TOPRIGHT
+};
+
+enum WHICH_VER {VER_GLITTER, VER_COMPILE};
+#define VER_LEN 10
+
+// Support functions
+void TinGetVersion(WHICH_VER which, char *buffer, int length);
+
// Library functions in TINLIB.C
-void control(int param);
-void stand(int actor, int x, int y, SCNHANDLE film);
+void ActorBrightness(int actor, int brightness);
+void ActorPalette(int actor, int startColour, int length);
+void Control(int param);
+void HookScene(SCNHANDLE scene, int entrance, int transition);
+void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition);
+void Offset(EXTREME extreme, int x, int y);
+void RestoreScene(void);
+void ResumeLastGame(void);
+void SaveScene(CORO_PARAM);
+void Stand(CORO_PARAM, int actor, int x, int y, SCNHANDLE film);
+void SetTextPal(COLORREF col);
+
+void KeepOnScreen(OBJECT *pText, int *pTextX, int *pTextY);
+
+enum SPEECH_TYPE { IS_SAY, IS_SAYAT, IS_TALK, IS_TALKAT };
} // end of namespace Tinsel
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index fdf38d9409..85e6c2390f 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/error.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/file.h"
@@ -44,13 +45,15 @@
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
+#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/faders.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/heapmem.h" // MemoryInit
-#include "tinsel/inventory.h"
+#include "tinsel/dialogs.h"
+#include "tinsel/mareels.h"
#include "tinsel/music.h"
#include "tinsel/object.h"
#include "tinsel/pid.h"
@@ -60,6 +63,7 @@
#include "tinsel/serializer.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
+#include "tinsel/sysvar.h"
#include "tinsel/timers.h"
#include "tinsel/tinsel.h"
@@ -71,6 +75,14 @@ namespace Tinsel {
extern void SetDoFadeIn(bool tf);
extern void DropBackground(void);
+// In BMV.CPP
+extern void FettleBMV(void);
+extern bool MoviePlaying(void);
+extern void CopyMovieToScreen(void);
+extern void FinishBMV();
+extern int32 MovieAudioLag();
+extern uint32 NextMovieTime();
+
// In CURSOR.CPP
extern void CursorProcess(CORO_PARAM, const void *);
@@ -79,7 +91,6 @@ extern void InventoryProcess(CORO_PARAM, const void *);
// In SCENE.CPP
extern void PrimeBackground();
-extern void NewScene(SCNHANDLE scene, int entry);
extern SCNHANDLE GetSceneHandle(void);
// In TIMER.CPP
@@ -94,6 +105,12 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition);
bool bRestart = false;
bool bHasRestarted = false;
+static bool bCuttingScene = false;
+
+static bool bChangingForRestore = false;
+
+static Common::Point clickPos;
+
#ifdef DEBUG
bool bFast; // set to make it go ludicrously fast
#endif
@@ -110,11 +127,11 @@ static Scene NextScene = { 0, 0, 0 };
static Scene HookScene = { 0, 0, 0 };
static Scene DelayedScene = { 0, 0, 0 };
-static bool bHookSuspend = false;
-
static PROCESS *pMouseProcess = 0;
static PROCESS *pKeyboardProcess = 0;
+static SCNHANDLE hCdChangeScene;
+
// Stack of pending mouse button events
Common::List<Common::EventType> mouseButtons;
@@ -142,6 +159,7 @@ void KeyboardProcess(CORO_PARAM, const void *) {
// 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) {
@@ -150,21 +168,21 @@ void KeyboardProcess(CORO_PARAM, const void *) {
case Common::KEYCODE_RALT:
if (evt.type == Common::EVENT_KEYDOWN) {
if (!bSwapButtons)
- ProcessButEvent(BE_RDSTART);
+ ProcessButEvent(PLR_DRAG2_START);
else
- ProcessButEvent(BE_LDSTART);
+ ProcessButEvent(PLR_DRAG1_START);
} else {
if (!bSwapButtons)
- ProcessButEvent(BE_LDEND);
+ ProcessButEvent(PLR_DRAG1_END);
else
- ProcessButEvent(BE_RDEND);
+ ProcessButEvent(PLR_DRAG2_END);
}
continue;
case Common::KEYCODE_LCTRL:
case Common::KEYCODE_RCTRL:
if (evt.type == Common::EVENT_KEYDOWN) {
- ProcessKeyEvent(LOOK_KEY);
+ ProcessKeyEvent(PLR_LOOK);
} else {
// Control key release
}
@@ -186,42 +204,42 @@ void KeyboardProcess(CORO_PARAM, const void *) {
switch (evt.kbd.keycode) {
/*** SPACE = WALKTO ***/
case Common::KEYCODE_SPACE:
- ProcessKeyEvent(WALKTO_KEY);
+ ProcessKeyEvent(PLR_WALKTO);
continue;
/*** RETURN = ACTION ***/
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
- ProcessKeyEvent(ACTION_KEY);
+ ProcessKeyEvent(PLR_ACTION);
continue;
/*** l = LOOK ***/
case Common::KEYCODE_l: // LOOK
- ProcessKeyEvent(LOOK_KEY);
+ ProcessKeyEvent(PLR_LOOK);
continue;
case Common::KEYCODE_ESCAPE:
- // WORKAROUND: Check if any of the starting logo screens are active, and if so
- // manually skip to the title screen, allowing them to be bypassed
- {
+ if (!TinselV2) {
+ // WORKAROUND: For Discworld 1, check if any of the starting logo screens are
+ // active, and if so manually skip to the title screen, allowing them to be bypassed
int sceneOffset = (_vm->getFeatures() & GF_SCNFILES) ? 1 : 0;
int sceneNumber = (GetSceneHandle() >> SCNHANDLE_SHIFT) - sceneOffset;
-#if 0 // FIXME: Disabled this code for now, as it doesn't work as it should (see bug #2078922).
if ((g_language == TXT_GERMAN) &&
((sceneNumber >= 25 && sceneNumber <= 27) || (sceneNumber == 17))) {
// Skip to title screen
// It seems the German CD version uses scenes 25,26,27,17 for the intro,
// instead of 13,14,15,11; also, the title screen is 11 instead of 10
SetNewScene((11 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT);
- } else
-#endif
- if ((sceneNumber >= 13) && (sceneNumber <= 15) || (sceneNumber == 11)) {
+ } else if ((sceneNumber >= 13) && (sceneNumber <= 15) || (sceneNumber == 11)) {
// Skip to title screen
SetNewScene((10 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT);
} else {
// Not on an intro screen, so process the key normally
- ProcessKeyEvent(ESC_KEY);
+ ProcessKeyEvent(PLR_ESCAPE);
}
+ } else {
+ // Running Discworld 2, so process the key normally
+ ProcessKeyEvent(PLR_ESCAPE);
}
continue;
@@ -236,38 +254,43 @@ void KeyboardProcess(CORO_PARAM, const void *) {
case Common::KEYCODE_F1:
// Options dialog
- ProcessKeyEvent(OPTION_KEY);
+ ProcessKeyEvent(PLR_MENU);
continue;
case Common::KEYCODE_F5:
// Save game
- ProcessKeyEvent(SAVE_KEY);
+ ProcessKeyEvent(PLR_SAVE);
continue;
case Common::KEYCODE_F7:
// Load game
- ProcessKeyEvent(LOAD_KEY);
+ 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(QUIT_KEY);
+ ProcessKeyEvent(PLR_QUIT);
continue;
case Common::KEYCODE_PAGEUP:
case Common::KEYCODE_KP9:
- ProcessKeyEvent(PGUP_KEY);
+ ProcessKeyEvent(PLR_PGUP);
continue;
case Common::KEYCODE_PAGEDOWN:
case Common::KEYCODE_KP3:
- ProcessKeyEvent(PGDN_KEY);
+ ProcessKeyEvent(PLR_PGDN);
continue;
case Common::KEYCODE_HOME:
case Common::KEYCODE_KP7:
- ProcessKeyEvent(HOME_KEY);
+ ProcessKeyEvent(PLR_HOME);
continue;
case Common::KEYCODE_END:
case Common::KEYCODE_KP1:
- ProcessKeyEvent(END_KEY);
+ ProcessKeyEvent(PLR_END);
continue;
default:
- ProcessKeyEvent(NOEVENT_KEY);
+ ProcessKeyEvent(PLR_NOEVENT);
break;
}
}
@@ -275,9 +298,35 @@ void KeyboardProcess(CORO_PARAM, const void *) {
}
/**
+ * 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;
+}
+
+/**
* Process to handle changes in the mouse buttons.
*/
-void MouseProcess(CORO_PARAM, const void *) {
+static void MouseProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
bool lastLWasDouble;
@@ -292,9 +341,6 @@ void MouseProcess(CORO_PARAM, const void *) {
_ctx->lastLeftClick = _ctx->lastRightClick = DwGetCurrentTime();
while (true) {
- // FIXME: I'm still keeping the ctrl/Alt handling in the ProcessKeyEvent method.
- // Need to make sure that this works correctly
- //DragKeys();
if (mouseButtons.empty()) {
// allow scheduling
@@ -306,23 +352,46 @@ void MouseProcess(CORO_PARAM, const void *) {
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) {
- // signal left drag start
- ProcessButEvent(BE_LDSTART);
+ // 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(BE_DLEFT);
+ // signal left double click event
+ ProcessButEvent(PLR_DLEFT);
+ }
_ctx->lastLWasDouble = true;
} else {
- // signal left drag start
- ProcessButEvent(BE_LDSTART);
+ // 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(BE_SLEFT);
+ // signal left single click event
+ ProcessButEvent(PLR_SLEFT);
+ }
_ctx->lastLWasDouble = false;
}
@@ -332,32 +401,53 @@ void MouseProcess(CORO_PARAM, const void *) {
// left button release
// update click timer
- if (_ctx->lastLWasDouble == false)
+ if (_ctx->lastLWasDouble == false) {
_ctx->lastLeftClick = DwGetCurrentTime();
- else
+
+ // 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;
- // signal left drag end
- ProcessButEvent(BE_LDEND);
+ 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) {
- // signal right drag start
- ProcessButEvent(BE_RDSTART);
+ // 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(BE_DRIGHT);
+ // signal right double click event
+ ProcessButEvent(PLR_DRIGHT);
+ }
_ctx->lastRWasDouble = true;
} else {
- // signal right drag start
- ProcessButEvent(BE_RDSTART);
+ 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(BE_SRIGHT);
+ // signal right single click event
+ ProcessButEvent(PLR_SRIGHT);
+ }
_ctx->lastRWasDouble = false;
}
@@ -372,8 +462,12 @@ void MouseProcess(CORO_PARAM, const void *) {
else
_ctx->lastRightClick -= dclickSpeed;
- // signal right drag end
- ProcessButEvent(BE_RDEND);
+ if (TinselV2)
+ // Signal left drag end
+ PlayerEvent(PLR_DRAG2_END, mousePos);
+ else
+ // signal right drag end
+ ProcessButEvent(PLR_DRAG2_END);
break;
default:
@@ -402,9 +496,24 @@ static void MasterScriptProcess(CORO_PARAM, const void *) {
/**
* Store the facts pertaining to a scene change.
*/
-
void SetNewScene(SCNHANDLE scene, int entrance, int transition) {
- if (HookScene.scene == 0 || bHookSuspend) {
+ if (!bCuttingScene && TinselV2)
+ WrapScene();
+
+ // If CD change will be required, stick in the scene change scene
+ if (CdNumber(scene) != GetCurrentCD()) {
+ // This scene gets delayed
+ DelayedScene.scene = scene;
+ DelayedScene.entry = entrance;
+ DelayedScene.trans = transition;
+
+ NextScene.scene = hCdChangeScene;
+ NextScene.entry = CdNumber(scene) - '0';
+ NextScene.trans = TRANS_FADE;
+ return;
+ }
+
+ if (HookScene.scene == 0 || bCuttingScene) {
// This scene comes next
NextScene.scene = scene;
NextScene.entry = entrance;
@@ -424,6 +533,9 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition) {
}
}
+/**
+ * Store a scene as hooked
+ */
void SetHookScene(SCNHANDLE scene, int entrance, int transition) {
assert(HookScene.scene == 0); // scene already hooked
@@ -432,6 +544,9 @@ void SetHookScene(SCNHANDLE scene, int entrance, int transition) {
HookScene.trans = transition;
}
+/**
+ * Hooked scene is over, trigger a change to the delayed scene
+ */
void UnHookScene(void) {
assert(DelayedScene.scene != 0); // no scene delayed
@@ -444,11 +559,40 @@ void UnHookScene(void) {
}
void SuspendHook(void) {
- bHookSuspend = true;
+ bCuttingScene = true;
+}
+
+void CdHasChanged(void) {
+ if (bChangingForRestore) {
+ bChangingForRestore = false;
+ RestoreGame(-2);
+ } else {
+ assert(DelayedScene.scene != 0);
+
+ WrapScene();
+
+ // The delayed scene can go now
+ NextScene.scene = DelayedScene.scene;
+ NextScene.entry = DelayedScene.entry;
+ NextScene.trans = DelayedScene.trans;
+
+ DelayedScene.scene = 0;
+ }
+}
+
+void SetCdChangeScene(SCNHANDLE hScene) {
+ hCdChangeScene = hScene;
+}
+
+void CDChangeForRestore(int cdNumber) {
+ NextScene.scene = hCdChangeScene;
+ NextScene.entry = cdNumber;
+ NextScene.trans = TRANS_FADE;
+ bChangingForRestore = true;
}
void UnSuspendHook(void) {
- bHookSuspend = false;
+ bCuttingScene = false;
}
void syncSCdata(Serializer &s) {
@@ -468,16 +612,23 @@ static void RestoredProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
+ bool bConverse;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// get the stuff copied to process when it was created
- _ctx->pic = *((INT_CONTEXT **)param);
+ _ctx->pic = *((INT_CONTEXT * const *)param);
_ctx->pic = RestoreInterpretContext(_ctx->pic);
+ _ctx->bConverse = TinselV2 && (_ctx->pic->event == CONVERSE);
+
CORO_INVOKE_1(Interpret, _ctx->pic);
+ // Restore control after CallScene() from a conversation icon
+ if (_ctx->bConverse)
+ ControlOn();
+
CORO_END_CODE;
}
@@ -500,10 +651,17 @@ static int CountOut = 1; // == 1 for immediate start of first scene
* When the count expires, the screen will have faded. Ensure the scene |
* is loaded, clear the screen, and start the new scene.
*/
-void ChangeScene() {
+bool ChangeScene(bool bReset) {
+
+ // Prevent attempt to fade-out when restarting game
+ if (bReset) {
+ CountOut = 1; // immediate start of first scene again
+ DelayedScene.scene = HookScene.scene = 0;
+ return false;
+ }
if (IsRestoringScene())
- return;
+ return true;
if (NextScene.scene != 0) {
if (!CountOut) {
@@ -517,12 +675,15 @@ void ChangeScene() {
// Trigger pre-load and fade and start countdown
CountOut = COUNTOUT_COUNT;
FadeOutFast(NULL);
+ if (TinselV2)
+ _vm->_pcmMusic->startFadeOut(COUNTOUT_COUNT);
break;
}
} else if (--CountOut == 0) {
- ClearScreen();
+ if (!TinselV2)
+ ClearScreen();
- NewScene(NextScene.scene, NextScene.entry);
+ StartNewScene(NextScene.scene, NextScene.entry);
NextScene.scene = 0;
switch (NextScene.trans) {
@@ -535,20 +696,32 @@ void ChangeScene() {
SetDoFadeIn(true);
break;
}
- }
+ } else
+ _vm->_pcmMusic->fadeOutIteration();
}
+
+ return false;
}
/**
- * LoadBasicChunks
+ * CuttingScene
*/
+void CuttingScene(bool bCutting) {
+ bCuttingScene = bCutting;
+
+ if (!bCutting)
+ WrapScene();
+}
+/**
+ * LoadBasicChunks
+ */
void LoadBasicChunks(void) {
byte *cptr;
int numObjects;
// Allocate RAM for savescene data
- InitialiseSs();
+ InitialiseSaveScenes();
// CHUNK_TOTAL_ACTORS seems to be missing in the released version, hard coding a value
// TODO: Would be nice to just change 511 to MAX_SAVED_ALIVES
@@ -581,6 +754,23 @@ void LoadBasicChunks(void) {
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_POLY);
if (cptr != NULL)
MaxPolygons(*cptr);
+
+ if (TinselV2) {
+ // Global processes
+ cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_NUM_PROCESSES);
+ assert(cptr && (*cptr < 100));
+ int num = *cptr;
+ cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_PROCESSES);
+ assert(!num || cptr);
+ GlobalProcesses(num, cptr);
+
+ // CdPlay() stuff
+ cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_CDPLAY_HANDLE);
+ assert(cptr);
+ uint32 playHandle = READ_LE_UINT32(cptr);
+ assert(playHandle < 512);
+ SetCdPlayHandle(playHandle);
+ }
}
//----------------- TinselEngine --------------------
@@ -602,10 +792,39 @@ static const GameSettings tinselSettings[] = {
{NULL, NULL, 0, 0, NULL}
};
+const char *TinselEngine::_sampleIndices[][3] = {
+ { "english.idx", "english1.idx", "english2.idx" },
+ { "french.idx", "french1.idx", "french2.idx" },
+ { "german.idx", "german1.idx", "german2.idx" },
+ { "italian.idx", "italian1.idx", "italian2.idx" },
+ { "spanish.idx", "spanish1.idx", "spanish2.idx" },
+};
+const char *TinselEngine::_sampleFiles[][3] = {
+ { "english.smp", "english1.smp", "english2.smp" },
+ { "french.smp", "french1.smp", "french2.smp" },
+ { "german.smp", "german1.smp", "german2.smp" },
+ { "italian.smp", "italian1.smp", "italian2.smp" },
+ { "spanish.smp", "spanish1.smp", "spanish2.smp" },
+};
+const char *TinselEngine::_textFiles[][3] = {
+ { "english.txt", "english1.txt", "english2.txt" },
+ { "french.txt", "french1.txt", "french2.txt" },
+ { "german.txt", "german1.txt", "german2.txt" },
+ { "italian.txt", "italian1.txt", "italian2.txt" },
+ { "spanish.txt", "spanish1.txt", "spanish2.txt" }
+};
+
+
TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc) {
_vm = this;
+ // Register debug flags
+ Common::addSpecialDebugLevel(kTinselDebugAnimations, "animations", "Animations debugging");
+ Common::addSpecialDebugLevel(kTinselDebugActions, "actions", "Actions debugging");
+ Common::addSpecialDebugLevel(kTinselDebugSound, "sound", "Sound debugging");
+ Common::addSpecialDebugLevel(kTinselDebugMusic, "music", "Music debugging");
+
// Setup mixer
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
@@ -629,9 +848,10 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
if (native_mt32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
- _music = new MusicPlayer(_driver);
- //_music->setNativeMT32(native_mt32);
- //_music->setAdlib(adlib);
+ _midiMusic = new MidiMusicPlayer(_driver);
+ _pcmMusic = new PCMMusicPlayer();
+ //_midiMusic->setNativeMT32(native_mt32);
+ //_midiMusic->setAdlib(adlib);
_musicVolume = ConfMan.getInt("music_volume");
@@ -644,25 +864,38 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
}
TinselEngine::~TinselEngine() {
+ if (MoviePlaying())
+ FinishBMV();
+
delete _sound;
- delete _music;
+ delete _midiMusic;
+ delete _pcmMusic;
delete _console;
delete _driver;
_screenSurface.free();
- FreeSs();
+ FreeSaveScenes();
FreeTextBuffer();
FreeHandleTable();
FreeActors();
FreeObjectList();
+ FreeGlobalProcesses();
FreeGlobals();
delete _scheduler;
}
Common::Error TinselEngine::init() {
// Initialize backend
- initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false);
-
- _screenSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT, 1);
+ if (getGameID() == GID_DW2) {
+#ifndef DW2_EXACT_SIZE
+ initGraphics(640, 480, true);
+#else
+ initGraphics(640, 432, true);
+#endif
+ _screenSurface.create(640, 432, 1);
+ } else {
+ initGraphics(320, 200, false);
+ _screenSurface.create(320, 200, 1);
+ }
g_system->getEventManager()->registerRandomSource(_random, "tinsel");
@@ -670,6 +903,8 @@ Common::Error TinselEngine::init() {
_scheduler = new Scheduler();
+ InitSysVars();
+
// init memory manager
MemoryInit();
@@ -684,20 +919,19 @@ Common::Error TinselEngine::init() {
RebootCursor();
RebootDeadTags();
RebootMovers();
+ resetUserEventTime();
RebootTimers();
RebootScalingReels();
DelayedScene.scene = HookScene.scene = 0;
#endif
+ // Load in text strings
+ ChangeLanguage(g_language);
+
// Init palette and object managers, scheduler, keyboard and mouse
RestartDrivers();
- // TODO: More stuff from dos_main.c may have to be added here
-
- // load in text strings
- ChangeLanguage(g_language);
-
// load in graphics info
SetupHandleTable();
@@ -707,14 +941,23 @@ Common::Error TinselEngine::init() {
return Common::kNoError;
}
+void TinselEngine::syncSoundSettings() {
+ // Sync the engine with the config manager
+ int soundVolumeMusic = ConfMan.getInt("music_volume");
+ int soundVolumeSFX = ConfMan.getInt("sfx_volume");
+ int soundVolumeSpeech = ConfMan.getInt("speech_volume");
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
+}
+
Common::String TinselEngine::getSavegameFilename(int16 saveNum) const {
char filename[256];
snprintf(filename, 256, "%s.%03d", getTargetName().c_str(), saveNum);
return filename;
}
-#define GAME_FRAME_DELAY (1000 / ONE_SECOND)
-
Common::Error TinselEngine::go() {
uint32 timerVal = 0;
@@ -758,6 +1001,9 @@ Common::Error TinselEngine::go() {
// Save/Restore scene file transfers
ProcessSRQueue();
+ // Handle any playing movie
+ FettleBMV();
+
#ifdef DEBUG
if (bFast)
continue; // run flat-out
@@ -765,7 +1011,12 @@ Common::Error TinselEngine::go() {
// Loop processing events while there are any pending
while (pollEvent());
- g_system->delayMillis(10);
+ DoCdChange();
+
+ if (MoviePlaying() && NextMovieTime())
+ g_system->delayMillis(MAX<int>(NextMovieTime() - g_system->getMillis() + MovieAudioLag(), 0));
+ else
+ g_system->delayMillis(10);
}
// Write configuration
@@ -776,8 +1027,11 @@ Common::Error TinselEngine::go() {
void TinselEngine::NextGameCycle(void) {
- //
- ChangeScene();
+ // Dim Music
+ _pcmMusic->dimIteration();
+
+ // Check for scene change
+ ChangeScene(false);
// Allow a user event for this schedule
ResetEcount();
@@ -785,8 +1039,11 @@ void TinselEngine::NextGameCycle(void) {
// schedule process
_scheduler->schedule();
- // redraw background
- DrawBackgnd();
+ if (MoviePlaying())
+ CopyMovieToScreen();
+ else
+ // redraw background
+ DrawBackgnd();
// Why waste resources on yet another process?
FettleTimers();
@@ -810,7 +1067,13 @@ bool TinselEngine::pollEvent() {
break;
case Common::EVENT_MOUSEMOVE:
- _mousePos = event.mouse;
+ {
+ // This fragment takes care of Tinsel 2 when it's been compiled with
+ // blank areas at the top and bottom of thes creen
+ int ySize = (g_system->getHeight() - _vm->screen().h) / 2;
+ if ((event.mouse.y >= ySize) && (event.mouse.y < (g_system->getHeight() - ySize)))
+ _mousePos = Common::Point(event.mouse.x, event.mouse.y - ySize);
+ }
break;
case Common::EVENT_KEYDOWN:
@@ -828,7 +1091,6 @@ bool TinselEngine::pollEvent() {
/**
* Start the processes that continue between scenes.
*/
-
void TinselEngine::CreateConstProcesses(void) {
// Process to run the master script
_scheduler->createProcess(PID_MASTER_SCR, MasterScriptProcess, NULL, 0);
@@ -841,7 +1103,6 @@ void TinselEngine::CreateConstProcesses(void) {
/**
* Restart the game
*/
-
void TinselEngine::RestartGame(void) {
HoldItem(INV_NOICON); // Holding nothing
@@ -878,7 +1139,6 @@ void TinselEngine::RestartGame(void) {
/**
* Init palette and object managers, scheduler, keyboard and mouse.
*/
-
void TinselEngine::RestartDrivers(void) {
// init the palette manager
ResetPalAllocator();
@@ -902,13 +1162,12 @@ void TinselEngine::RestartDrivers(void) {
}
// Set midi volume
- SetMidiVolume(volMidi);
+ SetMidiVolume(volMusic);
}
/**
* Remove keyboard, mouse and joystick drivers.
*/
-
void TinselEngine::ChopDrivers(void) {
// remove sound driver
StopMidi();
@@ -923,7 +1182,6 @@ void TinselEngine::ChopDrivers(void) {
/**
* Process a keyboard event
*/
-
void TinselEngine::ProcessKeyEvent(const Common::Event &event) {
// Handle any special keys immediately
@@ -974,4 +1232,48 @@ void TinselEngine::ProcessKeyEvent(const Common::Event &event) {
keypresses.push_back(event);
}
+const char *TinselEngine::getSampleIndex(LANGUAGE lang) {
+ int cd;
+
+ if (TinselV2) {
+ cd = GetCurrentCD();
+ assert((cd == 1) || (cd == 2));
+ assert(((unsigned int) lang) < 5);
+ } else {
+ cd = 0;
+ lang = TXT_ENGLISH;
+ }
+
+ return _sampleIndices[lang][cd];
+}
+
+const char *TinselEngine::getSampleFile(LANGUAGE lang) {
+ int cd;
+
+ if (TinselV2) {
+ cd = GetCurrentCD();
+ assert((cd == 1) || (cd == 2));
+ assert(((unsigned int) lang) < 5);
+ } else {
+ cd = 0;
+ lang = TXT_ENGLISH;
+ }
+
+ return _sampleFiles[lang][cd];
+}
+
+const char *TinselEngine::getTextFile(LANGUAGE lang) {
+ assert(((unsigned int) lang) < 5);
+
+ int cd;
+
+ if (TinselV2) {
+ cd = GetCurrentCD();
+ assert((cd == 1) || (cd == 2));
+ } else
+ cd = 0;
+
+ return _textFiles[lang][cd];
+}
+
} // End of namespace Tinsel
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index 080e539f69..370d500abc 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -28,6 +28,7 @@
#include "common/scummsys.h"
#include "common/system.h"
+#include "common/error.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/util.h"
@@ -39,10 +40,12 @@
#include "tinsel/debugger.h"
#include "tinsel/graphics.h"
#include "tinsel/sound.h"
+#include "tinsel/dw.h"
namespace Tinsel {
-class MusicPlayer;
+class MidiMusicPlayer;
+class PCMMusicPlayer;
class Scheduler;
class SoundManager;
@@ -65,11 +68,34 @@ enum TinselGameFeatures {
GF_USE_5FLAGS = 1 << 6 // All 5 flags
};
+/**
+ * The following is the ScummVM definitions of the various Tinsel versions:
+ * TINSEL_V0 - This was an early engine version that was only used in the Discworld 1
+ * demo. It is not currently supported.
+ * TINSEL_V1 - This was the engine version used by Discworld 1. Note that there were two
+ * major releases: an earlier version that used *.gra files, and a later one that
+ * used *.scn files, and contained certain script and engine bugfixes. In ScummVM,
+ * we treat both releases as 'Tinsel 1', since the engine fixes from the later
+ * version work equally well the earlier version data.
+ * TINSEL_V2 - This is the engine used for the Discworld 2 game.
+ */
enum TinselEngineVersion {
- TINSEL_V0 = 0, // Used in the DW1 demo only
- TINSEL_V1 = 1
+ TINSEL_V0 = 0,
+ TINSEL_V1 = 1,
+ TINSEL_V2 = 2
+};
+
+enum {
+ kTinselDebugAnimations = 1 << 0,
+ kTinselDebugActions = 1 << 1,
+ kTinselDebugSound = 1 << 2,
+ kTinselDebugMusic = 2 << 3
};
+#define DEBUG_BASIC 1
+#define DEBUG_INTERMEDIATE 2
+#define DEBUG_DETAILED 3
+
struct TinselGameDescription;
enum TinselKeyDirection {
@@ -79,6 +105,22 @@ enum TinselKeyDirection {
typedef bool (*KEYFPTR)(const Common::KeyState &);
+#define SCREEN_WIDTH (_vm->screen().w) // PC screen dimensions
+#define SCREEN_HEIGHT (_vm->screen().h)
+#define SCRN_CENTRE_X ((SCREEN_WIDTH - 1) / 2) // screen centre x
+#define SCRN_CENTRE_Y ((SCREEN_HEIGHT - 1) / 2) // screen centre y
+#define UNUSED_LINES 48
+#define EXTRA_UNUSED_LINES 3
+//#define SCREEN_BOX_HEIGHT1 (SCREEN_HEIGHT - UNUSED_LINES)
+//#define SCREEN_BOX_HEIGHT2 (SCREEN_BOX_HEIGHT1 - EXTRA_UNUSED_LINES)
+#define SCREEN_BOX_HEIGHT1 SCREEN_HEIGHT
+#define SCREEN_BOX_HEIGHT2 SCREEN_HEIGHT
+
+#define GAME_FRAME_DELAY (1000 / ONE_SECOND)
+
+#define TinselVersion (_vm->getVersion())
+#define TinselV2 (TinselVersion == TINSEL_V2)
+
class TinselEngine : public Engine {
int _gameId;
Common::KeyState _keyPressed;
@@ -89,11 +131,16 @@ class TinselEngine : public Engine {
Console *_console;
Scheduler *_scheduler;
+ static const char *_sampleIndices[][3];
+ static const char *_sampleFiles[][3];
+ static const char *_textFiles[][3];
+
protected:
// Engine APIs
virtual Common::Error init();
virtual Common::Error go();
+ virtual void syncSoundSettings();
public:
TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc);
@@ -109,13 +156,18 @@ public:
uint16 getVersion() const;
Common::Platform getPlatform() const;
+ const char *getSampleIndex(LANGUAGE lang);
+ const char *getSampleFile(LANGUAGE lang);
+ const char *getTextFile(LANGUAGE lang);
+
MidiDriver *_driver;
SoundManager *_sound;
- MusicPlayer *_music;
+ MidiMusicPlayer *_midiMusic;
+ PCMMusicPlayer *_pcmMusic;
KEYFPTR _keyHandler;
private:
- //MusicPlayer *_music;
+ //MidiMusicPlayer *_midiMusic;
int _musicVolume;
void NextGameCycle(void);
@@ -134,7 +186,8 @@ public:
Common::Point getMousePosition() const { return _mousePos; }
void setMousePosition(const Common::Point &pt) {
- g_system->warpMouse(pt.x, pt.y);
+ int ySize = (g_system->getHeight() - _screenSurface.h) / 2;
+ g_system->warpMouse(pt.x, pt.y + ySize);
_mousePos = pt;
}
void divertKeyInput(KEYFPTR fptr) { _keyHandler = fptr; }
@@ -145,6 +198,11 @@ public:
// Global reference to the TinselEngine object
extern TinselEngine *_vm;
+// Externally available methods
+void CuttingScene(bool bCutting);
+void CDChangeForRestore(int cdNumber);
+void CdHasChanged(void);
+
} // End of namespace Tinsel
#endif /* TINSEL_H */