Initial commit
This commit is contained in:
4
engines/tinsel/POTFILES
Normal file
4
engines/tinsel/POTFILES
Normal file
@@ -0,0 +1,4 @@
|
||||
engines/tinsel/detection_tables.h
|
||||
engines/tinsel/metaengine.cpp
|
||||
engines/tinsel/saveload.cpp
|
||||
engines/tinsel/tinsel.cpp
|
||||
1694
engines/tinsel/actors.cpp
Normal file
1694
engines/tinsel/actors.cpp
Normal file
File diff suppressed because it is too large
Load Diff
228
engines/tinsel/actors.h
Normal file
228
engines/tinsel/actors.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Prototypes of actor functions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_ACTOR_H // prevent multiple includes
|
||||
#define TINSEL_ACTOR_H
|
||||
|
||||
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
#include "tinsel/events.h" // for TINSEL_EVENT
|
||||
#include "tinsel/palette.h" // for COLORREF
|
||||
#include "tinsel/movers.h" // for MOVER *
|
||||
|
||||
namespace Common {
|
||||
class Serializer;
|
||||
}
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct FREEL;
|
||||
struct INT_CONTEXT;
|
||||
struct MOVER;
|
||||
struct OBJECT;
|
||||
struct ACTORINFO;
|
||||
struct Z_POSITIONS;
|
||||
|
||||
#define ACTORTAG_KEY 0x1000000
|
||||
#define OTH_RELATEDACTOR 0x00000fff
|
||||
#define OTH_RELATIVE 0x00001000
|
||||
#define OTH_ABSOLUTE 0x00002000
|
||||
#define MAX_TAGACTORS 10
|
||||
#define NUM_ZPOSITIONS 200 // Reasonable-sounding number
|
||||
#define MAX_REELS 6
|
||||
|
||||
struct TAGACTORDATA {
|
||||
// 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.
|
||||
};
|
||||
|
||||
struct SAVED_ACTOR {
|
||||
short actorID;
|
||||
short zFactor;
|
||||
bool bAlive;
|
||||
bool bHidden;
|
||||
SCNHANDLE presFilm; ///< the film that reel belongs to
|
||||
short presRnum; ///< the present reel number
|
||||
short presPlayX, presPlayY;
|
||||
};
|
||||
|
||||
struct Z_POSITIONS {
|
||||
short actor;
|
||||
short column;
|
||||
int z;
|
||||
};
|
||||
|
||||
struct ACTORDATA {
|
||||
int32 masking; ///< type of actor masking (Tinsel V1)
|
||||
SCNHANDLE hActorId; ///< handle actor ID string index
|
||||
SCNHANDLE hActorCode; ///< handle to actor script
|
||||
SCNHANDLE hTagText; // tag (Tinsel V2)
|
||||
int32 tagPortionV; // defines tag area (Tinsel V2)
|
||||
int32 tagPortionH; // defines tag area (Tinsel V2)
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
class Actor {
|
||||
public:
|
||||
Actor();
|
||||
virtual ~Actor();
|
||||
|
||||
int GetLeadId();
|
||||
SCNHANDLE GetActorTagHandle(int ano);
|
||||
void ToggleActor(int ano, bool show);
|
||||
SCNHANDLE GetActorCode(int ano);
|
||||
SCNHANDLE GetTaggedActorCode(int ano);
|
||||
void RunCodeToCompletion(int ano);
|
||||
int GetCount() { return _numActors; }
|
||||
|
||||
void RegisterActors(int num);
|
||||
void SetLeadId(int rid);
|
||||
bool ActorIsGhost(int actor);
|
||||
void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript);
|
||||
void DropActors(); // No actor reels running
|
||||
|
||||
void DisableActor(int actor);
|
||||
void EnableActor(int actor);
|
||||
|
||||
void Tag_Actor(int ano, SCNHANDLE tagtext, int tp);
|
||||
void UnTagActor(int ano);
|
||||
void ReTagActor(int ano);
|
||||
int TagType(int ano);
|
||||
|
||||
bool actorAlive(int ano);
|
||||
int32 actorMaskType(int ano);
|
||||
void GetActorPos(int ano, int *x, int *y);
|
||||
void GetActorMidTop(int ano, int *x, int *y);
|
||||
int GetActorLeft(int ano);
|
||||
int GetActorRight(int ano);
|
||||
int GetActorTop(int ano);
|
||||
int GetActorBottom(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 hFilm, OBJECT *pobj, int reelnum, int x, int y);
|
||||
const FREEL *actorReel(int ano);
|
||||
|
||||
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();
|
||||
int NextTaggedActor();
|
||||
int NextTaggedActor(int previous);
|
||||
int AsetZPos(OBJECT *pObj, int y, int32 zFactor);
|
||||
|
||||
void storeActorAttr(int ano, int r1, int g1, int b1);
|
||||
COLORREF GetActorRGB(int ano);
|
||||
void SetActorRGB(int ano, COLORREF color);
|
||||
void SetActorZfactor(int ano, uint32 zFactor);
|
||||
uint32 GetActorZfactor(int ano);
|
||||
void SetActorsOn();
|
||||
void ActorsLife(int id, bool bAlive);
|
||||
void dwEndActor(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 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);
|
||||
|
||||
int SaveActors(SAVED_ACTOR *sActorInfo);
|
||||
void RestoreActors(int numActors, SAVED_ACTOR *sActorInfo);
|
||||
|
||||
void SaveZpositions(void *zpp);
|
||||
void RestoreZpositions(void *zpp);
|
||||
|
||||
void SaveActorZ(byte *saveActorZ);
|
||||
void RestoreActorZ(byte *saveActorZ);
|
||||
|
||||
int TaggedActorIndex(int actor);
|
||||
|
||||
void syncAllActorsAlive(Common::Serializer &s);
|
||||
|
||||
private:
|
||||
void StartActor(const ACTORDATA *ad, bool bRunScript);
|
||||
void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right);
|
||||
|
||||
ACTORINFO *_actorInfo;
|
||||
COLORREF _defaultColor; // Text color
|
||||
bool _actorsOn;
|
||||
int ti;
|
||||
TAGACTORDATA _taggedActors[MAX_TAGACTORS];
|
||||
int _numTaggedActors;
|
||||
uint8 *_zFactors;
|
||||
Z_POSITIONS _zPositions[NUM_ZPOSITIONS];
|
||||
int _leadActorId; // The lead actor
|
||||
int _numActors; // The total number of actors in the game
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be);
|
||||
void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
|
||||
void ActorEvent(int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
|
||||
void ShowActor(CORO_PARAM, int ano);
|
||||
void HideActor(CORO_PARAM, int ano);
|
||||
void RestoreActorProcess(int id, INT_CONTEXT *pic, bool savegameFlag);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_ACTOR_H */
|
||||
169
engines/tinsel/adpcm.cpp
Normal file
169
engines/tinsel/adpcm.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "tinsel/adpcm.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
static const double TinselFilterTable[4][2] = {
|
||||
{0, 0 },
|
||||
{0.9375, 0},
|
||||
{1.796875, -0.8125},
|
||||
{1.53125, -0.859375}
|
||||
};
|
||||
|
||||
void Tinsel_ADPCMStream::readBufferTinselHeader() {
|
||||
uint8 start = _stream->readByte();
|
||||
uint8 filterVal = (start & 0xC0) >> 6;
|
||||
|
||||
if ((start & 0x20) != 0) {
|
||||
//Lower 6 bit are negative
|
||||
|
||||
// Negate
|
||||
start = ~(start | 0xC0) + 1;
|
||||
|
||||
_status.predictor = (uint64)1 << start;
|
||||
} else {
|
||||
// Lower 6 bit are positive
|
||||
|
||||
// Truncate
|
||||
start &= 0x1F;
|
||||
|
||||
_status.predictor = ((double) 1.0) / ((uint64)1 << start);
|
||||
}
|
||||
|
||||
_status.K0 = TinselFilterTable[filterVal][0];
|
||||
_status.K1 = TinselFilterTable[filterVal][1];
|
||||
}
|
||||
|
||||
int16 Tinsel_ADPCMStream::decodeTinsel(int16 code, double eVal) {
|
||||
double sample;
|
||||
|
||||
sample = (double)code;
|
||||
sample *= eVal * _status.predictor;
|
||||
sample += (_status.d0 * _status.K0) + (_status.d1 * _status.K1);
|
||||
|
||||
_status.d1 = _status.d0;
|
||||
_status.d0 = sample;
|
||||
|
||||
return (int16) CLIP<double>(sample, -32768.0, 32767.0);
|
||||
}
|
||||
|
||||
int Tinsel4_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
uint16 data;
|
||||
const double eVal = 1.142822265;
|
||||
|
||||
samples = 0;
|
||||
|
||||
assert(numSamples % 2 == 0);
|
||||
|
||||
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
|
||||
if (_blockPos[0] == _blockAlign) {
|
||||
readBufferTinselHeader();
|
||||
_blockPos[0] = 0;
|
||||
}
|
||||
|
||||
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos[0]++) {
|
||||
// Read 1 byte = 8 bits = two 4 bit blocks
|
||||
data = _stream->readByte();
|
||||
buffer[samples] = decodeTinsel((data << 8) & 0xF000, eVal);
|
||||
buffer[samples+1] = decodeTinsel((data << 12) & 0xF000, eVal);
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
int Tinsel6_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
const double eVal = 1.032226562;
|
||||
|
||||
samples = 0;
|
||||
|
||||
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
|
||||
if (_blockPos[0] == _blockAlign) {
|
||||
readBufferTinselHeader();
|
||||
_blockPos[0] = 0;
|
||||
_chunkPos = 0;
|
||||
}
|
||||
|
||||
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) {
|
||||
|
||||
switch (_chunkPos) {
|
||||
case 0:
|
||||
_chunkData = _stream->readByte();
|
||||
buffer[samples] = decodeTinsel((_chunkData << 8) & 0xFC00, eVal);
|
||||
break;
|
||||
case 1:
|
||||
_chunkData = (_chunkData << 8) | (_stream->readByte());
|
||||
buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal);
|
||||
_blockPos[0]++;
|
||||
break;
|
||||
case 2:
|
||||
_chunkData = (_chunkData << 8) | (_stream->readByte());
|
||||
buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal);
|
||||
_blockPos[0]++;
|
||||
break;
|
||||
case 3:
|
||||
_chunkData = (_chunkData << 8);
|
||||
buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal);
|
||||
_blockPos[0]++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
int Tinsel8_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
byte data;
|
||||
const double eVal = 1.007843258;
|
||||
|
||||
samples = 0;
|
||||
|
||||
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
|
||||
if (_blockPos[0] == _blockAlign) {
|
||||
readBufferTinselHeader();
|
||||
_blockPos[0] = 0;
|
||||
}
|
||||
|
||||
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos[0]++) {
|
||||
// Read 1 byte = 8 bits = one 8 bit block
|
||||
data = _stream->readByte();
|
||||
buffer[samples] = decodeTinsel(data << 8, eVal);
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
101
engines/tinsel/adpcm.h
Normal file
101
engines/tinsel/adpcm.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_ADPCM_H
|
||||
#define TINSEL_ADPCM_H
|
||||
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
class Tinsel_ADPCMStream : public Audio::ADPCMStream {
|
||||
protected:
|
||||
struct {
|
||||
// Tinsel
|
||||
double predictor;
|
||||
double K0, K1;
|
||||
double d0, d1;
|
||||
} _status;
|
||||
|
||||
void reset() override {
|
||||
ADPCMStream::reset();
|
||||
memset(&_status, 0, sizeof(_status));
|
||||
}
|
||||
|
||||
int16 decodeTinsel(int16, double);
|
||||
void readBufferTinselHeader();
|
||||
|
||||
public:
|
||||
Tinsel_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
|
||||
|
||||
if (blockAlign == 0)
|
||||
error("Tinsel_ADPCMStream(): blockAlign isn't specified");
|
||||
|
||||
if (channels != 1)
|
||||
error("Tinsel_ADPCMStream(): Tinsel ADPCM only supports mono");
|
||||
|
||||
memset(&_status, 0, sizeof(_status));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Tinsel4_ADPCMStream : public Tinsel_ADPCMStream {
|
||||
public:
|
||||
Tinsel4_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
};
|
||||
|
||||
class Tinsel6_ADPCMStream : public Tinsel_ADPCMStream {
|
||||
protected:
|
||||
uint8 _chunkPos;
|
||||
uint16 _chunkData;
|
||||
|
||||
void reset() override {
|
||||
ADPCMStream::reset();
|
||||
_chunkPos = 0;
|
||||
_chunkData = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
Tinsel6_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
|
||||
_chunkPos = 0;
|
||||
_chunkData = 0;
|
||||
}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
};
|
||||
|
||||
class Tinsel8_ADPCMStream : public Tinsel_ADPCMStream {
|
||||
public:
|
||||
Tinsel8_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
448
engines/tinsel/anim.cpp
Normal file
448
engines/tinsel/anim.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains utilities to handle object animation.
|
||||
*/
|
||||
|
||||
#include "tinsel/anim.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/multiobj.h" // multi-part object defintions etc.
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/**
|
||||
* Advance to next frame routine.
|
||||
* @param pAnim Animation data structure
|
||||
*/
|
||||
SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
|
||||
// get a pointer to the script
|
||||
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)_vm->_handle->LockMem(pAnim->hScript);
|
||||
|
||||
while (1) { // repeat until a real image
|
||||
debugC(DEBUG_DETAILED, kTinselDebugAnimations,
|
||||
"DoNextFrame %ph index=%d, op=%xh", (const void *)pAnim, pAnim->scriptIndex,
|
||||
FROM_32(pAni[pAnim->scriptIndex].op));
|
||||
|
||||
switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) {
|
||||
case ANI_END: // end of animation script
|
||||
|
||||
// move to next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// indicate script has finished
|
||||
return ScriptFinished;
|
||||
|
||||
case ANI_JUMP: // do animation jump
|
||||
|
||||
// move to jump address
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// jump to new frame position
|
||||
pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op);
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
|
||||
case ANI_HFLIP: // flip animated object horizontally
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiHorizontalFlip(pAnim->pObject);
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
|
||||
case ANI_VFLIP: // flip animated object vertically
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiVerticalFlip(pAnim->pObject);
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
case ANI_HVFLIP: // flip animated object in both directions
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiHorizontalFlip(pAnim->pObject);
|
||||
MultiVerticalFlip(pAnim->pObject);
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
|
||||
case ANI_ADJUSTX: // adjust animated object x animation point
|
||||
|
||||
// move to x adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0);
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
|
||||
case ANI_ADJUSTY: // adjust animated object y animation point
|
||||
|
||||
// move to y adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op));
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
|
||||
case ANI_ADJUSTXY: // adjust animated object x & y animation points
|
||||
{
|
||||
int x, y;
|
||||
|
||||
// move to x adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
x = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
|
||||
|
||||
// move to y adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
y = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
|
||||
|
||||
MultiAdjustXY(pAnim->pObject, x, y);
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
}
|
||||
|
||||
case ANI_NOSLEEP: // do not sleep for this frame
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// indicate not to sleep
|
||||
return ScriptNoSleep;
|
||||
|
||||
case ANI_CALL: // call routine
|
||||
|
||||
// move to function address
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// make function call
|
||||
|
||||
// REMOVED BUGGY CODE
|
||||
// pFunc is a function pointer that's part of a union and is assumed to be 32-bits.
|
||||
// There is no known place where a function pointer is stored inside the animation
|
||||
// scripts, something which wouldn't have worked anyway. Having played through the
|
||||
// entire game, there hasn't been any occurrence of this case, so just error out here
|
||||
// in case we missed something (highly unlikely though)
|
||||
error("ANI_CALL opcode encountered! Please report this error to the ScummVM team");
|
||||
//(*pAni[pAnim->scriptIndex].pFunc)(pAnim);
|
||||
return ScriptSleep; // for compilers that don't support NORETURN
|
||||
|
||||
#if 0
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// go fetch a real image
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ANI_HIDE: // hide animated object
|
||||
|
||||
MultiHideObject(pAnim->pObject);
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// dont skip a sleep
|
||||
return ScriptSleep;
|
||||
|
||||
default: // must be an actual animation frame handle
|
||||
|
||||
// set objects new animation frame
|
||||
pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame);
|
||||
|
||||
// re-shape the object
|
||||
MultiReshape(pAnim->pObject);
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// dont skip a sleep
|
||||
return ScriptSleep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init a ANIM structure for single stepping through a animation script.
|
||||
* @param pAnim Animation data structure
|
||||
* @param pAniObj Object to animate
|
||||
* @param hNewScript Script of multipart frames
|
||||
* @param aniSpeed Sets speed of animation in frames
|
||||
*/
|
||||
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, (void *)pAnim);
|
||||
|
||||
pAnim->aniDelta = 1; // will animate on next call to NextAnimRate
|
||||
pAnim->pObject = pAniObj; // set object to animate
|
||||
pAnim->hScript = hNewScript; // set animation script
|
||||
pAnim->scriptIndex = 0; // start of script
|
||||
pAnim->aniRate = aniSpeed; // set speed of animation
|
||||
|
||||
// reset flip flags for the object - let the script do the flipping
|
||||
for (pObj = pAniObj; pObj != NULL; pObj = pObj->pSlave) {
|
||||
AnimateObjectFlags(pObj, pObj->flags & ~(DMA_FLIPH | DMA_FLIPV),
|
||||
pObj->hImg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the next command in a animation script.
|
||||
* @param pAnim Animation data structure
|
||||
*/
|
||||
SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
|
||||
SCRIPTSTATE state;
|
||||
|
||||
if (--pAnim->aniDelta == 0) {
|
||||
// re-init animation delta counter
|
||||
pAnim->aniDelta = pAnim->aniRate;
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
state = DoNextFrame(pAnim);
|
||||
else {
|
||||
// move to next frame
|
||||
while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
|
||||
;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// indicate calling task should sleep
|
||||
return ScriptSleep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the specified number of frames.
|
||||
* @param pAnim Animation data structure
|
||||
* @param numFrames Number of frames to skip
|
||||
*/
|
||||
void SkipFrames(ANIM *pAnim, int numFrames) {
|
||||
// get a pointer to the script
|
||||
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)_vm->_handle->LockMem(pAnim->hScript);
|
||||
|
||||
if ((TinselVersion <= 1) && (numFrames <= 0))
|
||||
// do nothing
|
||||
return;
|
||||
|
||||
while (1) { // repeat until a real image
|
||||
|
||||
switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) {
|
||||
case ANI_END: // end of animation script
|
||||
// going off the end is probably a error, but only in Tinsel 1
|
||||
if (TinselVersion <= 1)
|
||||
error("SkipFrames(): formally 'assert(0)!'");
|
||||
return;
|
||||
|
||||
case ANI_JUMP: // do animation jump
|
||||
|
||||
// move to jump address
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
// jump to new frame position
|
||||
pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op);
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
// Done if skip to jump
|
||||
return;
|
||||
break;
|
||||
|
||||
case ANI_HFLIP: // flip animated object horizontally
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiHorizontalFlip(pAnim->pObject);
|
||||
break;
|
||||
|
||||
case ANI_VFLIP: // flip animated object vertically
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiVerticalFlip(pAnim->pObject);
|
||||
break;
|
||||
|
||||
case ANI_HVFLIP: // flip animated object in both directions
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiHorizontalFlip(pAnim->pObject);
|
||||
MultiVerticalFlip(pAnim->pObject);
|
||||
break;
|
||||
|
||||
case ANI_ADJUSTX: // adjust animated object x animation point
|
||||
|
||||
// move to x adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0);
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
break;
|
||||
|
||||
case ANI_ADJUSTY: // adjust animated object y animation point
|
||||
|
||||
// move to y adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op));
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
break;
|
||||
|
||||
case ANI_ADJUSTXY: // adjust animated object x & y animation points
|
||||
{
|
||||
int x, y;
|
||||
|
||||
// move to x adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
x = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
|
||||
|
||||
// move to y adjustment operand
|
||||
pAnim->scriptIndex++;
|
||||
y = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
|
||||
|
||||
MultiAdjustXY(pAnim->pObject, x, y);
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ANI_NOSLEEP: // do not sleep for this frame
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
break;
|
||||
|
||||
case ANI_CALL: // call routine
|
||||
|
||||
// skip function address
|
||||
pAnim->scriptIndex += 2;
|
||||
break;
|
||||
|
||||
case ANI_HIDE: // hide animated object
|
||||
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
break;
|
||||
|
||||
default: // must be an actual animation frame handle
|
||||
|
||||
// one less frame
|
||||
if (numFrames == 0)
|
||||
return;
|
||||
|
||||
if (numFrames == -1 || numFrames-- > 0) {
|
||||
// next opcode
|
||||
pAnim->scriptIndex++;
|
||||
} else {
|
||||
// set objects new animation frame
|
||||
pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame);
|
||||
|
||||
// re-shape the object
|
||||
MultiReshape(pAnim->pObject);
|
||||
|
||||
// we have skipped to the correct place
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* About to jump or end
|
||||
* @param pAnim Animation data structure
|
||||
*/
|
||||
bool AboutToJumpOrEnd(ANIM *pAnim) {
|
||||
if (pAnim->aniDelta == 1) {
|
||||
// get a pointer to the script
|
||||
ANI_SCRIPT *pAni = (ANI_SCRIPT *)_vm->_handle->LockMem(pAnim->hScript);
|
||||
int zzz = pAnim->scriptIndex;
|
||||
|
||||
for (;;) {
|
||||
// repeat until a real image
|
||||
switch (FROM_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
|
||||
91
engines/tinsel/anim.h
Normal file
91
engines/tinsel/anim.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Object animation definitions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_ANIM_H // prevent multiple includes
|
||||
#define TINSEL_ANIM_H
|
||||
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct OBJECT;
|
||||
|
||||
/** animation structure */
|
||||
struct ANIM {
|
||||
int aniRate; ///< animation speed
|
||||
int aniDelta; ///< animation speed delta counter
|
||||
OBJECT *pObject; ///< object to animate (assumed to be multi-part)
|
||||
uint32 hScript; ///< animation script handle
|
||||
int scriptIndex; ///< current position in animation script
|
||||
};
|
||||
|
||||
/** 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
|
||||
};
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Anim Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
/** states for DoNextFrame */
|
||||
enum SCRIPTSTATE {ScriptFinished, ScriptNoSleep, ScriptSleep};
|
||||
|
||||
SCRIPTSTATE DoNextFrame( // Execute the next animation frame of a animation script
|
||||
ANIM *pAnim); // animation data structure
|
||||
|
||||
void InitStepAnimScript( // Init a ANIM struct for single stepping through a animation script
|
||||
ANIM *pAnim, // animation data structure
|
||||
OBJECT *pAniObj, // object to animate
|
||||
SCNHANDLE hNewScript, // handle to script of multipart frames
|
||||
int aniSpeed); // sets speed of animation in frames
|
||||
|
||||
SCRIPTSTATE StepAnimScript( // Execute the next command in a animation script
|
||||
ANIM *pAnim); // animation data structure
|
||||
|
||||
void SkipFrames( // Skip the specified number of frames
|
||||
ANIM *pAnim, // animation data structure
|
||||
int numFrames); // number of frames to skip
|
||||
|
||||
bool AboutToJumpOrEnd(ANIM *pAnim);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_ANIM_H
|
||||
330
engines/tinsel/background.cpp
Normal file
330
engines/tinsel/background.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Background handling code.
|
||||
*/
|
||||
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/cliprect.h" // object clip rect defs
|
||||
#include "tinsel/font.h"
|
||||
#include "tinsel/graphics.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/sched.h" // process sheduler defs
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/pid.h" // process identifiers
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/noir/spriter.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
Background::Background(Font* font) : _font(font), _pCurBgnd(nullptr), _hBgPal(0), _BGspeed(0), _hBackground(0), _bDoFadeIn(false), _bgReels(0) {
|
||||
for (int i = 0; i < MAX_BG; i++) {
|
||||
_pBG[i] = nullptr;
|
||||
_thisAnim[i].pObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to initialize a background.
|
||||
*/
|
||||
void Background::InitBackground() {
|
||||
// set current background
|
||||
_pCurBgnd = new BACKGND();
|
||||
_pCurBgnd->rgbSkyColor = BLACK;
|
||||
_pCurBgnd->ptInitWorld = Common::Point(0, 0);
|
||||
_pCurBgnd->rcScrollLimits = Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
_pCurBgnd->refreshRate = 0; // no background update process
|
||||
_pCurBgnd->pXscrollTable = nullptr;
|
||||
_pCurBgnd->pYscrollTable = nullptr;
|
||||
_pCurBgnd->bAutoErase = false;
|
||||
|
||||
int numPlayFields = 2;
|
||||
if (TinselVersion == 3) {
|
||||
numPlayFields = 9;
|
||||
}
|
||||
for (int i = 0; i < numPlayFields; ++i) {
|
||||
PLAYFIELD playfield = {
|
||||
NULL, // display list
|
||||
0, // init field x
|
||||
0, // init field y
|
||||
0, // x vel
|
||||
0, // y vel
|
||||
Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), // clip rect
|
||||
false // moved flag
|
||||
};
|
||||
_pCurBgnd->fieldArray.push_back(playfield);
|
||||
}
|
||||
|
||||
// init background sky color
|
||||
SetBgndColor(_pCurBgnd->rgbSkyColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the xy position of the specified playfield in the current background.
|
||||
* @param which Which playfield
|
||||
* @param newXpos New x position
|
||||
* @param newYpos New y position
|
||||
*/
|
||||
|
||||
void Background::PlayfieldSetPos(unsigned int which, int newXpos, int newYpos) {
|
||||
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 < _pCurBgnd->fieldArray.size());
|
||||
|
||||
// get playfield pointer
|
||||
pPlayfield = &_pCurBgnd->fieldArray[which];
|
||||
|
||||
// set new integer position
|
||||
pPlayfield->fieldX = intToFrac(newXpos);
|
||||
pPlayfield->fieldY = intToFrac(newYpos);
|
||||
|
||||
// set moved flag
|
||||
pPlayfield->bMoved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the xy position of the specified playfield in the current background.
|
||||
* @param which Which playfield
|
||||
* @param pXpos Returns current x position
|
||||
* @param pYpos Returns current y position
|
||||
*/
|
||||
|
||||
void Background::PlayfieldGetPos(unsigned int which, int *pXpos, int *pYpos) {
|
||||
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 < _pCurBgnd->fieldArray.size());
|
||||
|
||||
// get playfield pointer
|
||||
pPlayfield = &_pCurBgnd->fieldArray[which];
|
||||
|
||||
// get current integer position
|
||||
*pXpos = fracToInt(pPlayfield->fieldX);
|
||||
*pYpos = fracToInt(pPlayfield->fieldY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x position of the center of the specified playfield
|
||||
* @param which Which playfield
|
||||
*/
|
||||
|
||||
int Background::PlayfieldGetCenterX(unsigned 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 < _pCurBgnd->fieldArray.size());
|
||||
|
||||
// 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
|
||||
*/
|
||||
|
||||
OBJECT **Background::GetPlayfieldList(unsigned 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 < _pCurBgnd->fieldArray.size());
|
||||
|
||||
// get playfield pointer
|
||||
pPlayfield = &_pCurBgnd->fieldArray[which];
|
||||
|
||||
// return the display list pointer for this playfield
|
||||
return &pPlayfield->pDispList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all the playfield object lists for the current background.
|
||||
* The playfield velocity is added to the playfield position in order
|
||||
* to scroll each playfield before it is drawn.
|
||||
*/
|
||||
|
||||
void Background::DrawBackgnd() {
|
||||
PLAYFIELD *pPlay; // playfield pointer
|
||||
int prevX, prevY; // save interger part of position
|
||||
Common::Point ptWin; // window top left
|
||||
|
||||
if (_pCurBgnd == NULL)
|
||||
return; // no current background
|
||||
|
||||
// scroll each background playfield
|
||||
for (unsigned int i = 0; i < _pCurBgnd->fieldArray.size(); i++) {
|
||||
// get pointer to correct playfield
|
||||
pPlay = &_pCurBgnd->fieldArray[i];
|
||||
|
||||
// save integer part of position
|
||||
prevX = fracToInt(pPlay->fieldX);
|
||||
prevY = fracToInt(pPlay->fieldY);
|
||||
|
||||
// update scrolling
|
||||
pPlay->fieldX += pPlay->fieldXvel;
|
||||
pPlay->fieldY += pPlay->fieldYvel;
|
||||
|
||||
// convert fixed point window pos to a int
|
||||
ptWin.x = fracToInt(pPlay->fieldX);
|
||||
ptWin.y = fracToInt(pPlay->fieldY);
|
||||
|
||||
// set the moved flag if the playfield has moved
|
||||
if (prevX != ptWin.x || prevY != ptWin.y)
|
||||
pPlay->bMoved = true;
|
||||
|
||||
// sort the display list for this background - just in case somebody has changed object Z positions
|
||||
SortObjectList(&pPlay->pDispList);
|
||||
|
||||
// generate clipping rects for all objects that have moved etc.
|
||||
FindMovingObjects(&pPlay->pDispList, &ptWin,
|
||||
&pPlay->rcClip, false, pPlay->bMoved);
|
||||
|
||||
// clear playfield moved flag
|
||||
pPlay->bMoved = false;
|
||||
}
|
||||
|
||||
// merge the clipping rectangles
|
||||
MergeClipRect();
|
||||
|
||||
// redraw all playfields within the clipping rectangles
|
||||
const RectList &clipRects = GetClipRects();
|
||||
|
||||
// Noir 3D model's cliprect is updated for rendering.
|
||||
if (TinselVersion == 3) {
|
||||
for (unsigned int i = 0; i < _pCurBgnd->fieldArray.size(); i++) {
|
||||
// get pointer to correct playfield
|
||||
pPlay = &_pCurBgnd->fieldArray[i];
|
||||
|
||||
// convert fixed point window pos to a int
|
||||
ptWin.x = fracToInt(pPlay->fieldX);
|
||||
ptWin.y = fracToInt(pPlay->fieldY);
|
||||
|
||||
for (OBJECT* pObj = pPlay->pDispList; pObj != NULL; pObj = pObj->pNext) {
|
||||
if (pObj->flags & DMA_3D) {
|
||||
_vm->_spriter->Draw(0, 0, 0, 0, 0);
|
||||
// TODO: fix clip rect after the Spriter updates them
|
||||
//AddClipRect(SpriterGetModelRect());
|
||||
//AddClipRect(SpriterGetShadowRect());
|
||||
//MergeClipRect();
|
||||
} else {
|
||||
for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) {
|
||||
Common::Rect rcPlayClip; // clip rect for this playfield
|
||||
if (IntersectRectangle(rcPlayClip, pPlay->rcClip, *r)) {
|
||||
// redraw all objects within this clipping rect
|
||||
UpdateClipRectSingle(pObj, &ptWin, &rcPlayClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) {
|
||||
// clear the clip rectangle on the virtual screen
|
||||
// for each background playfield
|
||||
for (unsigned int i = 0; i < _pCurBgnd->fieldArray.size(); i++) {
|
||||
Common::Rect rcPlayClip; // clip rect for this playfield
|
||||
|
||||
// get pointer to correct playfield
|
||||
pPlay = &_pCurBgnd->fieldArray[i];
|
||||
|
||||
// convert fixed point window pos to a int
|
||||
ptWin.x = fracToInt(pPlay->fieldX);
|
||||
ptWin.y = fracToInt(pPlay->fieldY);
|
||||
|
||||
if (IntersectRectangle(rcPlayClip, pPlay->rcClip, *r))
|
||||
// redraw all objects within this clipping rect
|
||||
UpdateClipRect(&pPlay->pDispList, &ptWin, &rcPlayClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TinselVersion != 3) {
|
||||
// transfer any new palettes to the video DAC
|
||||
PalettesToVideoDAC();
|
||||
}
|
||||
|
||||
// update the screen within the clipping rectangles
|
||||
for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) {
|
||||
UpdateScreenRect(*r);
|
||||
}
|
||||
|
||||
g_system->updateScreen();
|
||||
|
||||
// delete all the clipping rectangles
|
||||
ResetClipRect();
|
||||
}
|
||||
|
||||
int Background::BgWidth() {
|
||||
assert(_pBG[0]);
|
||||
return MultiRightmost(_pBG[0]) + 1;
|
||||
}
|
||||
|
||||
int Background::BgHeight() {
|
||||
assert(_pBG[0]);
|
||||
return MultiLowest(_pBG[0]) + 1;
|
||||
}
|
||||
|
||||
void Background::SetBackPal(SCNHANDLE hPal) {
|
||||
_hBgPal = hPal;
|
||||
|
||||
_font->FettleFontPal(_hBgPal);
|
||||
CreateTranslucentPalette(_hBgPal);
|
||||
}
|
||||
|
||||
void Background::DropBackground() {
|
||||
_pBG[0] = nullptr; // No background
|
||||
|
||||
if (TinselVersion <= 1)
|
||||
_hBgPal = 0; // No background palette
|
||||
}
|
||||
|
||||
void Background::ChangePalette(SCNHANDLE hPal) {
|
||||
SwapPalette(FindPalette(_hBgPal), hPal);
|
||||
SetBackPal(hPal);
|
||||
}
|
||||
|
||||
void Background::WaitForBG(CORO_PARAM) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
while (_pBG[0] == nullptr) {
|
||||
CORO_SLEEP(1);
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
168
engines/tinsel/background.h
Normal file
168
engines/tinsel/background.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Data structures used for handling backgrounds
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_BACKGND_H // prevent multiple includes
|
||||
#define TINSEL_BACKGND_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/coroutines.h"
|
||||
#include "common/frac.h"
|
||||
#include "common/rect.h"
|
||||
#include "tinsel/anim.h" // for ANIM
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
#include "tinsel/object.h" // for OBJECT *
|
||||
#include "tinsel/palette.h" // palette definitions
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct OBJECT;
|
||||
|
||||
|
||||
/** Scrolling padding. Needed because scroll process does not normally run on every frame */
|
||||
enum {
|
||||
SCROLLX_PAD = 64,
|
||||
SCROLLY_PAD = 64
|
||||
};
|
||||
|
||||
/** When module BLK_INFO list is this long, switch from a binary to linear search */
|
||||
#define LINEAR_SEARCH 5
|
||||
|
||||
/** background playfield structure - a playfield is a container for modules */
|
||||
struct PLAYFIELD {
|
||||
OBJECT *pDispList; ///< object display list for this playfield
|
||||
frac_t fieldX; ///< current world x position of playfield
|
||||
frac_t fieldY; ///< current world y position of playfield
|
||||
frac_t fieldXvel; ///< current x velocity of playfield
|
||||
frac_t fieldYvel; ///< current y velocity of playfield
|
||||
Common::Rect rcClip; ///< clip rectangle for this playfield
|
||||
bool bMoved; ///< set when playfield has moved
|
||||
};
|
||||
|
||||
/** multi-playfield background structure - a backgnd is a container of playfields */
|
||||
struct BACKGND {
|
||||
COLORREF rgbSkyColor; ///< background sky color
|
||||
Common::Point ptInitWorld; ///< initial world position
|
||||
Common::Rect rcScrollLimits; ///< scroll limits
|
||||
int refreshRate; ///< background update process refresh rate
|
||||
frac_t *pXscrollTable; ///< pointer to x direction scroll table for this background
|
||||
frac_t *pYscrollTable; ///< pointer to y direction scroll table for this background
|
||||
Common::Array<PLAYFIELD> fieldArray; ///< list of all playfields for this background
|
||||
bool bAutoErase; ///< when set - screen is cleared before anything is plotted (unused)
|
||||
};
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Background Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
#define MAX_BG 10
|
||||
|
||||
class Font;
|
||||
|
||||
class Background {
|
||||
public:
|
||||
Background(Font* font);
|
||||
|
||||
void InitBackground();
|
||||
|
||||
void DrawBackgnd(); // Draws all playfields for the current background
|
||||
|
||||
/**
|
||||
* Called before scene change.
|
||||
*/
|
||||
void DropBackground();
|
||||
|
||||
void ResetBackground() {
|
||||
_pCurBgnd->fieldArray.clear();
|
||||
delete _pCurBgnd;
|
||||
_pCurBgnd = nullptr;
|
||||
}
|
||||
|
||||
void StartupBackground(CORO_PARAM, SCNHANDLE hFilm);
|
||||
|
||||
void PlayfieldSetPos( // Sets the xy position of the specified playfield in the current background
|
||||
unsigned int which, // which playfield
|
||||
int newXpos, // new x position
|
||||
int newYpos); // new y position
|
||||
|
||||
void PlayfieldGetPos( // Returns the xy position of the specified playfield in the current background
|
||||
unsigned int which, // which playfield
|
||||
int* pXpos, // returns current x position
|
||||
int* pYpos); // returns current y position
|
||||
|
||||
int PlayfieldGetCenterX( // Returns the xy position of the specified playfield in the current background
|
||||
unsigned int which); // which playfield
|
||||
|
||||
OBJECT** GetPlayfieldList( // Returns the display list for the specified playfield
|
||||
unsigned int which); // which playfield
|
||||
|
||||
OBJECT* GetBgObject() { return _pBG[0]; }
|
||||
|
||||
void ChangePalette(SCNHANDLE hPal);
|
||||
|
||||
SCNHANDLE BgPal() { return _hBgPal; }
|
||||
|
||||
void SetDoFadeIn(bool tf) { _bDoFadeIn = tf; }
|
||||
|
||||
bool GetDoFadeIn() { return _bDoFadeIn; }
|
||||
|
||||
/**
|
||||
* Return the current scene handle.
|
||||
*/
|
||||
SCNHANDLE GetBgroundHandle() { return _hBackground; }
|
||||
|
||||
/**
|
||||
* Return the width of the current background.
|
||||
*/
|
||||
int BgWidth();
|
||||
|
||||
/**
|
||||
* Return the height of the current background.
|
||||
*/
|
||||
int BgHeight();
|
||||
|
||||
void SetBackPal(SCNHANDLE hPal);
|
||||
|
||||
int getBgSpeed() { return _BGspeed; }
|
||||
|
||||
void WaitForBG(CORO_PARAM);
|
||||
|
||||
private:
|
||||
Font *_font;
|
||||
|
||||
// current background
|
||||
BACKGND *_pCurBgnd;
|
||||
|
||||
SCNHANDLE _hBgPal; // Background's palette
|
||||
int _BGspeed;
|
||||
SCNHANDLE _hBackground; // Current scene handle - stored in case of Save_Scene()
|
||||
bool _bDoFadeIn;
|
||||
|
||||
public:
|
||||
int _bgReels;
|
||||
OBJECT *_pBG[MAX_BG];
|
||||
ANIM _thisAnim[MAX_BG]; // used by BGmainProcess()
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_BACKGND_H
|
||||
202
engines/tinsel/bg.cpp
Normal file
202
engines/tinsel/bg.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Plays the background film of a scene.
|
||||
*/
|
||||
|
||||
#include "tinsel/anim.h"
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/faders.h"
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/font.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/pcode.h" // CONTROL_STARTOFF
|
||||
#include "tinsel/pid.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/timers.h" // For ONE_SECOND constant
|
||||
#include "tinsel/tinlib.h" // For Control()
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/**
|
||||
* Run main animation that comprises the scene background.
|
||||
*/
|
||||
void BGmainProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
const FILM *pFilm;
|
||||
const FREEL *pReel;
|
||||
const MULTI_INIT *pmi;
|
||||
|
||||
// get the stuff copied to process when it was created
|
||||
if (_vm->_bg->_pBG[0] == NULL) {
|
||||
/*** At start of scene ***/
|
||||
|
||||
if (TinselVersion <= 1) {
|
||||
pReel = (const FREEL *)param;
|
||||
|
||||
// Get the MULTI_INIT structure
|
||||
pmi = pReel->GetMultiInit();
|
||||
|
||||
// Initialize and insert the object, and initialize its script.
|
||||
_vm->_bg->_pBG[0] = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _vm->_bg->_pBG[0]);
|
||||
InitStepAnimScript(&_vm->_bg->_thisAnim[0], _vm->_bg->_pBG[0], FROM_32(pReel->script), _vm->_bg->getBgSpeed());
|
||||
_vm->_bg->_bgReels = 1;
|
||||
} else {
|
||||
/*** At start of scene ***/
|
||||
pFilm = (const FILM *)_vm->_handle->LockMem(_vm->_bg->GetBgroundHandle());
|
||||
_vm->_bg->_bgReels = FROM_32(pFilm->numreels);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < _vm->_bg->_bgReels; i++) {
|
||||
// Get the MULTI_INIT structure
|
||||
pmi = pFilm->reels[i].GetMultiInit();
|
||||
|
||||
// Initialize and insert the object, and initialize its script.
|
||||
_vm->_bg->_pBG[i] = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _vm->_bg->_pBG[i]);
|
||||
MultiSetZPosition(_vm->_bg->_pBG[i], 0);
|
||||
InitStepAnimScript(&_vm->_bg->_thisAnim[i], _vm->_bg->_pBG[i], FROM_32(pFilm->reels[i].script), _vm->_bg->getBgSpeed());
|
||||
|
||||
if (i > 0)
|
||||
_vm->_bg->_pBG[i-1]->pSlave = _vm->_bg->_pBG[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (_vm->_bg->GetDoFadeIn()) {
|
||||
FadeInFast();
|
||||
_vm->_bg->SetDoFadeIn(false);
|
||||
} else if (TinselVersion >= 2)
|
||||
PokeInTagColor();
|
||||
|
||||
for (;;) {
|
||||
for (int i = 0; i < _vm->_bg->_bgReels; i++) {
|
||||
if (StepAnimScript(&_vm->_bg->_thisAnim[i]) == ScriptFinished)
|
||||
error("Background animation has finished");
|
||||
}
|
||||
|
||||
CORO_SLEEP(1);
|
||||
}
|
||||
} else {
|
||||
// New background during scene
|
||||
if (TinselVersion <= 1) {
|
||||
pReel = (const FREEL *)param;
|
||||
InitStepAnimScript(&_vm->_bg->_thisAnim[0], _vm->_bg->_pBG[0], FROM_32(pReel->script), _vm->_bg->getBgSpeed());
|
||||
StepAnimScript(&_vm->_bg->_thisAnim[0]);
|
||||
} else {
|
||||
pFilm = (const FILM *)_vm->_handle->LockMem(_vm->_bg->GetBgroundHandle());
|
||||
assert(_vm->_bg->_bgReels == (int32)FROM_32(pFilm->numreels));
|
||||
|
||||
// Just re-initialize the scripts.
|
||||
for (int i = 0; i < _vm->_bg->_bgReels; i++) {
|
||||
InitStepAnimScript(&_vm->_bg->_thisAnim[i], _vm->_bg->_pBG[i], pFilm->reels[i].script, _vm->_bg->getBgSpeed());
|
||||
StepAnimScript(&_vm->_bg->_thisAnim[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs secondary reels for a scene background
|
||||
*/
|
||||
void BGotherProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
OBJECT *pObj;
|
||||
ANIM anim;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
const FREEL *pReel = (const FREEL *)param;
|
||||
const MULTI_INIT *pmi = pReel->GetMultiInit();
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
// Initialize and insert the object, and initialize its script.
|
||||
_ctx->pObj = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _ctx->pObj);
|
||||
|
||||
InitStepAnimScript(&_ctx->anim, _vm->_bg->_pBG[0], FROM_32(pReel->script), _vm->_bg->getBgSpeed());
|
||||
|
||||
while (StepAnimScript(&_ctx->anim) != ScriptFinished)
|
||||
CORO_SLEEP(1);
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 hFilm Scene background film
|
||||
*/
|
||||
void Background::StartupBackground(CORO_PARAM, SCNHANDLE hFilm) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
|
||||
const FREEL *pfr = &pfilm->reels[0];
|
||||
|
||||
if (TinselVersion != 3) {
|
||||
const MULTI_INIT *pmi = pfr->GetMultiInit();
|
||||
const FRAME *pFrame = pmi->GetFrame();
|
||||
const IMAGE *pim = _vm->_handle->GetImage(READ_32(pFrame));
|
||||
SetBackPal(pim->hImgPal);
|
||||
delete pim;
|
||||
}
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
_hBackground = hFilm; // Save handle in case of Save_Scene()
|
||||
|
||||
// Extract the film speed
|
||||
_BGspeed = ONE_SECOND / FROM_32(pfilm->frate);
|
||||
|
||||
// Start display process for each reel in the film
|
||||
CoroScheduler.createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL));
|
||||
|
||||
if (TinselVersion == 0) {
|
||||
for (uint i = 1; i < FROM_32(pfilm->numreels); ++i)
|
||||
CoroScheduler.createProcess(PID_REEL, BGotherProcess, &pfilm->reels[i], sizeof(FREEL));
|
||||
}
|
||||
|
||||
if (_pBG[0] == NULL)
|
||||
ControlStartOff();
|
||||
|
||||
if ((TinselVersion >= 2) && (coroParam != Common::nullContext))
|
||||
CORO_GIVE_WAY;
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
1475
engines/tinsel/bmv.cpp
Normal file
1475
engines/tinsel/bmv.cpp
Normal file
File diff suppressed because it is too large
Load Diff
185
engines/tinsel/bmv.h
Normal file
185
engines/tinsel/bmv.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Plays films within a scene, takes into account the actor in each 'column'.
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_BMV_H
|
||||
#define TINSEL_BMV_H
|
||||
|
||||
#include "common/coroutines.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/palette.h"
|
||||
|
||||
namespace Audio {
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BMV_OP_DELTA = 0,
|
||||
BMV_OP_RAW = 1,
|
||||
BMV_OP_RUN = 2,
|
||||
BMV_OP_COUNT
|
||||
} BMV_OP;
|
||||
|
||||
class BMVPlayer {
|
||||
|
||||
bool bOldAudio;
|
||||
|
||||
/// Set when a movie is on
|
||||
bool bMovieOn;
|
||||
|
||||
/// Set to kill one off
|
||||
bool bAbort;
|
||||
|
||||
/// For escaping out of movies
|
||||
int bmvEscape;
|
||||
|
||||
/// Movie file pointer
|
||||
Common::File stream;
|
||||
|
||||
/// Movie file name
|
||||
char szMovieFile[14];
|
||||
|
||||
/// Pointers to buffers
|
||||
byte *bigBuffer;
|
||||
|
||||
/// Next data to use to extract a frame
|
||||
int nextUseOffset;
|
||||
|
||||
/// Next data to use to extract sound data
|
||||
int nextSoundOffset;
|
||||
|
||||
/// When above offset gets to what this is set at, rewind
|
||||
int wrapUseOffset;
|
||||
|
||||
/// The offset of the most future packet
|
||||
int mostFutureOffset;
|
||||
|
||||
/// The current frame
|
||||
int currentFrame;
|
||||
int currentSoundFrame;
|
||||
|
||||
/// Number of packets currently in RAM
|
||||
int numAdvancePackets;
|
||||
|
||||
/// Next slot that will be read from disc
|
||||
int nextReadSlot;
|
||||
|
||||
/// Set when the whole file has been read
|
||||
bool bFileEnd;
|
||||
|
||||
/// Palette
|
||||
COLORREF moviePal[256 * 8]; // TinselV1 & V2 need 256, TinselVersion == 3 needs 2048
|
||||
|
||||
int blobsInBuffer;
|
||||
|
||||
struct {
|
||||
OBJECT *pText;
|
||||
int dieFrame;
|
||||
} texts[2];
|
||||
|
||||
COLORREF talkColor;
|
||||
|
||||
/// TinselV3 header fields
|
||||
int slotSize;
|
||||
int frames;
|
||||
int prefetchSlots;
|
||||
int numSlots;
|
||||
int frameRate;
|
||||
int audioMaxSize;
|
||||
int audioBlobSize;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/// TinselV3
|
||||
int frameTime;
|
||||
int bpp;
|
||||
|
||||
int bigProblemCount;
|
||||
|
||||
bool bIsText;
|
||||
|
||||
int movieTick;
|
||||
int startTick;
|
||||
uint32 nextMovieTime;
|
||||
|
||||
uint16 Au_Prev1;
|
||||
uint16 Au_Prev2;
|
||||
byte *ScreenBeg;
|
||||
byte *screenBuffer;
|
||||
|
||||
bool audioStarted;
|
||||
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
Audio::SoundHandle _audioHandle;
|
||||
|
||||
int nextMaintain;
|
||||
public:
|
||||
BMVPlayer();
|
||||
|
||||
void PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape);
|
||||
void FinishBMV();
|
||||
void CopyMovieToScreen();
|
||||
void FettleBMV();
|
||||
|
||||
bool MoviePlaying();
|
||||
|
||||
int32 MovieAudioLag();
|
||||
|
||||
uint32 NextMovieTime();
|
||||
|
||||
void AbortMovie();
|
||||
|
||||
private:
|
||||
void ReadHeader();
|
||||
|
||||
void InitBMV(byte *memoryBuffer);
|
||||
void PrepAudio(const byte *sourceData, int blobCount, byte *destPtr);
|
||||
void PrepBMV(const byte *sourceData, int length, short deltaFetchDisp);
|
||||
void t3DoOperation(BMV_OP op, uint32 len, const byte **src, byte **dst, int32 deltaOffset);
|
||||
void t3PrepBMV(const byte *src, uint32 len, int32 deltaOffset);
|
||||
void MoviePalette(int paletteOffset);
|
||||
void InitializeMovieSound();
|
||||
void StartMovieSound();
|
||||
void FinishMovieSound();
|
||||
void MovieAudio(int audioOffset, int blobs);
|
||||
void FettleMovieText();
|
||||
void BmvDrawText(bool bDraw);
|
||||
void MovieText(CORO_PARAM, int stringId, int x, int y, int fontId, COLORREF *pTalkColor, int duration);
|
||||
int MovieCommand(char cmd, int commandOffset);
|
||||
int FollowingPacket(int thisPacket, bool bReallyImportant);
|
||||
void LoadSlots(int number);
|
||||
void InitializeBMV();
|
||||
bool MaintainBuffer();
|
||||
bool DoBMVFrame();
|
||||
bool DoSoundFrame();
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
324
engines/tinsel/cliprect.cpp
Normal file
324
engines/tinsel/cliprect.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains the clipping rectangle code.
|
||||
*/
|
||||
|
||||
#include "tinsel/cliprect.h" // object clip rect defs
|
||||
#include "tinsel/graphics.h" // normal object drawing
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/palette.h"
|
||||
#include "tinsel/tinsel.h" // for _vm
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/**
|
||||
* Resets the clipping rectangle allocator.
|
||||
*/
|
||||
void ResetClipRect() {
|
||||
_vm->_clipRects.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a clipping rectangle from the free list.
|
||||
* @param pClip clip rectangle dimensions to allocate
|
||||
*/
|
||||
void AddClipRect(const Common::Rect &pClip) {
|
||||
_vm->_clipRects.push_back(pClip);
|
||||
}
|
||||
|
||||
const RectList &GetClipRects() {
|
||||
return _vm->_clipRects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the intersection of two rectangles.
|
||||
* Returns True if there is a intersection.
|
||||
* @param pDest Pointer to destination rectangle that is to receive the intersection
|
||||
* @param pSrc1 Pointer to a source rectangle
|
||||
* @param pSrc2 Pointer to a source rectangle
|
||||
*/
|
||||
bool IntersectRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
|
||||
pDest.left = MAX(pSrc1.left, pSrc2.left);
|
||||
pDest.top = MAX(pSrc1.top, pSrc2.top);
|
||||
pDest.right = MIN(pSrc1.right, pSrc2.right);
|
||||
pDest.bottom = MIN(pSrc1.bottom, pSrc2.bottom);
|
||||
|
||||
return !pDest.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the union of two rectangles.
|
||||
* Returns True if there is a union.
|
||||
* @param pDest destination rectangle that is to receive the new union
|
||||
* @param pSrc1 a source rectangle
|
||||
* @param pSrc2 a source rectangle
|
||||
*/
|
||||
bool UnionRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
|
||||
pDest.left = MIN(pSrc1.left, pSrc2.left);
|
||||
pDest.top = MIN(pSrc1.top, pSrc2.top);
|
||||
pDest.right = MAX(pSrc1.right, pSrc2.right);
|
||||
pDest.bottom = MAX(pSrc1.bottom, pSrc2.bottom);
|
||||
|
||||
return !pDest.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the two rectangles are next to each other.
|
||||
* @param pSrc1 a source rectangle
|
||||
* @param pSrc2 a source rectangle
|
||||
*/
|
||||
static bool LooseIntersectRectangle(const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
|
||||
Common::Rect pDest;
|
||||
|
||||
pDest.left = MAX(pSrc1.left, pSrc2.left);
|
||||
pDest.top = MAX(pSrc1.top, pSrc2.top);
|
||||
pDest.right = MIN(pSrc1.right, pSrc2.right);
|
||||
pDest.bottom = MIN(pSrc1.bottom, pSrc2.bottom);
|
||||
|
||||
return pDest.isValidRect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds velocities and creates clipping rectangles for all the
|
||||
* objects that have moved on the specified object list.
|
||||
* @param pObjList Playfield display list to draw
|
||||
* @param pWin Playfield window top left position
|
||||
* @param pClip Playfield clipping rectangle
|
||||
* @param bNoVelocity When reset, objects pos is updated with velocity
|
||||
* @param bScrolled) When set, playfield has scrolled
|
||||
*/
|
||||
void FindMovingObjects(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip, bool bNoVelocity, bool bScrolled) {
|
||||
OBJECT *pObj; // object list traversal pointer
|
||||
|
||||
for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) {
|
||||
if (!bNoVelocity) {
|
||||
// we want to add velocities to objects position
|
||||
|
||||
if (bScrolled) {
|
||||
// this playfield has scrolled
|
||||
|
||||
// indicate change
|
||||
pObj->flags |= DMA_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pObj->flags & DMA_CHANGED) || // object changed
|
||||
HasPalMoved(pObj->pPal)) { // or palette moved
|
||||
// object has changed in some way
|
||||
|
||||
Common::Rect rcClip; // objects clipped bounding rectangle
|
||||
Common::Rect rcObj; // objects bounding rectangle
|
||||
|
||||
// calc intersection of objects previous bounding rectangle
|
||||
// NOTE: previous position is in screen co-ords
|
||||
if (IntersectRectangle(rcClip, pObj->rcPrev, *pClip)) {
|
||||
// previous position is within clipping rect
|
||||
AddClipRect(rcClip);
|
||||
}
|
||||
|
||||
// calc objects current bounding rectangle
|
||||
if (pObj->flags & DMA_ABS) {
|
||||
// object position is absolute
|
||||
rcObj.left = fracToInt(pObj->xPos);
|
||||
rcObj.top = fracToInt(pObj->yPos);
|
||||
} else {
|
||||
// object position is relative to window
|
||||
rcObj.left = fracToInt(pObj->xPos) - pWin->x;
|
||||
rcObj.top = fracToInt(pObj->yPos) - pWin->y;
|
||||
}
|
||||
rcObj.right = rcObj.left + pObj->width;
|
||||
rcObj.bottom = rcObj.top + pObj->height;
|
||||
|
||||
// calc intersection of object with clipping rect
|
||||
if (IntersectRectangle(rcClip, rcObj, *pClip)) {
|
||||
// current position is within clipping rect
|
||||
AddClipRect(rcClip);
|
||||
|
||||
// update previous position
|
||||
pObj->rcPrev = rcClip;
|
||||
} else {
|
||||
// clear previous position
|
||||
pObj->rcPrev = Common::Rect();
|
||||
}
|
||||
|
||||
// clear changed flag
|
||||
pObj->flags &= ~DMA_CHANGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges any clipping rectangles that overlap to try and reduce
|
||||
* the total number of clip rectangles.
|
||||
*/
|
||||
void MergeClipRect() {
|
||||
RectList &s_rectList = _vm->_clipRects;
|
||||
|
||||
if (s_rectList.size() <= 1)
|
||||
return;
|
||||
|
||||
RectList::iterator rOuter, rInner;
|
||||
|
||||
for (rOuter = s_rectList.begin(); rOuter != s_rectList.end(); ++rOuter) {
|
||||
rInner = rOuter;
|
||||
while (++rInner != s_rectList.end()) {
|
||||
|
||||
if (LooseIntersectRectangle(*rOuter, *rInner)) {
|
||||
// these two rectangles overlap or
|
||||
// are next to each other - merge them
|
||||
|
||||
UnionRectangle(*rOuter, *rOuter, *rInner);
|
||||
|
||||
// remove the inner rect from the list
|
||||
s_rectList.erase(rInner);
|
||||
|
||||
// move back to beginning of list
|
||||
rInner = rOuter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraws single objects within this clipping rectangle.
|
||||
* @param pObjList Object list to draw
|
||||
* @param pWin Window top left position
|
||||
* @param pClip Pointer to clip rectangle
|
||||
*/
|
||||
void UpdateClipRectSingle(OBJECT *pObj, Common::Point *pWin, Common::Rect *pClip) {
|
||||
int x, y, right, bottom; // object corners
|
||||
int hclip, vclip; // total size of object clipping
|
||||
DRAWOBJECT currentObj; // filled in to draw the current object in list
|
||||
|
||||
// Initialize the fields of the drawing object to empty
|
||||
memset(¤tObj, 0, sizeof(DRAWOBJECT));
|
||||
|
||||
if (pObj->flags & DMA_ABS) {
|
||||
// object position is absolute
|
||||
x = fracToInt(pObj->xPos);
|
||||
y = fracToInt(pObj->yPos);
|
||||
} else {
|
||||
// object position is relative to window
|
||||
x = fracToInt(pObj->xPos) - pWin->x;
|
||||
y = fracToInt(pObj->yPos) - pWin->y;
|
||||
}
|
||||
|
||||
// calc object right
|
||||
right = x + pObj->width;
|
||||
if (right < 0)
|
||||
// totally clipped if negative
|
||||
return;
|
||||
|
||||
// calc object bottom
|
||||
bottom = y + pObj->height;
|
||||
if (bottom < 0)
|
||||
// totally clipped if negative
|
||||
return;
|
||||
|
||||
// bottom clip = low right y - clip low right y
|
||||
currentObj.botClip = bottom - pClip->bottom;
|
||||
if (currentObj.botClip < 0) {
|
||||
// negative - object is not clipped
|
||||
currentObj.botClip = 0;
|
||||
}
|
||||
|
||||
// right clip = low right x - clip low right x
|
||||
currentObj.rightClip = right - pClip->right;
|
||||
if (currentObj.rightClip < 0) {
|
||||
// negative - object is not clipped
|
||||
currentObj.rightClip = 0;
|
||||
}
|
||||
|
||||
// top clip = clip top left y - top left y
|
||||
currentObj.topClip = pClip->top - y;
|
||||
if (currentObj.topClip < 0) {
|
||||
// negative - object is not clipped
|
||||
currentObj.topClip = 0;
|
||||
} else { // clipped - adjust start position to top of clip rect
|
||||
y = pClip->top;
|
||||
}
|
||||
|
||||
// left clip = clip top left x - top left x
|
||||
currentObj.leftClip = pClip->left - x;
|
||||
if (currentObj.leftClip < 0) {
|
||||
// negative - object is not clipped
|
||||
currentObj.leftClip = 0;
|
||||
} else {
|
||||
// NOTE: This else statement is disabled in tinsel v1
|
||||
// clipped - adjust start position to left of clip rect
|
||||
x = pClip->left;
|
||||
}
|
||||
|
||||
// calc object total horizontal clipping
|
||||
hclip = currentObj.leftClip + currentObj.rightClip;
|
||||
|
||||
// calc object total vertical clipping
|
||||
vclip = currentObj.topClip + currentObj.botClip;
|
||||
|
||||
if (hclip + vclip != 0) {
|
||||
// object is clipped in some way
|
||||
|
||||
if (pObj->width <= hclip)
|
||||
// object totally clipped horizontally - ignore
|
||||
return;
|
||||
|
||||
if (pObj->height <= vclip)
|
||||
// object totally clipped vertically - ignore
|
||||
return;
|
||||
|
||||
// set clip bit in object flags
|
||||
currentObj.flags = pObj->flags | DMA_CLIP;
|
||||
} else { // object is not clipped - copy flags
|
||||
currentObj.flags = pObj->flags;
|
||||
}
|
||||
|
||||
// copy object properties to local object
|
||||
currentObj.width = pObj->width;
|
||||
currentObj.height = pObj->height;
|
||||
currentObj.xPos = (short)x;
|
||||
currentObj.yPos = (short)y;
|
||||
if (TinselVersion != 3) {
|
||||
currentObj.pPal = pObj->pPal;
|
||||
} else {
|
||||
currentObj.isRLE = pObj->isRLE;
|
||||
currentObj.colorFlags = pObj->colorFlags;
|
||||
}
|
||||
currentObj.constant = pObj->constant;
|
||||
currentObj.hBits = pObj->hBits;
|
||||
|
||||
// draw the object
|
||||
DrawObject(¤tObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraws all objects within this clipping rectangle.
|
||||
* @param pObjList Object list to draw
|
||||
* @param pWin Window top left position
|
||||
* @param pClip Pointer to clip rectangle
|
||||
*/
|
||||
void UpdateClipRect(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip) {
|
||||
OBJECT *pObj; // object list iterator
|
||||
|
||||
for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) {
|
||||
UpdateClipRectSingle(pObj, pWin, pClip);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
77
engines/tinsel/cliprect.h
Normal file
77
engines/tinsel/cliprect.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Clipping rectangle defines
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_CLIPRECT_H // prevent multiple includes
|
||||
#define TINSEL_CLIPRECT_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct OBJECT;
|
||||
|
||||
typedef Common::List<Common::Rect> RectList;
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Clip Rect Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
void ResetClipRect(); // Resets the clipping rectangle allocator
|
||||
|
||||
void AddClipRect( // Allocate a clipping rectangle from the free list
|
||||
const Common::Rect &pClip); // clip rectangle dimensions to allocate
|
||||
|
||||
const RectList &GetClipRects();
|
||||
|
||||
bool IntersectRectangle( // Creates the intersection of two rectangles
|
||||
Common::Rect &pDest, // pointer to destination rectangle that is to receive the intersection
|
||||
const Common::Rect &pSrc1, // pointer to a source rectangle
|
||||
const Common::Rect &pSrc2); // pointer to a source rectangle
|
||||
|
||||
bool UnionRectangle( // Creates the union of two rectangles
|
||||
Common::Rect &pDest, // destination rectangle that is to receive the new union
|
||||
const Common::Rect &pSrc1, // a source rectangle
|
||||
const Common::Rect &pSrc2); // a source rectangle
|
||||
|
||||
void FindMovingObjects( // Creates clipping rectangles for all the objects that have moved on the specified object list
|
||||
OBJECT **pObjList, // playfield display list to draw
|
||||
Common::Point *pWin, // playfield window top left position
|
||||
Common::Rect *pClip, // playfield clipping rectangle
|
||||
bool bVelocity, // when set, objects pos is updated with velocity
|
||||
bool bScrolled); // when set, playfield has scrolled
|
||||
|
||||
void MergeClipRect(); // Merges any clipping rectangles that overlap
|
||||
|
||||
void UpdateClipRectSingle( // Redraws single object within this clipping rectangle
|
||||
OBJECT *pObj, // object to draw
|
||||
Common::Point *pWin, // window top left position
|
||||
Common::Rect *pClip); // pointer to clip rectangle
|
||||
|
||||
void UpdateClipRect( // Redraws all objects within this clipping rectangle
|
||||
OBJECT **pObjList, // object list to draw
|
||||
Common::Point *pWin, // window top left position
|
||||
Common::Rect *pClip); // pointer to clip rectangle
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_CLIPRECT_H
|
||||
181
engines/tinsel/config.cpp
Normal file
181
engines/tinsel/config.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains configuration functionality
|
||||
*/
|
||||
|
||||
//#define USE_3FLAGS 1
|
||||
|
||||
#include "tinsel/config.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/sound.h"
|
||||
#include "tinsel/music.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
Config::Config(TinselEngine *vm) : _vm(vm) {
|
||||
_dclickSpeed = DOUBLE_CLICK_TIME;
|
||||
_musicVolume = Audio::Mixer::kMaxChannelVolume;
|
||||
_soundVolume = Audio::Mixer::kMaxChannelVolume;
|
||||
_voiceVolume = Audio::Mixer::kMaxChannelVolume;
|
||||
_textSpeed = DEFTEXTSPEED;
|
||||
_useSubtitles = false;
|
||||
_swapButtons = false;
|
||||
_language = TXT_ENGLISH;
|
||||
_isAmericanEnglishVersion = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write settings to config manager and flush the config file to disk.
|
||||
*/
|
||||
void Config::writeToDisk() {
|
||||
ConfMan.setInt("dclick_speed", _dclickSpeed);
|
||||
ConfMan.setInt("music_volume", _musicVolume);
|
||||
ConfMan.setInt("sfx_volume", _soundVolume);
|
||||
ConfMan.setInt("speech_volume", _voiceVolume);
|
||||
ConfMan.setInt("talkspeed", (_textSpeed * 255) / 100);
|
||||
ConfMan.setBool("subtitles", _useSubtitles);
|
||||
//ConfMan.setBool("swap_buttons", _swapButtons ? 1 : 0);
|
||||
|
||||
// Store language for multilingual versions
|
||||
if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
|
||||
Common::Language lang;
|
||||
switch (_language) {
|
||||
case TXT_FRENCH:
|
||||
lang = Common::FR_FRA;
|
||||
break;
|
||||
case TXT_GERMAN:
|
||||
lang = Common::DE_DEU;
|
||||
break;
|
||||
case TXT_SPANISH:
|
||||
lang = Common::ES_ESP;
|
||||
break;
|
||||
case TXT_ITALIAN:
|
||||
lang = Common::IT_ITA;
|
||||
break;
|
||||
case TXT_US:
|
||||
lang = Common::EN_USA;
|
||||
break;
|
||||
case TXT_JAPANESE:
|
||||
lang = Common::JA_JPN;
|
||||
break;
|
||||
default:
|
||||
lang = Common::EN_ANY;
|
||||
}
|
||||
|
||||
ConfMan.set("language", Common::getLanguageCode(lang));
|
||||
}
|
||||
|
||||
// Write to disk
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read configuration settings from the config file into memory
|
||||
*/
|
||||
void Config::readFromDisk() {
|
||||
if (ConfMan.hasKey("dclick_speed"))
|
||||
_dclickSpeed = ConfMan.getInt("dclick_speed");
|
||||
|
||||
// HACK/FIXME:
|
||||
// We need to clip the volumes from [0, 256] to [0, 255]
|
||||
// here, since for example Tinsel's internal options dialog
|
||||
// and also the midi playback code rely on the volumes to be
|
||||
// in [0, 255]
|
||||
_musicVolume = CLIP(ConfMan.getInt("music_volume"), 0, 255);
|
||||
_soundVolume = CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
|
||||
_voiceVolume = CLIP(ConfMan.getInt("speech_volume"), 0, 255);
|
||||
|
||||
if (ConfMan.hasKey("talkspeed"))
|
||||
_textSpeed = (ConfMan.getInt("talkspeed") * 100) / 255;
|
||||
if (ConfMan.hasKey("subtitles"))
|
||||
_useSubtitles = ConfMan.getBool("subtitles");
|
||||
|
||||
// FIXME: If JAPAN is set, subtitles are forced OFF in the original engine
|
||||
|
||||
//_swapButtons = ConfMan.getBool("swap_buttons") == 1 ? true : false;
|
||||
//ConfigData.language = language; // not necessary, as language has been set in the launcher
|
||||
//ConfigData._isAmericanEnglishVersion = _isAmericanEnglishVersion; // EN_USA / EN_GRB
|
||||
|
||||
// Set language - we'll be clever here and use the ScummVM language setting
|
||||
_language = TXT_ENGLISH;
|
||||
Common::Language lang = _vm->getLanguage();
|
||||
if (lang == Common::UNK_LANG && ConfMan.hasKey("language"))
|
||||
lang = Common::parseLanguage(ConfMan.get("language")); // For multi-lingual versions, fall back to user settings
|
||||
switch (lang) {
|
||||
case Common::FR_FRA:
|
||||
_language = TXT_FRENCH;
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
_language = TXT_GERMAN;
|
||||
break;
|
||||
case Common::ES_ESP:
|
||||
_language = TXT_SPANISH;
|
||||
break;
|
||||
case Common::IT_ITA:
|
||||
_language = TXT_ITALIAN;
|
||||
break;
|
||||
case Common::EN_USA:
|
||||
_language = TXT_US;
|
||||
break;
|
||||
case Common::JA_JPN:
|
||||
_language = TXT_JAPANESE;
|
||||
break;
|
||||
default:
|
||||
_language = TXT_ENGLISH;
|
||||
}
|
||||
|
||||
if (lang == Common::JA_JPN) {
|
||||
// TODO: Add support for JAPAN version
|
||||
} else if (lang == Common::HE_ISR) {
|
||||
// TODO: Add support for HEBREW version
|
||||
|
||||
// The Hebrew version appears to the software as being English
|
||||
// but it needs to have subtitles on...
|
||||
_language = TXT_ENGLISH;
|
||||
_useSubtitles = true;
|
||||
} else if (_vm->getFeatures() & GF_USE_3FLAGS) {
|
||||
// 3 FLAGS version supports French, German, Spanish
|
||||
// Fall back to German if necessary
|
||||
if (_language != TXT_FRENCH && _language != TXT_GERMAN && _language != TXT_SPANISH) {
|
||||
_language = TXT_GERMAN;
|
||||
_useSubtitles = true;
|
||||
}
|
||||
} else if (_vm->getFeatures() & GF_USE_4FLAGS) {
|
||||
// 4 FLAGS version supports French, German, Spanish, Italian
|
||||
// Fall back to German if necessary
|
||||
if (_language != TXT_FRENCH && _language != TXT_GERMAN &&
|
||||
_language != TXT_SPANISH && _language != TXT_ITALIAN) {
|
||||
_language = TXT_GERMAN;
|
||||
_useSubtitles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::isJapanMode() {
|
||||
return _language == TXT_JAPANESE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
62
engines/tinsel/config.h
Normal file
62
engines/tinsel/config.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_CONFIG_H
|
||||
#define TINSEL_CONFIG_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// double click timer initial value
|
||||
enum {
|
||||
DOUBLE_CLICK_TIME = 10, // 10 @ 18Hz = .55 sec
|
||||
DEFTEXTSPEED = 0
|
||||
};
|
||||
|
||||
class TinselEngine;
|
||||
|
||||
class Config {
|
||||
private:
|
||||
TinselEngine *_vm;
|
||||
|
||||
public:
|
||||
int _dclickSpeed;
|
||||
int _musicVolume;
|
||||
int _soundVolume;
|
||||
int _voiceVolume;
|
||||
int _textSpeed;
|
||||
int _useSubtitles;
|
||||
int _swapButtons;
|
||||
LANGUAGE _language;
|
||||
int _isAmericanEnglishVersion;
|
||||
|
||||
public:
|
||||
Config(TinselEngine *vm);
|
||||
|
||||
void writeToDisk();
|
||||
void readFromDisk();
|
||||
bool isJapanMode();
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
3
engines/tinsel/configure.engine
Normal file
3
engines/tinsel/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine tinsel "Tinsel" yes "" "" "" "midi tinygl"
|
||||
9
engines/tinsel/credits.pl
Normal file
9
engines/tinsel/credits.pl
Normal file
@@ -0,0 +1,9 @@
|
||||
begin_section("Tinsel");
|
||||
add_person("Torbjörn Andersson", "eriktorbjorn", "");
|
||||
add_person("Fabio Battaglia", "Hkz", "PSX version support");
|
||||
add_person("Paul Gilbert", "dreammaster", "");
|
||||
add_person("Sven Hesse", "DrMcCoy", "");
|
||||
add_person("Max Horn", "Fingolfin", "(retired)");
|
||||
add_person("Filippos Karapetis", "bluegr", "");
|
||||
add_person("Joost Peters", "joostp", "");
|
||||
end_section();
|
||||
630
engines/tinsel/cursor.cpp
Normal file
630
engines/tinsel/cursor.cpp
Normal file
@@ -0,0 +1,630 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Cursor and cursor trails.
|
||||
*/
|
||||
|
||||
#include "tinsel/cursor.h"
|
||||
|
||||
#include "tinsel/anim.h"
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/events.h" // For EventsManager class
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/graphics.h"
|
||||
#include "tinsel/handle.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
|
||||
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
#define ITERATION_BASE FRAC_ONE
|
||||
#define ITER_ACCELERATION (10L << (FRAC_BITS - 4))
|
||||
|
||||
Cursor::Cursor() {
|
||||
_mainCursor = nullptr;
|
||||
_auxCursor = nullptr;
|
||||
|
||||
_mainCursorAnim = {0, 0, 0, 0, 0};
|
||||
_auxCursorAnim = {0, 0, 0, 0, 0};
|
||||
|
||||
_hiddenCursor = false;
|
||||
_hiddenTrails = false;
|
||||
_tempHiddenCursor = false;
|
||||
_frozenCursor = false;
|
||||
|
||||
_iterationSize = 0;
|
||||
|
||||
_cursorFilm = 0;
|
||||
|
||||
_numTrails = 0;
|
||||
_nextTrail = 0;
|
||||
|
||||
_cursorProcessesStopped = false;
|
||||
_cursorProcessesRestarted = false;
|
||||
|
||||
_auxCursorOffsetX = 0;
|
||||
_auxCursorOffsetY = 0;
|
||||
|
||||
_lastCursorX = 0;
|
||||
_lastCursorY = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and insert a cursor trail object, set its Z-pos, and hide
|
||||
* it. Also initialize its animation script.
|
||||
*/
|
||||
void Cursor::InitCurTrailObj(int i, int x, int y) {
|
||||
if (!_numTrails)
|
||||
return;
|
||||
|
||||
const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
|
||||
const FREEL *pfr = (const FREEL *)&pFilm->reels[i + 1];
|
||||
const MULTI_INIT *pmi = pfr->GetMultiInit();
|
||||
|
||||
PokeInPalette(pmi);
|
||||
|
||||
// Get rid of old object
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
|
||||
|
||||
// Initialize and insert the object, set its Z-pos, and hide it
|
||||
_trailData[i].trailObj = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
|
||||
MultiSetAniXYZ(_trailData[i].trailObj, x, y, Z_CURSORTRAIL);
|
||||
|
||||
// Initialize the animation script
|
||||
InitStepAnimScript(&_trailData[i].trailAnim, _trailData[i].trailObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate));
|
||||
StepAnimScript(&_trailData[i].trailAnim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cursor position from the mouse driver.
|
||||
*/
|
||||
bool Cursor::GetDriverPosition(int *x, int *y) {
|
||||
Common::Point ptMouse = _vm->getMousePosition();
|
||||
*x = ptMouse.x;
|
||||
*y = ptMouse.y;
|
||||
|
||||
return(*x >= 0 && *x <= SCREEN_WIDTH - 1 &&
|
||||
*y >= 0 && *y <= SCREEN_HEIGHT - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor relative to current position.
|
||||
*/
|
||||
void Cursor::AdjustCursorXY(int deltaX, int deltaY) {
|
||||
int x, y;
|
||||
|
||||
if (deltaX || deltaY) {
|
||||
if (GetDriverPosition(&x, &y))
|
||||
_vm->setMousePosition(Common::Point(x + deltaX, y + deltaY));
|
||||
}
|
||||
DoCursorMove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to an absolute position.
|
||||
*/
|
||||
void Cursor::SetCursorXY(int newx, int newy) {
|
||||
int x, y;
|
||||
int Loffset, Toffset; // Screen offset
|
||||
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
newx -= Loffset;
|
||||
newy -= Toffset;
|
||||
|
||||
if (GetDriverPosition(&x, &y))
|
||||
_vm->setMousePosition(Common::Point(newx, newy));
|
||||
DoCursorMove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to a screen position.
|
||||
*/
|
||||
void Cursor::SetCursorScreenXY(int newx, int newy) {
|
||||
int x, y;
|
||||
|
||||
if (GetDriverPosition(&x, &y))
|
||||
_vm->setMousePosition(Common::Point(newx, newy));
|
||||
DoCursorMove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the world and his brother.
|
||||
* Returns the cursor's animation position in (x,y).
|
||||
* Returns false if there is no cursor object.
|
||||
*/
|
||||
bool Cursor::GetCursorXYNoWait(int *x, int *y, bool absolute) {
|
||||
if (_mainCursor == NULL) {
|
||||
*x = *y = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
GetAniPosition(_mainCursor, x, y);
|
||||
|
||||
if (absolute) {
|
||||
int Loffset, Toffset; // Screen offset
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
*x += Loffset;
|
||||
*y += Toffset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the world and his brother.
|
||||
* Returns the cursor's animation position.
|
||||
* If called while there is no cursor object, the calling process ends
|
||||
* up waiting until there is.
|
||||
*/
|
||||
void Cursor::GetCursorXY(int *x, int *y, bool absolute) {
|
||||
//while (McurObj == NULL)
|
||||
// ProcessSleepSelf();
|
||||
assert(_mainCursor);
|
||||
GetCursorXYNoWait(x, y, absolute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-initialize the main cursor to use the main cursor reel.
|
||||
* Called from TINLIB.C to restore cursor after hiding it.
|
||||
* Called from INVENTRY.C to restore cursor after customising it.
|
||||
*/
|
||||
void Cursor::RestoreMainCursor() {
|
||||
const FILM *pfilm;
|
||||
|
||||
if (_mainCursor != NULL) {
|
||||
pfilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
|
||||
|
||||
InitStepAnimScript(&_mainCursorAnim, _mainCursor, FROM_32(pfilm->reels->script), ONE_SECOND / FROM_32(pfilm->frate));
|
||||
StepAnimScript(&_mainCursorAnim);
|
||||
}
|
||||
_hiddenCursor = false;
|
||||
_frozenCursor = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from INVENTRY.C to customise the main cursor.
|
||||
*/
|
||||
void Cursor::SetTempCursor(SCNHANDLE pScript) {
|
||||
if (_mainCursor != NULL)
|
||||
InitStepAnimScript(&_mainCursorAnim, _mainCursor, pScript, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the cursor.
|
||||
*/
|
||||
void Cursor::DwHideCursor() {
|
||||
_hiddenCursor = true;
|
||||
|
||||
if (_mainCursor)
|
||||
MultiHideObject(_mainCursor);
|
||||
if (_auxCursor)
|
||||
MultiHideObject(_auxCursor);
|
||||
|
||||
for (int i = 0; i < _numTrails; i++) {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
|
||||
}
|
||||
}
|
||||
|
||||
void Cursor::HideCursorProcess() {
|
||||
if (_mainCursor)
|
||||
MultiHideObject(_mainCursor);
|
||||
if (_auxCursor)
|
||||
MultiHideObject(_auxCursor);
|
||||
|
||||
for (int i = 0; i < _numTrails; i++) {
|
||||
if (_trailData[i].trailObj != NULL)
|
||||
MultiHideObject(_trailData[i].trailObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhide the cursor.
|
||||
*/
|
||||
void Cursor::UnHideCursor() {
|
||||
_hiddenCursor = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze the cursor.
|
||||
*/
|
||||
void Cursor::FreezeCursor() {
|
||||
_frozenCursor = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze the cursor, or not.
|
||||
*/
|
||||
void Cursor::DoFreezeCursor(bool bFreeze) {
|
||||
_frozenCursor = bFreeze;
|
||||
}
|
||||
|
||||
/**
|
||||
* HideCursorTrails
|
||||
*/
|
||||
void Cursor::HideCursorTrails() {
|
||||
int i;
|
||||
|
||||
_hiddenTrails = true;
|
||||
|
||||
for (i = 0; i < _numTrails; i++) {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UnHideCursorTrails
|
||||
*/
|
||||
void Cursor::UnHideCursorTrails() {
|
||||
_hiddenTrails = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete auxiliary cursor. Restore animation offsets in the image.
|
||||
*/
|
||||
void Cursor::DelAuxCursor() {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_auxCursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set auxiliary cursor.
|
||||
* Save animation offsets from the image if required.
|
||||
*/
|
||||
void Cursor::SetAuxCursor(SCNHANDLE hFilm) {
|
||||
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
|
||||
const FREEL *pfr = &pfilm->reels[0];
|
||||
const MULTI_INIT *pmi = pfr->GetMultiInit();
|
||||
const FRAME *pFrame = pmi->GetFrame();
|
||||
const IMAGE *pim;
|
||||
int x, y; // Cursor position
|
||||
|
||||
DelAuxCursor(); // Get rid of previous
|
||||
|
||||
// Noir does not use palettes
|
||||
if (TinselVersion < 3) {
|
||||
// WORKAROUND: There's no palette when loading a DW1 savegame with a held item, so exit if so
|
||||
if (!_vm->_bg->BgPal())
|
||||
return;
|
||||
|
||||
assert(_vm->_bg->BgPal()); // no background palette
|
||||
PokeInPalette(pmi);
|
||||
}
|
||||
|
||||
GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear
|
||||
|
||||
pim = _vm->_handle->GetImage(READ_32(pFrame)); // Get pointer to auxiliary cursor's image
|
||||
|
||||
_auxCursorOffsetX = (short)(pim->imgWidth / 2 - ((int16) pim->anioffX));
|
||||
_auxCursorOffsetY = (short)((pim->imgHeight & ~C16_FLAG_MASK) / 2 -
|
||||
((int16) pim->anioffY));
|
||||
delete pim;
|
||||
|
||||
// Initialize and insert the auxiliary cursor object
|
||||
_auxCursor = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _auxCursor);
|
||||
|
||||
// Initialize the animation and set its position
|
||||
InitStepAnimScript(&_auxCursorAnim, _auxCursor, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
|
||||
MultiSetAniXYZ(_auxCursor, x - _auxCursorOffsetX, y - _auxCursorOffsetY, Z_ACURSOR);
|
||||
|
||||
if (_hiddenCursor)
|
||||
MultiHideObject(_auxCursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* MoveCursor
|
||||
*/
|
||||
void Cursor::DoCursorMove() {
|
||||
int startX, startY;
|
||||
Common::Point ptMouse;
|
||||
frac_t newX, newY;
|
||||
unsigned dir;
|
||||
|
||||
// get cursors start animation position
|
||||
GetCursorXYNoWait(&startX, &startY, false);
|
||||
|
||||
// get mouse drivers current position
|
||||
ptMouse = _vm->getMousePosition();
|
||||
|
||||
// convert to fixed point
|
||||
newX = intToFrac(ptMouse.x);
|
||||
newY = intToFrac(ptMouse.y);
|
||||
|
||||
// modify mouse driver position depending on cursor keys
|
||||
dir = _vm->getKeyDirection();
|
||||
if (dir != 0) {
|
||||
if (dir & MSK_LEFT)
|
||||
newX -= _iterationSize;
|
||||
|
||||
if (dir & MSK_RIGHT)
|
||||
newX += _iterationSize;
|
||||
|
||||
if (dir & MSK_UP)
|
||||
newY -= _iterationSize;
|
||||
|
||||
if (dir & MSK_DOWN)
|
||||
newY += _iterationSize;
|
||||
|
||||
_iterationSize += ITER_ACCELERATION;
|
||||
|
||||
// set new mouse driver position
|
||||
_vm->setMousePosition(Common::Point(fracToInt(newX), fracToInt(newY)));
|
||||
} else
|
||||
|
||||
_iterationSize = ITERATION_BASE;
|
||||
|
||||
// get new mouse driver position - could have been modified
|
||||
ptMouse = _vm->getMousePosition();
|
||||
|
||||
if (_lastCursorX != ptMouse.x || _lastCursorY != ptMouse.y) {
|
||||
resetUserEventTime();
|
||||
|
||||
if (!_hiddenTrails && !_hiddenCursor) {
|
||||
InitCurTrailObj(_nextTrail++, _lastCursorX, _lastCursorY);
|
||||
if (_nextTrail == _numTrails)
|
||||
_nextTrail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust cursor to new mouse position
|
||||
if (_mainCursor)
|
||||
MultiSetAniXY(_mainCursor, ptMouse.x, ptMouse.y);
|
||||
if (_auxCursor != NULL)
|
||||
MultiSetAniXY(_auxCursor, ptMouse.x - _auxCursorOffsetX, ptMouse.y - _auxCursorOffsetY);
|
||||
|
||||
if (_vm->_dialogs->inventoryActive() && _mainCursor) {
|
||||
// Notify the inventory
|
||||
_vm->_dialogs->xMovement(ptMouse.x - startX);
|
||||
_vm->_dialogs->yMovement(ptMouse.y - startY);
|
||||
}
|
||||
|
||||
_lastCursorX = ptMouse.x;
|
||||
_lastCursorY = ptMouse.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize cursor object.
|
||||
*/
|
||||
void Cursor::InitCurObj() {
|
||||
const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
|
||||
const FREEL *pfr = (const FREEL *)&pFilm->reels[0];
|
||||
const MULTI_INIT *pmi = pfr->GetMultiInit();
|
||||
|
||||
if (TinselVersion != 3) {
|
||||
PokeInPalette(pmi);
|
||||
}
|
||||
|
||||
if (TinselVersion <= 1)
|
||||
_auxCursor = nullptr; // No auxiliary cursor
|
||||
|
||||
_mainCursor = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _mainCursor);
|
||||
|
||||
InitStepAnimScript(&_mainCursorAnim, _mainCursor, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the cursor position.
|
||||
*/
|
||||
void Cursor::InitCurPos() {
|
||||
Common::Point ptMouse = _vm->getMousePosition();
|
||||
_lastCursorX = ptMouse.x;
|
||||
_lastCursorY = ptMouse.y;
|
||||
|
||||
MultiSetZPosition(_mainCursor, Z_CURSOR);
|
||||
DoCursorMove();
|
||||
MultiHideObject(_mainCursor);
|
||||
|
||||
_iterationSize = ITERATION_BASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from dec_cursor() Glitter function.
|
||||
* Register the handle to cursor reel data.
|
||||
*/
|
||||
void Cursor::DwInitCursor(SCNHANDLE bfilm) {
|
||||
const FILM *pfilm;
|
||||
|
||||
_cursorFilm = bfilm;
|
||||
|
||||
pfilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
|
||||
_numTrails = FROM_32(pfilm->numreels) - 1;
|
||||
|
||||
assert(_numTrails <= MAX_TRAILERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* DropCursor is called when a scene is closing down.
|
||||
*/
|
||||
void Cursor::DropCursor() {
|
||||
if (TinselVersion >= 2) {
|
||||
if (_auxCursor)
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_auxCursor);
|
||||
if (_mainCursor)
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_mainCursor);
|
||||
|
||||
_cursorProcessesRestarted = false;
|
||||
}
|
||||
|
||||
_auxCursor = nullptr; // No auxiliary cursor
|
||||
_mainCursor = nullptr; // No cursor object (imminently deleted elsewhere)
|
||||
_hiddenCursor = false; // Not hidden in next scene
|
||||
_hiddenTrails = false; // Trailers not hidden in next scene
|
||||
_cursorProcessesStopped = true; // Suspend cursor processes
|
||||
|
||||
for (int i = 0; i < _numTrails; i++) {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RestartCursor is called when a new scene is starting up.
|
||||
*/
|
||||
void Cursor::RestartCursor() {
|
||||
_cursorProcessesRestarted = true; // Get the main cursor to re-initialize
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when restarting the game, ensures correct re-start with NULL
|
||||
* pointers etc.
|
||||
*/
|
||||
void Cursor::RebootCursor() {
|
||||
_mainCursor = _auxCursor = nullptr;
|
||||
for (int i = 0; i < MAX_TRAILERS; i++)
|
||||
_trailData[i].trailObj = nullptr;
|
||||
|
||||
_hiddenCursor = _hiddenTrails = _frozenCursor = false;
|
||||
|
||||
_cursorFilm = 0;
|
||||
|
||||
_cursorProcessesStopped = false;
|
||||
_cursorProcessesRestarted = false;
|
||||
}
|
||||
|
||||
void Cursor::StartCursorFollowed() {
|
||||
DelAuxCursor();
|
||||
|
||||
if (!SysVar(SV_ENABLEPRINTCURSOR))
|
||||
_tempHiddenCursor = true;
|
||||
}
|
||||
|
||||
void Cursor::EndCursorFollowed() {
|
||||
_vm->_dialogs->inventoryIconCursor(false); // May be holding something
|
||||
_tempHiddenCursor = false;
|
||||
}
|
||||
|
||||
bool Cursor::isCursorShown() {
|
||||
return !(_tempHiddenCursor || _hiddenCursor);
|
||||
}
|
||||
|
||||
void Cursor::AnimateProcess() {
|
||||
// Step the animation script(s)
|
||||
StepAnimScript(&_mainCursorAnim);
|
||||
if (_auxCursor != NULL)
|
||||
StepAnimScript(&_auxCursorAnim);
|
||||
for (int i = 0; i < _vm->_cursor->NumTrails(); i++) {
|
||||
if (_trailData[i].trailObj != NULL) {
|
||||
if (StepAnimScript(&_trailData[i].trailAnim) == ScriptFinished) {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the cursor as appropriate
|
||||
if (!_vm->_cursor->CursorIsFrozen())
|
||||
_vm->_cursor->DoCursorMove();
|
||||
}
|
||||
|
||||
/**
|
||||
* CursorStoppedCheck
|
||||
*/
|
||||
void CursorStoppedCheck(CORO_PARAM) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
// If scene is closing down
|
||||
if (_vm->_cursor->_cursorProcessesStopped) {
|
||||
// ...wait for next scene start-up
|
||||
while (!_vm->_cursor->_cursorProcessesRestarted)
|
||||
CORO_SLEEP(1);
|
||||
|
||||
// Re-initialize
|
||||
_vm->_cursor->InitCurObj();
|
||||
_vm->_cursor->InitCurPos();
|
||||
_vm->_dialogs->inventoryIconCursor(false); // May be holding something
|
||||
|
||||
// Re-start the cursor trails
|
||||
_vm->_cursor->_cursorProcessesRestarted = true;
|
||||
_vm->_cursor->_cursorProcessesStopped = false;
|
||||
}
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
bool CanInitializeCursor() {
|
||||
if (!_vm->_cursor->HasReelData()) {
|
||||
return false;
|
||||
} else if (TinselVersion != 3) {
|
||||
return (_vm->_bg->BgPal() != 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main cursor process.
|
||||
*/
|
||||
void CursorProcess(CORO_PARAM, const void *) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
while (!CanInitializeCursor())
|
||||
CORO_SLEEP(1);
|
||||
|
||||
_vm->_cursor->InitCurObj();
|
||||
_vm->_cursor->InitCurPos();
|
||||
_vm->_dialogs->inventoryIconCursor(false); // May be holding something
|
||||
|
||||
_vm->_cursor->_cursorProcessesStopped = false;
|
||||
_vm->_cursor->_cursorProcessesRestarted = false;
|
||||
|
||||
while (1) {
|
||||
// allow rescheduling
|
||||
CORO_SLEEP(1);
|
||||
|
||||
// Stop/start between scenes
|
||||
CORO_INVOKE_0(CursorStoppedCheck);
|
||||
|
||||
_vm->_cursor->AnimateProcess();
|
||||
|
||||
// If the cursor should be hidden...
|
||||
if (_vm->_cursor->ShouldBeHidden()) {
|
||||
_vm->_cursor->HideCursorProcess();
|
||||
|
||||
// Wait 'til cursor is again required.
|
||||
while (_vm->_cursor->IsHidden()) {
|
||||
CORO_SLEEP(1);
|
||||
|
||||
// Stop/start between scenes
|
||||
CORO_INVOKE_0(CursorStoppedCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
124
engines/tinsel/cursor.h
Normal file
124
engines/tinsel/cursor.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Clipping rectangle defines
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_CURSOR_H // prevent multiple includes
|
||||
#define TINSEL_CURSOR_H
|
||||
|
||||
#include "common/frac.h"
|
||||
#include "tinsel/anim.h"
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct IMAGE;
|
||||
struct FREEL;
|
||||
struct MULTI_INIT;
|
||||
struct FILM;
|
||||
struct OBJECT;
|
||||
|
||||
#define MAX_TRAILERS 10
|
||||
|
||||
class Cursor {
|
||||
public:
|
||||
Cursor();
|
||||
virtual ~Cursor() {}
|
||||
void AdjustCursorXY(int deltaX, int deltaY);
|
||||
void SetCursorXY(int x, int y);
|
||||
void SetCursorScreenXY(int newx, int newy);
|
||||
void GetCursorXY(int *x, int *y, bool absolute);
|
||||
bool GetCursorXYNoWait(int *x, int *y, bool absolute);
|
||||
bool isCursorShown();
|
||||
|
||||
void RestoreMainCursor();
|
||||
void SetTempCursor(SCNHANDLE pScript);
|
||||
void DwHideCursor();
|
||||
void UnHideCursor();
|
||||
void HideCursorProcess();
|
||||
void AnimateProcess();
|
||||
void FreezeCursor();
|
||||
void DoFreezeCursor(bool bFreeze);
|
||||
void HideCursorTrails();
|
||||
void UnHideCursorTrails();
|
||||
void DelAuxCursor();
|
||||
void SetAuxCursor(SCNHANDLE hFilm);
|
||||
void DwInitCursor(SCNHANDLE bfilm);
|
||||
void DropCursor();
|
||||
void RestartCursor();
|
||||
void RebootCursor();
|
||||
void StartCursorFollowed();
|
||||
void EndCursorFollowed();
|
||||
void InitCurObj();
|
||||
void InitCurPos();
|
||||
void DoCursorMove();
|
||||
|
||||
bool CursorIsFrozen() { return _frozenCursor; }
|
||||
int NumTrails() { return _numTrails; }
|
||||
bool IsHidden() { return _hiddenCursor; }
|
||||
bool ShouldBeHidden() { return _hiddenCursor || _tempHiddenCursor; }
|
||||
bool HasReelData() { return _cursorFilm != 0; }
|
||||
|
||||
bool _cursorProcessesStopped; // Set by DropCursor() at the end of a scene
|
||||
// - causes cursor processes to do nothing
|
||||
// Reset when main cursor has re-initialized
|
||||
|
||||
bool _cursorProcessesRestarted; // When main cursor has been bWhoa-ed, it waits
|
||||
// for this to be true.
|
||||
// Main cursor sets this to true after a re-start
|
||||
|
||||
private:
|
||||
void InitCurTrailObj(int i, int x, int y);
|
||||
bool GetDriverPosition(int *x, int *y);
|
||||
|
||||
OBJECT *_mainCursor; // Main cursor object
|
||||
OBJECT *_auxCursor; // Auxiliary cursor object
|
||||
|
||||
ANIM _mainCursorAnim; // Main cursor animation structure
|
||||
ANIM _auxCursorAnim; // Auxiliary cursor animation structure
|
||||
|
||||
bool _hiddenCursor; // Set when cursor is hidden
|
||||
bool _hiddenTrails; // Set when cursor trails are hidden
|
||||
bool _tempHiddenCursor; // Set when cursor is hidden
|
||||
|
||||
bool _frozenCursor; // Set when cursor position is frozen
|
||||
|
||||
frac_t _iterationSize;
|
||||
|
||||
SCNHANDLE _cursorFilm; // Handle to cursor reel data
|
||||
|
||||
int _numTrails;
|
||||
int _nextTrail;
|
||||
|
||||
// Auxillary cursor image's animation offsets
|
||||
short _auxCursorOffsetX;
|
||||
short _auxCursorOffsetY;
|
||||
|
||||
struct {
|
||||
ANIM trailAnim; // Animation structure
|
||||
OBJECT *trailObj; // This trailer's object
|
||||
} _trailData[MAX_TRAILERS];
|
||||
|
||||
int _lastCursorX, _lastCursorY;
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_CURSOR_H
|
||||
209
engines/tinsel/debugger.cpp
Normal file
209
engines/tinsel/debugger.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/debugger.h"
|
||||
#include "tinsel/dialogs.h"
|
||||
#include "tinsel/pcode.h"
|
||||
#include "tinsel/scene.h"
|
||||
#include "tinsel/sound.h"
|
||||
#include "tinsel/music.h"
|
||||
#include "tinsel/font.h"
|
||||
#include "tinsel/strres.h"
|
||||
#include "tinsel/noir/notebook.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- EXTERNAL FUNCTIONS ---------------------
|
||||
|
||||
// In PDISPLAY.CPP
|
||||
extern void TogglePathDisplay();
|
||||
// In tinsel.cpp
|
||||
extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
|
||||
// In scene.cpp
|
||||
extern SCNHANDLE GetSceneHandle();
|
||||
|
||||
//----------------- SUPPORT FUNCTIONS ---------------------
|
||||
|
||||
//static
|
||||
int strToInt(const char *s) {
|
||||
if (!*s)
|
||||
// No string at all
|
||||
return 0;
|
||||
else if (toupper(s[strlen(s) - 1]) != 'H')
|
||||
// Standard decimal string
|
||||
return atoi(s);
|
||||
|
||||
// Hexadecimal string
|
||||
uint tmp;
|
||||
if (!sscanf(s, "%xh", &tmp))
|
||||
tmp = 0;
|
||||
return (int)tmp;
|
||||
}
|
||||
|
||||
//----------------- CONSOLE CLASS ---------------------
|
||||
|
||||
Console::Console() : GUI::Debugger() {
|
||||
if (TinselVersion == 3) {
|
||||
registerCmd("add_clue", WRAP_METHOD(Console, cmd_add_clue));
|
||||
registerCmd("add_all_clues", WRAP_METHOD(Console, cmd_add_all_clues));
|
||||
registerCmd("cross_clue", WRAP_METHOD(Console, cmd_cross_clue));
|
||||
registerCmd("list_clues", WRAP_METHOD(Console, cmd_list_clues));
|
||||
}
|
||||
registerCmd("item", WRAP_METHOD(Console, cmd_item));
|
||||
registerCmd("scene", WRAP_METHOD(Console, cmd_scene));
|
||||
registerCmd("music", WRAP_METHOD(Console, cmd_music));
|
||||
registerCmd("sound", WRAP_METHOD(Console, cmd_sound));
|
||||
registerCmd("string", WRAP_METHOD(Console, cmd_string));
|
||||
}
|
||||
|
||||
Console::~Console() {
|
||||
}
|
||||
|
||||
bool Console::cmd_item(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("%s item_number\n", argv[0]);
|
||||
debugPrintf("Sets the currently active 'held' item\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
_vm->_dialogs->holdItem(INV_NOICON);
|
||||
_vm->_dialogs->holdItem(strToInt(argv[1]));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::cmd_scene(int argc, const char **argv) {
|
||||
if (argc < 1 || argc > 3) {
|
||||
debugPrintf("%s [scene_number [entry number]]\n", argv[0]);
|
||||
debugPrintf("If no parameters are given, prints the current scene.\n");
|
||||
debugPrintf("Otherwise changes to the specified scene number. Entry number defaults to 1 if none provided\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
debugPrintf("Current scene is %d\n", GetSceneHandle() >> SCNHANDLE_SHIFT);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 sceneNumber = (uint32)strToInt(argv[1]) << SCNHANDLE_SHIFT;
|
||||
int entryNumber = (argc >= 3) ? strToInt(argv[2]) : 1;
|
||||
|
||||
SetNewScene(sceneNumber, entryNumber, TRANS_CUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::cmd_music(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("%s track_number or %s -offset\n", argv[0], argv[0]);
|
||||
debugPrintf("Plays the MIDI track number provided, or the offset inside midi.dat\n");
|
||||
debugPrintf("A positive number signifies a track number, whereas a negative signifies an offset\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int param = strToInt(argv[1]);
|
||||
if (param == 0) {
|
||||
debugPrintf("Track number/offset can't be 0!\n");
|
||||
} else if (param > 0) {
|
||||
// Track provided
|
||||
_vm->_music->PlayMidiSequence(_vm->_music->GetTrackOffset(param - 1), false);
|
||||
} else if (param < 0) {
|
||||
// Offset provided
|
||||
param = param * -1;
|
||||
_vm->_music->PlayMidiSequence(param, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmd_sound(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("%s id\n", argv[0]);
|
||||
debugPrintf("Plays the sound with the given ID\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int id = strToInt(argv[1]);
|
||||
if (_vm->_sound->sampleExists(id)) {
|
||||
if (TinselVersion <= 1)
|
||||
_vm->_sound->playSample(id, Audio::Mixer::kSpeechSoundType);
|
||||
else
|
||||
_vm->_sound->playSample(id, 0, false, 0, 0, PRIORITY_TALK, Audio::Mixer::kSpeechSoundType);
|
||||
} else {
|
||||
debugPrintf("Sample %d does not exist!\n", id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmd_string(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("%s id\n", argv[0]);
|
||||
debugPrintf("Prints the string with the given ID\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
char tmp[TBUFSZ];
|
||||
int id = strToInt(argv[1]);
|
||||
LoadStringRes(id, tmp, TBUFSZ);
|
||||
debugPrintf("%s\n", tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Noir:
|
||||
bool Console::cmd_add_clue(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("%s clue_id\n", argv[0]);
|
||||
debugPrintf("Adds a clue to the notebook\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
_vm->_notebook->addClue(strToInt(argv[1]));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::cmd_add_all_clues(int argc, const char **argv) {
|
||||
auto clues = _vm->_dialogs->getAllNotebookClues();
|
||||
for (auto clue : clues) {
|
||||
_vm->_notebook->addClue(clue);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::cmd_cross_clue(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("%s clue_id\n", argv[0]);
|
||||
debugPrintf("Crosses out a clue in the notebook\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
_vm->_notebook->crossClue(strToInt(argv[1]));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::cmd_list_clues(int argc, const char **argv) {
|
||||
auto clues = _vm->_dialogs->getAllNotebookClues();
|
||||
for (auto clue : clues) {
|
||||
debugPrintf("%d\n", clue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
50
engines/tinsel/debugger.h
Normal file
50
engines/tinsel/debugger.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_DEBUGGER_H
|
||||
#define TINSEL_DEBUGGER_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
class TinselEngine;
|
||||
|
||||
class Console: public GUI::Debugger {
|
||||
public:
|
||||
Console();
|
||||
~Console() override;
|
||||
|
||||
private:
|
||||
bool cmd_add_clue(int argc, const char **argv);
|
||||
bool cmd_add_all_clues(int argc, const char **argv);
|
||||
bool cmd_cross_clue(int argc, const char **argv);
|
||||
bool cmd_list_clues(int argc, const char **argv);
|
||||
bool cmd_item(int argc, const char **argv);
|
||||
bool cmd_scene(int argc, const char **argv);
|
||||
bool cmd_music(int argc, const char **argv);
|
||||
bool cmd_sound(int argc, const char **argv);
|
||||
bool cmd_string(int argc, const char **argv);
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
220
engines/tinsel/detection.cpp
Normal file
220
engines/tinsel/detection.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "common/file.h"
|
||||
#include "common/md5.h"
|
||||
|
||||
#include "tinsel/detection.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
static const PlainGameDescriptor tinselGames[] = {
|
||||
{"dw", "Discworld"},
|
||||
{"dw2", "Discworld II: Missing Presumed ...!?"},
|
||||
{"noir", "Discworld Noir"},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Tinsel::kTinselDebugAnimations, "animations", "Animations debugging"},
|
||||
{Tinsel::kTinselDebugActions, "actions", "Actions debugging"},
|
||||
{Tinsel::kTinselDebugSound, "sound", "Sound debugging"},
|
||||
{Tinsel::kTinselDebugMusic, "music", "Music debugging"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
#include "tinsel/detection_tables.h"
|
||||
|
||||
class TinselMetaEngineDetection : public AdvancedMetaEngineDetection<Tinsel::TinselGameDescription> {
|
||||
public:
|
||||
TinselMetaEngineDetection() : AdvancedMetaEngineDetection(Tinsel::gameDescriptions, tinselGames) {
|
||||
}
|
||||
|
||||
const char *getName() const override{
|
||||
return "tinsel";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Tinsel";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Tinsel (C) Psygnosis";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
|
||||
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extraInfo) const override;
|
||||
};
|
||||
|
||||
struct SizeMD5 {
|
||||
unsigned int size;
|
||||
Common::String md5;
|
||||
};
|
||||
typedef Common::HashMap<Common::Path, SizeMD5, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> SizeMD5Map;
|
||||
typedef Common::HashMap<Common::Path, Common::FSNode, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> FileMap;
|
||||
typedef Common::Array<const ADGameDescription *> ADGameDescList;
|
||||
|
||||
/**
|
||||
* Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation
|
||||
* where the files haven't been renamed (i.e. don't have the '1' just before the extension)
|
||||
*/
|
||||
ADDetectedGame TinselMetaEngineDetection::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist, ADDetectedGameExtraInfo **extraInfo) const {
|
||||
Common::String extra;
|
||||
FileMap allFiles;
|
||||
SizeMD5Map filesSizeMD5;
|
||||
|
||||
const ADGameFileDescription *fileDesc;
|
||||
const Tinsel::TinselGameDescription *g;
|
||||
|
||||
if (fslist.empty())
|
||||
return ADDetectedGame();
|
||||
|
||||
// TODO: The following code is essentially a slightly modified copy of the
|
||||
// complete code of function detectGame() in engines/advancedDetector.cpp.
|
||||
// That quite some hefty and undesirable code duplication. Its only purpose
|
||||
// seems to be to treat filenames of the form "foo1.ext" as "foo.ext".
|
||||
// It would be nice to avoid this code duplication.
|
||||
|
||||
// First we compose a hashmap of all files in fslist.
|
||||
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
||||
if (file->isDirectory()) {
|
||||
if (!scumm_stricmp(file->getName().c_str(), "dw2")) {
|
||||
// Probably Discworld 2 subfolder on CD, so add it's contents as well
|
||||
Common::FSList files;
|
||||
if (file->getChildren(files, Common::FSNode::kListAll)) {
|
||||
Common::FSList::const_iterator file2;
|
||||
for (file2 = files.begin(); file2 != files.end(); ++file2) {
|
||||
if (file2->isDirectory())
|
||||
continue;
|
||||
|
||||
Common::Path fname = file2->getPathInArchive();
|
||||
allFiles[fname] = *file2;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Common::Path tstr = file->getPathInArchive();
|
||||
|
||||
allFiles[tstr] = *file; // Record the presence of this file
|
||||
}
|
||||
|
||||
// Check which files are included in some dw2 ADGameDescription *and* present
|
||||
// in fslist without a '1' suffix character. Compute MD5s and file sizes for these files.
|
||||
for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) {
|
||||
if (strcmp(g->desc.gameId, "dw2") != 0)
|
||||
continue;
|
||||
|
||||
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
|
||||
// Get the next filename, stripping off any '1' suffix character
|
||||
char tempFilename[50];
|
||||
Common::strlcpy(tempFilename, fileDesc->fileName, 50);
|
||||
char *pOne = strchr(tempFilename, '1');
|
||||
if (pOne) {
|
||||
do {
|
||||
*pOne = *(pOne + 1);
|
||||
pOne++;
|
||||
} while (*pOne);
|
||||
}
|
||||
|
||||
Common::Path fname(tempFilename);
|
||||
if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) {
|
||||
SizeMD5 tmp;
|
||||
Common::File testFile;
|
||||
|
||||
if (testFile.open(allFiles[fname])) {
|
||||
tmp.size = (int32)testFile.size();
|
||||
tmp.md5 = computeStreamMD5AsString(testFile, _md5Bytes);
|
||||
} else {
|
||||
tmp.size = AD_NO_SIZE;
|
||||
}
|
||||
|
||||
filesSizeMD5[fname] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ADDetectedGame matched;
|
||||
int maxFilesMatched = 0;
|
||||
|
||||
// MD5 based matching
|
||||
for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) {
|
||||
if (strcmp(g->desc.gameId, "dw2") != 0)
|
||||
continue;
|
||||
|
||||
bool fileMissing = false;
|
||||
|
||||
// Try to match all files for this game
|
||||
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
|
||||
// Get the next filename, stripping off any '1' suffix character
|
||||
char tempFilename[50];
|
||||
Common::strlcpy(tempFilename, fileDesc->fileName, 50);
|
||||
char *pOne = strchr(tempFilename, '1');
|
||||
if (pOne) {
|
||||
do {
|
||||
*pOne = *(pOne + 1);
|
||||
pOne++;
|
||||
} while (*pOne);
|
||||
}
|
||||
|
||||
Common::Path tstr(tempFilename);
|
||||
|
||||
if (!filesSizeMD5.contains(tstr)) {
|
||||
fileMissing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) {
|
||||
fileMissing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileDesc->fileSize != AD_NO_SIZE && fileDesc->fileSize != filesSizeMD5[tstr].size) {
|
||||
fileMissing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileMissing) {
|
||||
// Count the number of matching files. Then, only keep those
|
||||
// entries which match a maximal amount of files.
|
||||
int curFilesMatched = 0;
|
||||
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
|
||||
curFilesMatched++;
|
||||
|
||||
if (curFilesMatched >= maxFilesMatched) {
|
||||
maxFilesMatched = curFilesMatched;
|
||||
|
||||
matched = ADDetectedGame(&g->desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
REGISTER_PLUGIN_STATIC(TINSEL_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, TinselMetaEngineDetection);
|
||||
82
engines/tinsel/detection.h
Normal file
82
engines/tinsel/detection.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_DETECTION_H
|
||||
#define TINSEL_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// (Optionally) remove the "baked-in" letterboxing in Discworld 2/Noir.
|
||||
#define GAMEOPTION_CROP_HEIGHT_480_TO_432 GUIO_GAMEOPTIONS1
|
||||
|
||||
enum TinselGameID {
|
||||
GID_DW1 = 0,
|
||||
GID_DW2 = 1,
|
||||
GID_NOIR = 2
|
||||
};
|
||||
|
||||
enum TinselGameFeatures {
|
||||
GF_SCNFILES = 1 << 0,
|
||||
GF_ENHANCED_AUDIO_SUPPORT = 1 << 1,
|
||||
GF_ALT_MIDI = 1 << 2, // Alternate sequence in midi.dat file
|
||||
|
||||
// The GF_USE_?FLAGS values specify how many country flags are displayed
|
||||
// in the subtitles options dialog.
|
||||
// None of these defined -> 1 language, in ENGLISH.TXT
|
||||
GF_USE_3FLAGS = 1 << 3, // French, German, Spanish
|
||||
GF_USE_4FLAGS = 1 << 4, // French, German, Spanish, Italian
|
||||
GF_USE_5FLAGS = 1 << 5 // 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.
|
||||
* 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,
|
||||
TINSEL_V1 = 1,
|
||||
TINSEL_V2 = 2,
|
||||
TINSEL_V3 = 3
|
||||
};
|
||||
|
||||
struct TinselGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
|
||||
int gameID;
|
||||
int gameType;
|
||||
uint32 features;
|
||||
uint16 version;
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_DETECTION_H
|
||||
872
engines/tinsel/detection_tables.h
Normal file
872
engines/tinsel/detection_tables.h
Normal file
@@ -0,0 +1,872 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/translation.h"
|
||||
namespace Tinsel {
|
||||
|
||||
static const TinselGameDescription gameDescriptions[] = {
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
// ==== Discworld 1 early (TinselV0) entries ==============================
|
||||
|
||||
{ // Floppy Demo V0 from https://web.archive.org/web/20100415160943/http://www.adventure-treff.de/specials/dl_demos.php
|
||||
{
|
||||
"dw",
|
||||
"Floppy Demo",
|
||||
AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
|
||||
//AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO,
|
||||
GUIO3(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
0,
|
||||
TINSEL_V0,
|
||||
},
|
||||
|
||||
// ==== Discworld 1 entries ===============================================
|
||||
|
||||
{ // CD Demo V1 version, with *.gra files
|
||||
{
|
||||
"dw",
|
||||
"CD Demo",
|
||||
AD_ENTRY2s("dw.gra", "ef5a2518c9e205f786f5a4526396e661", 781676,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
0,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Macintosh CD Demo V1 version, with *.scn files, see tracker #5517
|
||||
{
|
||||
"dw",
|
||||
"CD Demo",
|
||||
{
|
||||
{"dw.scn", 0, "cfc40a8d5d476a1c9d3abf826fa46f8c", 1272686},
|
||||
{"english.txt", 0, "c69b5d2067e9114a63569a61e9a82faa", 228878},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_DEMO | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Multilingual Floppy V1 with *.gra files.
|
||||
// Note: It contains no english subtitles.
|
||||
{
|
||||
"dw",
|
||||
"Floppy",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"dw",
|
||||
"Floppy",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"dw",
|
||||
"Floppy",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"dw",
|
||||
"Floppy",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Floppy V1 version, with *.gra files
|
||||
{
|
||||
"dw",
|
||||
"Floppy",
|
||||
AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // CD V1 version, with *.gra files (same as the floppy one, with english.smp)
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Polish fan translation CD V1 version, with *.gra files (same as the floppy one, with english.smp)
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.gra", "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO_NONE
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Polish fan translaction floppy V1 version, with *.gra files
|
||||
{
|
||||
"dw",
|
||||
"Floppy",
|
||||
AD_ENTRY1s("dw.gra", "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NOSPEECH
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Italian CD with english speech and *.gra files.
|
||||
// Note: It contains only italian subtitles, but inside english.txt
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"english.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 237774},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Multilingual CD with english speech and *.gra files.
|
||||
// Note: It contains no english subtitles.
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
{
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
{
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"french.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"german.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"italian.txt", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish.txt", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE | ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // English CD v2
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.scn", "70955425870c7720d6eebed903b2ef41", 776188,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Hebrew CD v2
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.scn", "759d1374b4f02af6d52fc07c96679936", 770780,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Discworld PSX CD
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY1s("english.txt", "7526cfc3a64e00f223795de476b4e2c9", 230326),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformPSX,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Discworld PSX German CD
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY1s("dw.scn", "0b34bb57cd3961e4528e4bce48cc0ab9", 339764),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformPSX,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Discworld PSX CD Japanese
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY1s("dw.scn", "bd2e47010565998641ec45a9c9285be0", 328048),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformPSX,
|
||||
ADGF_CD | ADGF_UNSTABLE,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
|
||||
{ // multilanguage PSX demo
|
||||
{
|
||||
"dw",
|
||||
"CD Demo",
|
||||
{
|
||||
{"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
|
||||
{"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
|
||||
{"italian.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 239834},
|
||||
{"spanish.txt", 0, "c324170c3f1922c605c5cc09ba265aa5", 236702},
|
||||
{"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformPSX,
|
||||
ADGF_CD | ADGF_DEMO,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // English Saturn CD
|
||||
{
|
||||
"dw",
|
||||
_s("Saturn CD version is not yet supported"),
|
||||
AD_ENTRY2s("dw.scn", "6803f293c88758057cc685b9437f7637", 382248,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformSaturn,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | ADGF_UNSUPPORTED,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Mac English CD, see tracker #6384
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.scn", "114643df0d1f1530a0a9c5d4e38917bc", 1268553,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Mac multilanguage CD
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.scn", "cfc40a8d5d476a1c9d3abf826fa46f8c", 1265532,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Mac Japanese CD
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.scn", "55a11596b16130027fb28d8c203655a8", 1430841,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_CD | ADGF_UNSTABLE,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
|
||||
{ // German CD re-release "Neon Edition"
|
||||
// Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY1s("dw.scn", "6182c7986eaec893c62fb6ea13a9f225", 774556),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Russian Discworld 1
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
|
||||
{"english.txt", 0, "f73dcbd7b136b37c2adf7c9448ea336d", 231821},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // English CD "Argentum Collection"
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw.scn", "36795d539e290838fa1d4c11789cb142", 776188,
|
||||
"english.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Russian Discworld 1. Fan translation v1.1
|
||||
{
|
||||
"dw",
|
||||
"CD v1.1",
|
||||
{
|
||||
{"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
|
||||
{"english.txt", 0, "317542cf2e50106d9c9421ddcf821e22", 221656},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
{ // Polish fan translaction Discworld 1
|
||||
{
|
||||
"dw",
|
||||
"CD",
|
||||
{
|
||||
{"dw.scn", 0, "fa169d2c98660215ebd84b49c1899eef", 776396},
|
||||
{"english.txt", 0, "c1a53eb7ec812689dab70e2bb22cf2ab", 224151},
|
||||
{"english.smp", 0, NULL, AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::PL_POL,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO_NONE
|
||||
},
|
||||
GID_DW1,
|
||||
0,
|
||||
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
|
||||
TINSEL_V1,
|
||||
},
|
||||
|
||||
// ==== Discworld 2 entries ===============================================
|
||||
// Note: All Discworld 2 versions are CD only, therefore we don't add the ADGF_CD flag
|
||||
#define DISCWORLD2_GUIOPTIONS GUIO2(GUIO_NOASPECT, GAMEOPTION_CROP_HEIGHT_480_TO_432)
|
||||
|
||||
{ // English Discworld 2 demo (dw2-win-demo-en)
|
||||
{
|
||||
"dw2",
|
||||
"Demo",
|
||||
AD_ENTRY2s("dw2.scn", "853ab998f5136b69bc586991175d6eeb", 4231121,
|
||||
"english.smp", "b5660a0e031cb4710bcb0ef5629ea61d", 28562357),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // English Discworld 2 demo (second Windows demo: dw2-win-demo-2-en)
|
||||
{
|
||||
"dw2",
|
||||
"",
|
||||
AD_ENTRY3s("dw2.scn", "3f24abb61a058f8faeac7c0768cf21fc", 4224921,
|
||||
"english.smp", "b5660a0e031cb4710bcb0ef5629ea61d", 31360342,
|
||||
"english.txt", "f17e10eccac0fb2d1fea489a951da266", 283144),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_UNSUPPORTED | ADGF_DEMO,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // English (US) Discworld 2 demo (DOS demo: dw2-dos-demo-en)
|
||||
{
|
||||
"dw2",
|
||||
"",
|
||||
AD_ENTRY2s("dw2.scn", "05beafadd26562d708f68194d337b2cb", 103221,
|
||||
"us.smp", "b5660a0e031cb4710bcb0ef5629ea61d", 28320582),
|
||||
Common::EN_USA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_UNSUPPORTED | ADGF_DEMO,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // European/Australian Discworld 2 release
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
|
||||
"english1.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_GRB,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // US Discworld 2 release
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
|
||||
"us1.smp", NULL, AD_NO_SIZE),
|
||||
Common::EN_USA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // French version of Discworld 2
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
|
||||
"french1.smp", NULL, AD_NO_SIZE),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // German Discworld 2 re-release "Neon Edition"
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
|
||||
"german1.smp", NULL, AD_NO_SIZE),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // Italian/Spanish Discworld 2
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
{
|
||||
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
|
||||
{"english1.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"italian1.txt", 0, "d443249f8b55489b5888c227b9096f4e", 246495},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
{
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
{
|
||||
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
|
||||
{"english1.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"spanish1.txt", 0, "bc6e147c5f542db228ac577357e4d897", 230323},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
{ // Russian Discworld 2 release by Fargus
|
||||
{
|
||||
"dw2",
|
||||
"CD",
|
||||
{
|
||||
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
|
||||
{"english1.smp", 0, NULL, AD_NO_SIZE},
|
||||
{"english1.txt", 0, "b522e19d7b2cd7b85e50e36fe48e36a9", 274444},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
DISCWORLD2_GUIOPTIONS
|
||||
},
|
||||
GID_DW2,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V2,
|
||||
},
|
||||
|
||||
// ==== Discworld Noir entries ===============================================
|
||||
// Note: All Discworld Noir versions are CD only, therefore we don't add the ADGF_CD flag
|
||||
|
||||
#define NOIR_GUIOPTIONS GUIO1(GAMEOPTION_CROP_HEIGHT_480_TO_432)
|
||||
{ // Discworld Noir, Windows 3CD version
|
||||
{
|
||||
"noir",
|
||||
"CD",
|
||||
AD_ENTRY2s("dw3.scn", "16104acdc66cda903f860acac02a96bd", AD_NO_SIZE,
|
||||
"english.smp", "94e510fd33c5c4a67b274bf5c068a87a", AD_NO_SIZE),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_UNSTABLE,
|
||||
NOIR_GUIOPTIONS
|
||||
|
||||
},
|
||||
GID_NOIR,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V3,
|
||||
},
|
||||
|
||||
{ // Discworld Noir, Interactive Windows Demo
|
||||
{
|
||||
"noir",
|
||||
"Demo",
|
||||
AD_ENTRY2s("english.smp", "ecca3ec84ad6460bf289e6171ac32048", 73494004,
|
||||
"dw3.scn", "f8e50c19b44a2c6eaee5f78654647878", 803781),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_UNSTABLE | ADGF_DEMO,
|
||||
NOIR_GUIOPTIONS
|
||||
},
|
||||
GID_NOIR,
|
||||
0,
|
||||
GF_SCNFILES,
|
||||
TINSEL_V3,
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
5914
engines/tinsel/dialogs.cpp
Normal file
5914
engines/tinsel/dialogs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
577
engines/tinsel/dialogs.h
Normal file
577
engines/tinsel/dialogs.h
Normal file
@@ -0,0 +1,577 @@
|
||||
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Inventory related functions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_INVENTORY_H // prevent multiple includes
|
||||
#define TINSEL_INVENTORY_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/events.h" // for PLR_EVENT, PLR_EVENT
|
||||
#include "tinsel/inv_objects.h"
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/movers.h"
|
||||
|
||||
namespace Common {
|
||||
class Serializer;
|
||||
struct KeyState;
|
||||
}
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct OBJECT;
|
||||
struct FREEL;
|
||||
struct FILM;
|
||||
struct CONFINIT;
|
||||
|
||||
enum {
|
||||
INV_OPEN = -1, // DW1 only
|
||||
INV_CONV = 0,
|
||||
INV_1 = 1,
|
||||
INV_2 = 2,
|
||||
DW0_INV_CONF = 3,
|
||||
INV_MENU = 3, // DW2 constant
|
||||
NUM_INV_V0 = 4,
|
||||
|
||||
// Discworld 2 constants
|
||||
DW2_INV_OPEN = 5,
|
||||
INV_DEFAULT = 6,
|
||||
|
||||
// Noir constants
|
||||
INV_3 = 3,
|
||||
INV_4 = 4,
|
||||
NOIR_INV_CONF = 5,
|
||||
NUM_INV_V3 = 6,
|
||||
INV_7NOINV = 7,
|
||||
INV_8NOINV = 8,
|
||||
INV_NOTEBOOK = 9,
|
||||
|
||||
MAX_NUM_INV = NUM_INV_V3 // For determination of _invD array size
|
||||
};
|
||||
|
||||
#define INV_CONF ((TinselVersion == 3) ? NOIR_INV_CONF : DW0_INV_CONF)
|
||||
#define NUM_INV ((TinselVersion == 3) ? NUM_INV_V3 : NUM_INV_V0)
|
||||
|
||||
enum {
|
||||
NOOBJECT = -1,
|
||||
INV_NOICON_V0 = -1,
|
||||
INV_CLOSEICON = -2,
|
||||
INV_OPENICON = -3,
|
||||
INV_HELDNOTIN_V0 = -4,
|
||||
// Noir discerns between NOOBJECT and INV_NOICON
|
||||
INV_NOICON_V3 = 0,
|
||||
INV_HELDNOTIN_V3 = 1,
|
||||
INV_HELDIN = 2,
|
||||
};
|
||||
|
||||
#define INV_NOICON ((TinselVersion == 3) ? INV_NOICON_V3 : INV_NOICON_V0)
|
||||
#define INV_HELDNOTIN ((TinselVersion == 3) ? INV_HELDNOTIN_V3 : INV_HELDNOTIN_V0)
|
||||
|
||||
enum CONV_PARAM {
|
||||
CONV_DEF,
|
||||
CONV_BOTTOM,
|
||||
CONV_END,
|
||||
CONV_TOP
|
||||
};
|
||||
|
||||
enum InventoryType { EMPTY,
|
||||
FULL,
|
||||
CONF };
|
||||
|
||||
enum InvCursorFN { IC_AREA,
|
||||
IC_DROP };
|
||||
|
||||
#define sliderRange (_sliderYmax - _sliderYmin)
|
||||
#define MAXSLIDES 4
|
||||
#define MAX_PERMICONS 10 // Max permanent conversation icons
|
||||
#define MAXHICONS 10 // Max dimensions of
|
||||
#define MAXVICONS 6 // an inventory window
|
||||
#define SG_DESC_LEN 40 // Max. saved game description length
|
||||
|
||||
// Number of objects that makes up an empty window
|
||||
#define MAX_WCOMP_T0 21 // 4 corners + (3+3) sides + (2+2) extra sides
|
||||
// + Bground + title + slider
|
||||
// + more Needed for save game window
|
||||
#define MAX_WCOMP_T3 84
|
||||
#define MAX_WCOMP (TinselVersion == 3 ? MAX_WCOMP_T3 : MAX_WCOMP_T0)
|
||||
|
||||
#define MAX_ICONS MAXHICONS *MAXVICONS
|
||||
#define MAX_ININV_TOT 160
|
||||
|
||||
enum CONFTYPE {
|
||||
MAIN_MENU,
|
||||
SAVE_MENU,
|
||||
LOAD_MENU,
|
||||
QUIT_MENU,
|
||||
RESTART_MENU,
|
||||
SOUND_MENU,
|
||||
CONTROLS_MENU,
|
||||
SUBTITLES_MENU,
|
||||
HOPPER_MENU1,
|
||||
HOPPER_MENU2,
|
||||
TOP_WINDOW
|
||||
};
|
||||
|
||||
struct INV_DEF {
|
||||
int MinHicons; // }
|
||||
int MinVicons; // } Dimension limits
|
||||
int MaxHicons; // }
|
||||
int MaxVicons; // }
|
||||
|
||||
int NoofHicons; // }
|
||||
int NoofVicons; // } Current dimentsions
|
||||
|
||||
int contents[160]; // Contained items
|
||||
int NoofItems; // Current number of held items
|
||||
|
||||
int FirstDisp; // Index to first item currently displayed
|
||||
|
||||
int inventoryX; // } Display position
|
||||
int inventoryY; // }
|
||||
int otherX; // } Display position
|
||||
int otherY; // }
|
||||
|
||||
int MaxInvObj; // Max. allowed contents
|
||||
|
||||
SCNHANDLE hInvTitle; // Window heading
|
||||
|
||||
bool resizable; // Re-sizable window?
|
||||
bool bMoveable; // Moveable window?
|
||||
|
||||
int sNoofHicons; // }
|
||||
int sNoofVicons; // } Current dimensions
|
||||
|
||||
bool bMax; // Maximised last time open?
|
||||
};
|
||||
|
||||
//----- Data pertinant to scene hoppers ------------------------
|
||||
|
||||
struct HOPPER {
|
||||
uint32 hScene;
|
||||
SCNHANDLE hSceneDesc;
|
||||
uint32 numEntries;
|
||||
uint32 entryIndex;
|
||||
};
|
||||
|
||||
struct HOPENTRY {
|
||||
uint32 eNumber; // entrance number
|
||||
SCNHANDLE hDesc; // handle to entrance description
|
||||
uint32 flags;
|
||||
};
|
||||
|
||||
enum BTYPE {
|
||||
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,
|
||||
#ifndef JAPAN
|
||||
OPENSUBT,
|
||||
#endif
|
||||
OPENQUIT,
|
||||
INITGAME,
|
||||
MUSICVOL,
|
||||
|
||||
HOPPER2, // Call up Scene Hopper 2
|
||||
BF_CHANGESCENE,
|
||||
|
||||
CLANG,
|
||||
RLANG
|
||||
#ifdef MAC_OPTIONS
|
||||
,
|
||||
MASTERVOL,
|
||||
SAMPVOL
|
||||
#endif
|
||||
};
|
||||
|
||||
enum TM { TM_POINTER,
|
||||
TM_INDEX,
|
||||
TM_STRINGNUM,
|
||||
TM_UNK4,
|
||||
TM_NONE };
|
||||
|
||||
// For slideSlider() and similar
|
||||
enum SSFN {
|
||||
S_START,
|
||||
S_SLIDE,
|
||||
S_END,
|
||||
S_TIMEUP,
|
||||
S_TIMEDN
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// Data for button press/toggle effects
|
||||
struct BUTTONEFFECT {
|
||||
bool bButAnim;
|
||||
CONFBOX *box;
|
||||
bool press; // true = button press; false = button toggle
|
||||
};
|
||||
|
||||
enum class SysReel;
|
||||
|
||||
class Dialogs {
|
||||
public:
|
||||
Dialogs();
|
||||
virtual ~Dialogs();
|
||||
|
||||
void popUpInventory(int invno, int menuId = -1);
|
||||
void openMenu(CONFTYPE menuType);
|
||||
|
||||
void xMovement(int x);
|
||||
void yMovement(int y);
|
||||
|
||||
void eventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds);
|
||||
|
||||
int whichItemHeld();
|
||||
|
||||
void holdItem(int item, bool bKeepFilm = false);
|
||||
void dropItem(int item);
|
||||
void clearInventory(int invno);
|
||||
void addToInventory(int invno, int icon, bool hold = false);
|
||||
bool remFromInventory(int invno, int icon);
|
||||
|
||||
void registerIcons(void *cptr, int num);
|
||||
|
||||
void idec_convw(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
|
||||
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
|
||||
void idec_inv1(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
|
||||
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
|
||||
void idec_inv2(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
|
||||
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
|
||||
|
||||
// Noir
|
||||
Common::Array<int> getAllNotebookClues() const;
|
||||
void idec_invMain(SCNHANDLE text, int MaxContents);
|
||||
|
||||
bool inventoryActive();
|
||||
bool inventoryOrNotebookActive();
|
||||
|
||||
void permaConvIcon(int icon, bool bEnd = false);
|
||||
|
||||
void convPos(int bpos);
|
||||
void convPoly(HPOLYGON hPoly);
|
||||
int getIcon();
|
||||
void closeDownConv();
|
||||
void hideConversation(bool hide);
|
||||
bool convIsHidden();
|
||||
|
||||
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);
|
||||
void setConfigStrings(SCNHANDLE *tp);
|
||||
|
||||
int invItem(Common::Point &coOrds, bool update);
|
||||
int invItem(int *x, int *y, bool update);
|
||||
int invItemId(int x, int y);
|
||||
|
||||
int inventoryPos(int num);
|
||||
|
||||
bool isInInventory(int object, int invnum);
|
||||
|
||||
void killInventory();
|
||||
|
||||
void syncInvInfo(Common::Serializer &s);
|
||||
|
||||
int invGetLimit(int invno);
|
||||
void invSetLimit(int invno, int MaxContents);
|
||||
void invSetSize(int invno, int MinWidth, int MinHeight,
|
||||
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
|
||||
|
||||
bool getIsInvObject(int id);
|
||||
|
||||
int whichInventoryOpen();
|
||||
|
||||
bool isTopWindow();
|
||||
bool menuActive();
|
||||
bool isConvWindow();
|
||||
|
||||
const FILM *getObjectFilm(int object) const;
|
||||
void setObjectFilm(int object, SCNHANDLE hFilm);
|
||||
void callFunction(BFUNC boxFunc);
|
||||
|
||||
OBJECT *addObject(const FREEL *pfreel, int num);
|
||||
void invPutDown(int index);
|
||||
void slideMSlider(int x, SSFN fn);
|
||||
void addBoxes(bool posnSlide);
|
||||
void select(int i, bool force);
|
||||
void fillInInventory();
|
||||
void invCursor(InvCursorFN fn, int CurX, int CurY);
|
||||
const InventoryObject *getInvObject(int id);
|
||||
const InventoryObjectT3 *getInvObjectT3(int id);
|
||||
void invPointEvent(const InventoryObject *invObj, int index);
|
||||
bool updateString(const Common::KeyState &kbd);
|
||||
bool inventoryIsActive() { return _inventoryState == ACTIVE_INV; }
|
||||
bool isMixingDeskControl() { return _invDragging == ID_MDCONT; }
|
||||
int currentInventoryX() { return _invD[_activeInv].inventoryX; }
|
||||
int currentInventoryY() { return _invD[_activeInv].inventoryY; }
|
||||
bool configurationIsActive() { return _activeInv == INV_CONF; }
|
||||
bool displayObjectsActive() { return _objArray[0] != NULL; }
|
||||
bool inventoryIsHidden() { return _InventoryHidden; }
|
||||
const FILM *getWindowData();
|
||||
void redraw();
|
||||
|
||||
// Noir
|
||||
bool isConvAndNotMove();
|
||||
|
||||
bool _noLanguage;
|
||||
int _glitterIndex;
|
||||
volatile int _pointedWaitCount; // used by ObjectProcess - fix the 'repeated pressing bug'
|
||||
// For editing save game names
|
||||
char _saveGameDesc[SG_DESC_LEN + 2];
|
||||
|
||||
OBJECT *_iconArray[MAX_ICONS]; // Current display objects (icons)
|
||||
BUTTONEFFECT _buttonEffect;
|
||||
|
||||
private:
|
||||
int whichMenuBox(int curX, int curY, bool bSlides);
|
||||
void confActionSpecial(int i);
|
||||
bool rePosition();
|
||||
bool languageChange();
|
||||
void primeSceneHopper();
|
||||
void freeSceneHopper();
|
||||
void rememberChosenScene();
|
||||
void setChosenScene();
|
||||
void firstEntry(int first);
|
||||
void hopAction();
|
||||
void dumpIconArray();
|
||||
void dumpDobjArray();
|
||||
void dumpObjArray();
|
||||
void firstScene(int first);
|
||||
void firstFile(int first);
|
||||
int getObjectIndex(int id) const;
|
||||
void invSaveGame();
|
||||
void invLoadGame();
|
||||
int invArea(int x, int y);
|
||||
void invBoxes(bool InBody, int curX, int curY);
|
||||
void invLabels(bool InBody, int aniX, int aniY);
|
||||
void adjustTop();
|
||||
OBJECT *addInvObject(int num, SCNHANDLE *hNewScript, int *aniSpeed);
|
||||
void addBackground(OBJECT **rect, const Common::Rect &bounds, OBJECT **title = nullptr, int textFrom = 0);
|
||||
void addTitle(OBJECT **title, const Common::Rect &rect);
|
||||
void addSlider(OBJECT **slide, const FILM *pfilm);
|
||||
void addBox(int *pi, const int i);
|
||||
void addEWSlider(OBJECT **slide, const FILM *pfilm);
|
||||
void positionInventory(OBJECT *pMultiObj, int xOffset, int yOffset, int zPosition);
|
||||
int addExtraWindow(int x, int y, OBJECT **retObj);
|
||||
void constructInventoryCommon(SysReel reel, bool hasTitle);
|
||||
void constructConversationInventory();
|
||||
void constructInventory(InventoryType filling);
|
||||
void constructOtherInventory(int menuId);
|
||||
void constructMainInventory();
|
||||
void alterCursor(int num);
|
||||
void setMenuGlobals(CONFINIT *ci);
|
||||
void closeInventory();
|
||||
int nearestSlideY(int fity);
|
||||
void slideSlider(int y, SSFN fn);
|
||||
void slideCSlider(int y, SSFN fn);
|
||||
void gettingTaller();
|
||||
void gettingShorter();
|
||||
void gettingWider();
|
||||
void gettingNarrower();
|
||||
void changeingSize();
|
||||
void invDragStart();
|
||||
void invDragEnd();
|
||||
bool menuDown(int lines);
|
||||
bool menuUp(int lines);
|
||||
void menuRollDown();
|
||||
void menuRollUp();
|
||||
void menuPageDown();
|
||||
void menuPageUp();
|
||||
void inventoryDown();
|
||||
void inventoryUp();
|
||||
void menuAction(int i, bool dbl);
|
||||
void invPickup(int index);
|
||||
void invWalkTo(const Common::Point &coOrds);
|
||||
void invAction();
|
||||
void invLook(const Common::Point &coOrds);
|
||||
void idec_inv(int num, SCNHANDLE text, int MaxContents,
|
||||
int MinWidth, int MinHeight,
|
||||
int StartWidth, int StartHeight,
|
||||
int MaxWidth, int MaxHeight,
|
||||
int startx, int starty, bool moveable);
|
||||
|
||||
//----- Permanent data (set once) -----
|
||||
SCNHANDLE _flagFilm; // Window members and cursors' graphic data
|
||||
SCNHANDLE _configStrings[20];
|
||||
|
||||
INV_DEF _invD[MAX_NUM_INV]; // Conversation + 2 inventories + ...
|
||||
int _activeInv; // Which inventory is currently active
|
||||
InventoryObjects *_invObjects; // Inventory objects' data
|
||||
SCNHANDLE *_invFilms;
|
||||
DIRECTION _initialDirection;
|
||||
|
||||
//----- Permanent data (updated, valid while inventory closed) -----
|
||||
int _heldItem; // Current held item
|
||||
|
||||
SCNHANDLE _heldFilm;
|
||||
SCNHANDLE _hWinParts; // Window members and cursors' graphic data
|
||||
|
||||
// Permanent contents of conversation inventory
|
||||
int _permIcons[MAX_PERMICONS]; // Basic items i.e. permanent contents
|
||||
int _numPermIcons; // - copy to conv. inventory at pop-up time
|
||||
int _numEndIcons;
|
||||
|
||||
//----- Data pertinant to current active inventory -----
|
||||
|
||||
bool _InventoryHidden;
|
||||
bool _InventoryMaximised;
|
||||
bool _ItemsChanged; // When set, causes items to be re-drawn
|
||||
|
||||
int _SuppH; // 'Linear' element of
|
||||
int _SuppV; // dimensions during re-sizing
|
||||
|
||||
int _yChange; //
|
||||
int _yCompensate; // All to do with re-sizing.
|
||||
int _xChange; //
|
||||
int _xCompensate; //
|
||||
|
||||
bool _reOpenMenu;
|
||||
|
||||
int _TL, _TR, _BL, _BR; // Used during window construction
|
||||
int _TLwidth, _TLheight; //
|
||||
int _TRwidth; //
|
||||
int _BLheight; //
|
||||
|
||||
LANGUAGE _displayedLanguage;
|
||||
|
||||
OBJECT *_objArray[MAX_WCOMP_T3]; // Current display objects (window)
|
||||
OBJECT *_dispObjArray[MAX_WCOMP_T3]; // Current display objects (re-sizing window)
|
||||
ANIM _iconAnims[MAX_ICONS];
|
||||
|
||||
OBJECT *_rectObject, *_slideObject; // Current display objects, for reference
|
||||
// objects are in objArray.
|
||||
|
||||
int _sliderYpos; // For positioning the slider
|
||||
int _sliderYmax, _sliderYmin; //
|
||||
|
||||
// Also to do with the slider
|
||||
struct {
|
||||
int n;
|
||||
int y;
|
||||
} _slideStuff[MAX_ININV_TOT + 1];
|
||||
|
||||
struct MDSLIDES {
|
||||
int num;
|
||||
OBJECT *obj;
|
||||
int min, max;
|
||||
};
|
||||
MDSLIDES _mdSlides[MAXSLIDES];
|
||||
int _numMdSlides;
|
||||
|
||||
// Icon clicked on to cause an event
|
||||
// - Passed to conversation polygon or actor code via Topic()
|
||||
// - (sometimes) Passed to inventory icon code via OtherObject()
|
||||
int _thisIcon;
|
||||
|
||||
CONV_PARAM _thisConvFn; // Top, 'Middle' or Bottom
|
||||
HPOLYGON _thisConvPoly; // Conversation code is in a polygon code block
|
||||
int _thisConvActor; // ...or an actor's code block.
|
||||
int _pointedIcon; // used by invLabels - icon pointed to on last call
|
||||
int _sX; // used by slideMSlider() - current x-coordinate
|
||||
int _lX; // used by slideMSlider() - last x-coordinate
|
||||
|
||||
bool _bMoveOnUnHide; // Set before start of conversation
|
||||
// - causes conversation to be started in a sensible place
|
||||
|
||||
HOPPER *_pHopper;
|
||||
HOPENTRY *_pEntries;
|
||||
int _numScenes;
|
||||
|
||||
int _numEntries;
|
||||
|
||||
HOPPER *_pChosenScene;
|
||||
|
||||
int _lastChosenScene;
|
||||
bool _bRemember;
|
||||
enum { IC_NORMAL,
|
||||
IC_DR,
|
||||
IC_UR,
|
||||
IC_TB,
|
||||
IC_LR,
|
||||
IC_INV,
|
||||
IC_UP,
|
||||
IC_DN } _invCursor;
|
||||
|
||||
enum { NO_INV,
|
||||
IDLE_INV,
|
||||
ACTIVE_INV,
|
||||
BOGUS_INV } _inventoryState;
|
||||
|
||||
enum { ID_NONE,
|
||||
ID_MOVE,
|
||||
ID_SLIDE,
|
||||
ID_BOTTOM,
|
||||
ID_TOP,
|
||||
ID_LEFT,
|
||||
ID_RIGHT,
|
||||
ID_TLEFT,
|
||||
ID_TRIGHT,
|
||||
ID_BLEFT,
|
||||
ID_BRIGHT,
|
||||
ID_CSLIDE,
|
||||
ID_MDCONT } _invDragging;
|
||||
};
|
||||
|
||||
void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, int myEscape, bool *result = NULL);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_INVENTRY_H */
|
||||
244
engines/tinsel/drives.cpp
Normal file
244
engines/tinsel/drives.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* CD/drive handling functions
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "tinsel/drives.h"
|
||||
#include "tinsel/scene.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/strres.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
char g_currentCD = '1';
|
||||
|
||||
static bool g_bChangingCD = false;
|
||||
static char g_nextCD = '\0';
|
||||
|
||||
static uint32 g_lastTime = 0;
|
||||
extern LANGUAGE g_sampleLanguage;
|
||||
|
||||
void ResetVarsDrives() {
|
||||
g_currentCD = '1';
|
||||
|
||||
g_bChangingCD = false;
|
||||
g_nextCD = '\0';
|
||||
|
||||
g_lastTime = 0;
|
||||
}
|
||||
|
||||
void CdCD(CORO_PARAM) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
while (g_bChangingCD) {
|
||||
if (CoroScheduler.getCurrentProcess()) {
|
||||
// FIXME: CdCD gets passed a Common::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 == Common::nullContext)
|
||||
error("CdCD needs context");
|
||||
CORO_SLEEP(1);
|
||||
} else
|
||||
error("No current process in CdCD()");
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
int GetCurrentCD() {
|
||||
// count from 1
|
||||
return (g_currentCD - '1' + 1);
|
||||
}
|
||||
|
||||
static const uint32 cdFlags[] = { fCd1, fCd2, fCd3, fCd4, fCd5, fCd6, fCd7, fCd8 };
|
||||
|
||||
void SetCD(int flags) {
|
||||
if (flags & cdFlags[g_currentCD - '1'])
|
||||
return;
|
||||
|
||||
error("SetCD() problem");
|
||||
}
|
||||
|
||||
int GetCD(int flags) {
|
||||
int i;
|
||||
char cd = '\0';
|
||||
|
||||
if (flags & cdFlags[g_currentCD - '1'])
|
||||
return GetCurrentCD();
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (flags & cdFlags[i]) {
|
||||
cd = (char)(i + '1');
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(i != 8);
|
||||
|
||||
g_nextCD = cd;
|
||||
return cd;
|
||||
}
|
||||
|
||||
void DoCdChange() {
|
||||
if (g_bChangingCD && (g_system->getMillis() > (g_lastTime + 1000))) {
|
||||
g_lastTime = g_system->getMillis();
|
||||
_vm->_sound->closeSampleStream();
|
||||
|
||||
// Use the filesize of the sample file to determine, for Discworld 2, which CD it is
|
||||
if (TinselVersion >= 2) {
|
||||
TinselFile f;
|
||||
if (!f.open(_vm->getSampleFile(g_sampleLanguage)))
|
||||
// No CD present
|
||||
return;
|
||||
|
||||
char sampleCdNumber = (f.size() >= (200 * 1024 * 1024)) ? '1' : '2';
|
||||
|
||||
f.close();
|
||||
|
||||
if (g_currentCD != sampleCdNumber)
|
||||
return;
|
||||
}
|
||||
|
||||
_vm->_sound->openSampleFiles();
|
||||
ChangeLanguage(TextLanguage());
|
||||
g_bChangingCD = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetNextCD(int cdNumber) {
|
||||
assert(cdNumber == 1 || cdNumber == 2);
|
||||
|
||||
g_nextCD = (char)(cdNumber + '1' - 1);
|
||||
}
|
||||
|
||||
bool GotoCD() {
|
||||
// WORKAROUND: Somehow, CdDoChange() is called twice... Hopefully, this guard helps
|
||||
if (g_currentCD == g_nextCD)
|
||||
return false;
|
||||
|
||||
g_currentCD = g_nextCD;
|
||||
|
||||
/* if (bNoCD) {
|
||||
strcpy(cdDirectory, hdDirectory);
|
||||
cdLastBit[3] = currentCD;
|
||||
strcat(cdDirectory, cdLastBit);
|
||||
}
|
||||
*/
|
||||
g_bChangingCD = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TinselFile::_warningShown = false;
|
||||
|
||||
TinselFile::TinselFile() : ReadStreamEndian(TinselV1Saturn) {
|
||||
_stream = nullptr;
|
||||
}
|
||||
|
||||
TinselFile::TinselFile(bool bigEndian) : ReadStreamEndian(bigEndian) {
|
||||
_stream = nullptr;
|
||||
}
|
||||
|
||||
TinselFile::~TinselFile() {
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
bool TinselFile::openInternal(const Common::Path &filename) {
|
||||
_stream = SearchMan.createReadStreamForMember(filename);
|
||||
if (!_stream)
|
||||
_stream = SearchMan.createReadStreamForMember(filename.append("."));
|
||||
return _stream != 0;
|
||||
}
|
||||
|
||||
bool TinselFile::open(const Common::String &filename) {
|
||||
if (openInternal(Common::Path(filename)))
|
||||
return true;
|
||||
|
||||
if (TinselVersion <= 1)
|
||||
return false;
|
||||
|
||||
// Check if the file being requested is the *1.* or *2.* files
|
||||
const char *fname = filename.c_str();
|
||||
const char *p = strchr(fname, '1');
|
||||
if (!p)
|
||||
p = strchr(fname, '2');
|
||||
if (!p || (*(p + 1) != '.'))
|
||||
return false;
|
||||
|
||||
// Form a filename without the CD number character
|
||||
char newFilename[50];
|
||||
strncpy(newFilename, fname, p - fname);
|
||||
Common::strcpy_s(newFilename + (p - fname), sizeof(newFilename) - (p - fname), p + 1);
|
||||
|
||||
return openInternal(newFilename);
|
||||
}
|
||||
|
||||
void TinselFile::close() {
|
||||
delete _stream;
|
||||
_stream = nullptr;
|
||||
}
|
||||
|
||||
int64 TinselFile::pos() const {
|
||||
assert(_stream);
|
||||
return _stream->pos();
|
||||
}
|
||||
|
||||
int64 TinselFile::size() const {
|
||||
assert(_stream);
|
||||
return _stream->size();
|
||||
}
|
||||
|
||||
bool TinselFile::seek(int64 offset, int whence) {
|
||||
assert(_stream);
|
||||
return _stream->seek(offset, whence);
|
||||
}
|
||||
|
||||
bool TinselFile::eos() const {
|
||||
assert(_stream);
|
||||
return _stream->eos();
|
||||
}
|
||||
|
||||
bool TinselFile::err() const {
|
||||
assert(_stream);
|
||||
return _stream->err();
|
||||
}
|
||||
|
||||
void TinselFile::clearErr() {
|
||||
assert(_stream);
|
||||
_stream->clearErr();
|
||||
}
|
||||
|
||||
uint32 TinselFile::read(void *dataPtr, uint32 dataSize) {
|
||||
assert(_stream);
|
||||
return _stream->read(dataPtr, dataSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
90
engines/tinsel/drives.h
Normal file
90
engines/tinsel/drives.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* CD/drive handling functions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_DRIVES_H
|
||||
#define TINSEL_DRIVES_H
|
||||
|
||||
#include "common/coroutines.h"
|
||||
#include "common/stream.h"
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Common {
|
||||
class Path;
|
||||
}
|
||||
|
||||
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 CdCD(CORO_PARAM);
|
||||
|
||||
int GetCurrentCD();
|
||||
|
||||
void SetCD(int flags);
|
||||
|
||||
int GetCD(int flags);
|
||||
|
||||
void SetNextCD(int cdNumber);
|
||||
|
||||
bool GotoCD();
|
||||
|
||||
class TinselFile : public Common::SeekableReadStream, public Common::ReadStreamEndian {
|
||||
private:
|
||||
static bool _warningShown;
|
||||
Common::SeekableReadStream *_stream;
|
||||
bool openInternal(const Common::Path &filename);
|
||||
public:
|
||||
// This constructor is only used for _sampleStream inside sound.h
|
||||
TinselFile();
|
||||
TinselFile(bool bigEndian);
|
||||
~TinselFile() override;
|
||||
bool open(const Common::String &filename);
|
||||
void close();
|
||||
char getCdNumber();
|
||||
|
||||
bool err() const override;
|
||||
void clearErr() override;
|
||||
|
||||
bool eos() const override;
|
||||
uint32 read(void *dataPtr, uint32 dataSize) override;
|
||||
|
||||
int64 pos() const override;
|
||||
int64 size() const override;
|
||||
bool seek(int64 offset, int whence = SEEK_SET) override;
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_DRIVES_H */
|
||||
117
engines/tinsel/dw.h
Normal file
117
engines/tinsel/dw.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_DW_H
|
||||
#define TINSEL_DW_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/** scene handle data type */
|
||||
typedef uint32 SCNHANDLE;
|
||||
|
||||
/** polygon handle */
|
||||
typedef int HPOLYGON;
|
||||
|
||||
|
||||
#define EOS_CHAR '\0' // string terminator
|
||||
#define LF_CHAR '\x0a' // line feed
|
||||
|
||||
// file names
|
||||
#define MIDI_FILE "midi.dat" // all MIDI sequences
|
||||
#define INDEX_FILENAME "index" // name of index file
|
||||
#define PSX_INDEX_FILENAME "index.dat" // name of index file in psx version
|
||||
|
||||
#define NO_SCNHANDLES 300 // number of memory handles for scenes
|
||||
#define MASTER_SCNHANDLE 0 // master scene memory handle
|
||||
|
||||
// the minimum value a integer number can have
|
||||
#define MIN_INT (1 << (8*sizeof(int) - 1))
|
||||
#define MIN_INT16 (-32767)
|
||||
|
||||
// the maximum value a integer number can have
|
||||
#define MAX_INT (~MIN_INT)
|
||||
|
||||
// inventory object handle (if there are inventory objects)
|
||||
#define INV_OBJ_SCNHANDLE ((TinselVersion == 0) ? (2 << SCNHANDLE_SHIFT) : (1 << SCNHANDLE_SHIFT))
|
||||
|
||||
#define FIELD_WORLD ((TinselVersion == 3) ? 2 : 0)
|
||||
#define FIELD_STATUS ((TinselVersion == 3) ? 8 : 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
|
||||
|
||||
#define Z_INV_BRECT 10 // Inventory background rectangle
|
||||
#define Z_INV_MFRAME ((TinselVersion == 3) ? 16 : 15) // Inventory window frame
|
||||
#define Z_INV_HTEXT 15 // Inventory heading text
|
||||
#define Z_INV_ICONS 16 // Icons in inventory
|
||||
#define Z_INV_ITEXT 995 // Icon text
|
||||
|
||||
#define Z_INV_RFRAME 22 // Re-sizing frame
|
||||
|
||||
#define Z_CURSOR 1000 // Cursor
|
||||
#define Z_CURSORTRAIL 999 // Cursor trails
|
||||
#define Z_ACURSOR 990 // Auxillary cursor
|
||||
|
||||
#define Z_TAG_TEXT 995 // In front of auxiliary cursor
|
||||
|
||||
#define Z_MDGROOVE 20
|
||||
#define Z_MDSLIDER 21
|
||||
|
||||
#define Z_TOPPLAY 100
|
||||
|
||||
#define Z_TOPW_TEXT Z_TAG_TEXT
|
||||
|
||||
// Started a collection of assorted maximum numbers here:
|
||||
// TODO: Noir only has two movers - deal with that
|
||||
#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-existent entrance number for LoadScene()
|
||||
#define NO_ENTRY_NUM (-3458) // Magic unlikely number
|
||||
|
||||
|
||||
#define SAMPLETIMEOUT (20*ONE_SECOND)
|
||||
|
||||
// Language for the resource strings
|
||||
enum LANGUAGE {
|
||||
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
|
||||
139
engines/tinsel/effect.cpp
Normal file
139
engines/tinsel/effect.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Handles effect polygons.
|
||||
//
|
||||
// EffectPolyProcess() monitors triggering of effect code (i.e. a moving
|
||||
// actor entering an effect polygon).
|
||||
// EffectProcess() runs the appropriate effect code.
|
||||
//
|
||||
// NOTE: Currently will only run one effect process at a time, i.e.
|
||||
// effect polygons will not currently nest. It won't be very difficult
|
||||
// to fix this if required.
|
||||
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/events.h"
|
||||
#include "tinsel/pid.h"
|
||||
#include "tinsel/pcode.h" // LEAD_ACTOR
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/movers.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct EP_INIT {
|
||||
HPOLYGON hEpoly;
|
||||
MOVER *pMover;
|
||||
int index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs an effect polygon's Glitter code with ENTER event, waits for the
|
||||
* actor to leave that polygon. Then runs the polygon's Glitter code
|
||||
* with LEAVE event.
|
||||
*/
|
||||
static void EffectProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
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
|
||||
if (TinselVersion >= 2)
|
||||
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);
|
||||
GetMoverPosition(to->pMover, &x, &y);
|
||||
} while (InPolygon(x, y, EFFECT) == to->hEpoly);
|
||||
|
||||
// Run effect poly leave script
|
||||
if (TinselVersion >= 2)
|
||||
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKOUT,
|
||||
GetMoverId(to->pMover), false, 0));
|
||||
else
|
||||
effRunPolyTinselCode(to->hEpoly, WALKOUT, to->pMover->actorID);
|
||||
|
||||
SetMoverInEffect(to->index, false);
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the actor was not already in an effect polygon, checks to see if
|
||||
* 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, MOVER *pActor) {
|
||||
HPOLYGON hPoly;
|
||||
EP_INIT epi;
|
||||
|
||||
// If just entered an effect polygon, the effect should be triggered.
|
||||
if (!IsMAinEffectPoly(index)) {
|
||||
hPoly = InPolygon(x, y, EFFECT);
|
||||
if (hPoly != NOPOLY) {
|
||||
//Just entered effect polygon
|
||||
SetMoverInEffect(index, true);
|
||||
|
||||
epi.hEpoly = hPoly;
|
||||
epi.pMover = pActor;
|
||||
epi.index = index;
|
||||
CoroScheduler.createProcess(PID_TCODE, EffectProcess, &epi, sizeof(epi));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Just calls FettleEffectPolys() every clock tick.
|
||||
*/
|
||||
void EffectPolyProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
while (1) {
|
||||
for (int i = 0; i < MAX_MOVERS; i++) {
|
||||
MOVER *pActor = GetLiveMover(i);
|
||||
if (pActor != NULL) {
|
||||
int x, y;
|
||||
GetMoverPosition(pActor, &x, &y);
|
||||
FettleEffectPolys(x, y, i, pActor);
|
||||
}
|
||||
}
|
||||
|
||||
CORO_SLEEP(1); // allow re-scheduling
|
||||
}
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
726
engines/tinsel/events.cpp
Normal file
726
engines/tinsel/events.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Main purpose is to process user events.
|
||||
* Also provides a couple of utility functions.
|
||||
*/
|
||||
|
||||
#include "common/coroutines.h"
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/config.h"
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/events.h"
|
||||
#include "tinsel/handle.h" // For LockMem()
|
||||
#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/movers.h" // For walking lead actor
|
||||
#include "tinsel/sched.h"
|
||||
#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"
|
||||
#include "tinsel/noir/notebook.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- EXTERNAL FUNCTIONS ---------------------
|
||||
|
||||
// in PDISPLAY.C
|
||||
extern int GetTaggedActor();
|
||||
extern HPOLYGON GetTaggedPoly();
|
||||
|
||||
//----------------- EXTERNAL GLOBAL DATA ---------------------
|
||||
|
||||
extern bool g_bEnableMenu;
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
static uint32 g_lastUserEvent = 0; // Time it happened
|
||||
static int g_leftEvents = 0; // Single or double, left or right. Or escape key.
|
||||
static int g_escEvents = 1; // Escape key
|
||||
static int g_userEvents = 0; // Whenever a button or a key comes in
|
||||
|
||||
static int g_eCount = 0;
|
||||
|
||||
static int g_controlState;
|
||||
static bool g_bStartOff;
|
||||
|
||||
static int g_controlX, g_controlY;
|
||||
static bool g_bProvNotProcessed = false;
|
||||
|
||||
static uint32 lastRealAction = 0;
|
||||
|
||||
void ResetVarsEvents() {
|
||||
g_lastUserEvent = 0;
|
||||
g_leftEvents = 0; // Single or double, left or right. Or escape key.
|
||||
g_escEvents = 1; // Escape key
|
||||
g_userEvents = 0; // Whenever a button or a key comes in
|
||||
|
||||
g_eCount = 0;
|
||||
|
||||
g_controlState = 0;
|
||||
g_bStartOff = false;
|
||||
|
||||
g_controlX = 0;
|
||||
g_controlY = 0;
|
||||
g_bProvNotProcessed = false;
|
||||
|
||||
lastRealAction = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called before each schedule, only 1 user action per schedule
|
||||
* is allowed.
|
||||
*/
|
||||
void ResetEcount() {
|
||||
g_eCount = 0;
|
||||
}
|
||||
|
||||
void IncUserEvents() {
|
||||
g_userEvents++;
|
||||
g_lastUserEvent = DwGetCurrentTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a single click, wait to check it's not the first half of a
|
||||
* double click.
|
||||
* If this is a double click, the process from the waiting single click
|
||||
* gets killed.
|
||||
*/
|
||||
void AllowDclick(CORO_PARAM, PLR_EVENT be) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
if (be == PLR_SLEFT) {
|
||||
GetToken(TOKEN_LEFT_BUT);
|
||||
CORO_SLEEP(_vm->_config->_dclickSpeed+1);
|
||||
FreeToken(TOKEN_LEFT_BUT);
|
||||
|
||||
// Prevent activation of 2 events on the same tick
|
||||
if (++g_eCount != 1)
|
||||
CORO_KILL_SELF();
|
||||
|
||||
break;
|
||||
|
||||
} else if (be == PLR_DLEFT) {
|
||||
GetToken(TOKEN_LEFT_BUT);
|
||||
FreeToken(TOKEN_LEFT_BUT);
|
||||
}
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enables user control
|
||||
*/
|
||||
void ControlOn() {
|
||||
if (TinselVersion <= 1) {
|
||||
Control(CONTROL_ON);
|
||||
return;
|
||||
}
|
||||
|
||||
g_bEnableMenu = false;
|
||||
|
||||
if (g_controlState == CONTROL_OFF) {
|
||||
// Control is on
|
||||
g_controlState = CONTROL_ON;
|
||||
|
||||
// Restore cursor to where it was
|
||||
if (g_bStartOff == true)
|
||||
g_bStartOff = false;
|
||||
else
|
||||
_vm->_cursor->SetCursorXY(g_controlX, g_controlY);
|
||||
|
||||
// Re-instate cursor
|
||||
_vm->_cursor->UnHideCursor();
|
||||
|
||||
// Turn tags back on
|
||||
if (!_vm->_dialogs->inventoryOrNotebookActive())
|
||||
EnableTags();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes control from the user
|
||||
*/
|
||||
void ControlOff() {
|
||||
if (TinselVersion <= 1) {
|
||||
Control(CONTROL_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
g_bEnableMenu = false;
|
||||
|
||||
if (g_controlState == CONTROL_ON) {
|
||||
// Control is off
|
||||
g_controlState = CONTROL_OFF;
|
||||
|
||||
// Store cursor position
|
||||
_vm->_cursor->GetCursorXY(&g_controlX, &g_controlY, true);
|
||||
|
||||
// Blank out cursor
|
||||
_vm->_cursor->DwHideCursor();
|
||||
|
||||
// Switch off tags
|
||||
DisableTags();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent tags and cursor re-appearing
|
||||
*/
|
||||
void ControlStartOff() {
|
||||
if (TinselVersion <= 1) {
|
||||
Control(CONTROL_STARTOFF);
|
||||
return;
|
||||
}
|
||||
|
||||
g_bEnableMenu = false;
|
||||
|
||||
// Control is off
|
||||
g_controlState = CONTROL_OFF;
|
||||
|
||||
// Blank out cursor
|
||||
_vm->_cursor->DwHideCursor();
|
||||
|
||||
// Switch off tags
|
||||
DisableTags();
|
||||
|
||||
g_bStartOff = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take control from player, if the player has it.
|
||||
* Return TRUE if control taken, FALSE if not.
|
||||
*/
|
||||
bool GetControl(int param) {
|
||||
if (TinselVersion >= 2)
|
||||
return GetControl();
|
||||
|
||||
else if (TestToken(TOKEN_CONTROL)) {
|
||||
Control(param);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetControl() {
|
||||
if (g_controlState == CONTROL_ON) {
|
||||
ControlOff();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControlIsOn() {
|
||||
if (TinselVersion >= 2)
|
||||
return (g_controlState == CONTROL_ON);
|
||||
|
||||
return TestToken(TOKEN_CONTROL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
struct WP_INIT {
|
||||
int x; // } Where to walk to
|
||||
int y; // }
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a walk directly initiated by a click.
|
||||
*/
|
||||
static void WalkProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
MOVER *pMover;
|
||||
int thisWalk;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
const WP_INIT *to = (const WP_INIT *)param; // get the co-ordinates - copied to process when it was created
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
_ctx->pMover = GetMover(LEAD_ACTOR);
|
||||
|
||||
if ((TinselVersion >= 2) && 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);
|
||||
_vm->_scroll->DontScrollCursor();
|
||||
|
||||
while (MoverMoving(_ctx->pMover) && (_ctx->thisWalk == GetWalkNumber(_ctx->pMover)))
|
||||
CORO_SLEEP(1);
|
||||
|
||||
} else if ((TinselVersion <= 1) && _ctx->pMover->bActive) {
|
||||
assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path
|
||||
|
||||
GetToken(TOKEN_LEAD);
|
||||
SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
|
||||
_vm->_scroll->DontScrollCursor();
|
||||
|
||||
while (MoverMoving(_ctx->pMover))
|
||||
CORO_SLEEP(1);
|
||||
|
||||
FreeToken(TOKEN_LEAD);
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
void WalkTo(int x, int y) {
|
||||
WP_INIT to = { x, y };
|
||||
|
||||
CoroScheduler.createProcess(PID_TCODE, WalkProcess, &to, sizeof(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run appropriate actor or polygon glitter code.
|
||||
* If none, and it's a WALKTO event, do a walk.
|
||||
*/
|
||||
static void ProcessUserEvent(TINSEL_EVENT uEvent, const Common::Point &coOrds, PLR_EVENT be = PLR_NOEVENT) {
|
||||
int actor;
|
||||
int aniX, aniY;
|
||||
HPOLYGON hPoly;
|
||||
|
||||
// Prevent activation of 2 events on the same tick
|
||||
if (++g_eCount != 1)
|
||||
return;
|
||||
|
||||
if ((actor = GetTaggedActor()) != 0) {
|
||||
// Event for a tagged actor
|
||||
if (TinselVersion >= 2)
|
||||
ActorEvent(Common::nullContext, actor, uEvent, false, 0);
|
||||
else
|
||||
ActorEvent(actor, uEvent, be);
|
||||
} else if ((hPoly = GetTaggedPoly()) != NOPOLY) {
|
||||
// Event for active tagged polygon
|
||||
if (TinselVersion <= 1)
|
||||
RunPolyTinselCode(hPoly, uEvent, be, false);
|
||||
else if (uEvent != PROV_WALKTO)
|
||||
PolygonEvent(Common::nullContext, hPoly, uEvent, 0, false, 0);
|
||||
|
||||
} else {
|
||||
_vm->_cursor->GetCursorXY(&aniX, &aniY, true);
|
||||
|
||||
// There could be a poly involved which has no tag.
|
||||
if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY ||
|
||||
((TinselVersion <= 1) && ((hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY))) {
|
||||
if ((TinselVersion >= 2) && (uEvent != PROV_WALKTO))
|
||||
PolygonEvent(Common::nullContext, hPoly, uEvent, 0, false, 0);
|
||||
else if (TinselVersion <= 1)
|
||||
RunPolyTinselCode(hPoly, uEvent, be, false);
|
||||
} else if ((uEvent == PROV_WALKTO) || (uEvent == WALKTO)) {
|
||||
if (TinselVersion >= 2)
|
||||
ProcessedProvisional();
|
||||
WalkTo(aniX, aniY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ProcessButEvent
|
||||
*/
|
||||
void ProcessButEvent(PLR_EVENT be) {
|
||||
if (_vm->_config->_swapButtons) {
|
||||
switch (be) {
|
||||
case PLR_SLEFT:
|
||||
be = PLR_SRIGHT;
|
||||
break;
|
||||
case PLR_DLEFT:
|
||||
be = PLR_DRIGHT;
|
||||
break;
|
||||
case PLR_SRIGHT:
|
||||
be = PLR_SLEFT;
|
||||
break;
|
||||
case PLR_DRIGHT:
|
||||
be = PLR_DLEFT;
|
||||
break;
|
||||
case PLR_DRAG1_START:
|
||||
be = PLR_DRAG2_START;
|
||||
break;
|
||||
case PLR_DRAG1_END:
|
||||
be = PLR_DRAG2_END;
|
||||
break;
|
||||
case PLR_DRAG2_START:
|
||||
be = PLR_DRAG1_START;
|
||||
break;
|
||||
case PLR_DRAG2_END:
|
||||
be = PLR_DRAG1_END;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerEvent(be, _vm->getMousePosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* ProcessKeyEvent
|
||||
*/
|
||||
|
||||
void ProcessKeyEvent(PLR_EVENT ke) {
|
||||
// Pass the keyboard event to the player event handler
|
||||
int xp, yp;
|
||||
_vm->_cursor->GetCursorXYNoWait(&xp, &yp, true);
|
||||
const Common::Point mousePos(xp, yp);
|
||||
|
||||
PlayerEvent(ke, mousePos);
|
||||
}
|
||||
|
||||
#define REAL_ACTION_CHECK if (TinselVersion >= 2) { \
|
||||
if (DwGetCurrentTime() - lastRealAction < 4) return; \
|
||||
lastRealAction = DwGetCurrentTime(); \
|
||||
}
|
||||
|
||||
void CloseOpenInventories() {
|
||||
if (_vm->_notebook->isOpen()) {
|
||||
_vm->_notebook->close();
|
||||
} else {
|
||||
if (_vm->_dialogs->inventoryActive()) {
|
||||
if (_vm->_dialogs->whichInventoryOpen() != INV_3) {
|
||||
_vm->_dialogs->killInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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", "PLR_SAVE", "PLR_LOAD", "PLR_WHEEL_UP",
|
||||
"PLR_WHEEL_DOWN", "PLR_INVENTORY", "PLR_NOTEBOOK" };
|
||||
debugC(DEBUG_BASIC, kTinselDebugActions, "%s - (%d,%d)",
|
||||
actionList[pEvent], coOrds.x, coOrds.y);
|
||||
|
||||
// This stuff to allow F1 key during startup.
|
||||
if (g_bEnableMenu && pEvent == PLR_MENU)
|
||||
Control(CONTROL_ON);
|
||||
else
|
||||
IncUserEvents();
|
||||
|
||||
if (pEvent == PLR_ESCAPE) {
|
||||
++g_escEvents;
|
||||
++g_leftEvents; // Yes, I do mean this
|
||||
} else if ((pEvent == PLR_PROV_WALKTO)
|
||||
|| (pEvent == PLR_WALKTO)
|
||||
|| (pEvent == PLR_LOOK)
|
||||
|| (pEvent == PLR_ACTION)) {
|
||||
++g_leftEvents;
|
||||
}
|
||||
|
||||
// Only allow events if player control is on
|
||||
if (!ControlIsOn() && (pEvent != PLR_DRAG1_END))
|
||||
return;
|
||||
|
||||
if ((TinselVersion >= 2) && _vm->_dialogs->inventoryOrNotebookActive()) {
|
||||
int x, y;
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &x, &y);
|
||||
_vm->_dialogs->eventToInventory(pEvent, Common::Point(coOrds.x - x, coOrds.y - y));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pEvent) {
|
||||
case PLR_QUIT:
|
||||
_vm->_dialogs->openMenu(QUIT_MENU);
|
||||
break;
|
||||
|
||||
case PLR_MENU:
|
||||
if (TinselVersion == 3) {
|
||||
CloseOpenInventories();
|
||||
}
|
||||
_vm->_dialogs->openMenu(MAIN_MENU);
|
||||
break;
|
||||
|
||||
case PLR_INVENTORY:
|
||||
if (TinselVersion == 3) {
|
||||
CloseOpenInventories();
|
||||
_vm->_dialogs->popUpInventory(INV_1);
|
||||
}
|
||||
break;
|
||||
|
||||
case PLR_NOTEBOOK:
|
||||
if (TinselVersion == 3) {
|
||||
CloseOpenInventories();
|
||||
_vm->_notebook->show(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case PLR_JUMP:
|
||||
_vm->_dialogs->openMenu(HOPPER_MENU1);
|
||||
break;
|
||||
|
||||
case PLR_SAVE:
|
||||
if (TinselVersion == 3) {
|
||||
CloseOpenInventories();
|
||||
}
|
||||
_vm->_dialogs->openMenu(SAVE_MENU);
|
||||
break;
|
||||
|
||||
case PLR_LOAD:
|
||||
if (TinselVersion == 3) {
|
||||
CloseOpenInventories();
|
||||
}
|
||||
_vm->_dialogs->openMenu(LOAD_MENU);
|
||||
break;
|
||||
|
||||
case PLR_PROV_WALKTO: // Provisional WALKTO !
|
||||
ProcessUserEvent(PROV_WALKTO, coOrds);
|
||||
break;
|
||||
|
||||
case PLR_WALKTO:
|
||||
REAL_ACTION_CHECK;
|
||||
|
||||
if ((TinselVersion >= 2) || !_vm->_dialogs->inventoryActive())
|
||||
ProcessUserEvent(WALKTO, coOrds, PLR_SLEFT);
|
||||
else
|
||||
_vm->_dialogs->eventToInventory(PLR_SLEFT, coOrds);
|
||||
break;
|
||||
|
||||
case PLR_ACTION:
|
||||
REAL_ACTION_CHECK;
|
||||
|
||||
if ((TinselVersion >= 2) || !_vm->_dialogs->inventoryActive())
|
||||
ProcessUserEvent(ACTION, coOrds, PLR_DLEFT);
|
||||
else
|
||||
_vm->_dialogs->eventToInventory(PLR_DLEFT, coOrds);
|
||||
break;
|
||||
|
||||
case PLR_LOOK:
|
||||
REAL_ACTION_CHECK;
|
||||
|
||||
if ((TinselVersion >= 2) || !_vm->_dialogs->inventoryActive())
|
||||
ProcessUserEvent(LOOK, coOrds, PLR_SRIGHT);
|
||||
else
|
||||
_vm->_dialogs->eventToInventory(PLR_SRIGHT, coOrds);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_vm->_dialogs->inventoryActive())
|
||||
_vm->_dialogs->eventToInventory(pEvent, coOrds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For ESCapable Glitter sequences
|
||||
*/
|
||||
int GetEscEvents() {
|
||||
return g_escEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* For cutting short talk()s etc.
|
||||
*/
|
||||
int GetLeftEvents() {
|
||||
return g_leftEvents;
|
||||
}
|
||||
|
||||
bool LeftEventChange(int myleftEvent) {
|
||||
if (g_leftEvents != myleftEvent) {
|
||||
ProcessedProvisional();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For waitkey() Glitter function
|
||||
*/
|
||||
int getUserEvents() {
|
||||
return g_userEvents;
|
||||
}
|
||||
|
||||
uint32 getUserEventTime() {
|
||||
return DwGetCurrentTime() - g_lastUserEvent;
|
||||
}
|
||||
|
||||
void resetUserEventTime() {
|
||||
g_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;
|
||||
INT_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 (TinselVersion >= 2) {
|
||||
|
||||
// Take control for CONVERSE events
|
||||
if (to->event == CONVERSE) {
|
||||
_ctx->bTookControl = GetControl();
|
||||
_vm->_dialogs->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();
|
||||
|
||||
_vm->_dialogs->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)
|
||||
_vm->_dialogs->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)
|
||||
_vm->_dialogs->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;
|
||||
Common::PPROCESS pProc;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
PTP_INIT to;
|
||||
|
||||
if (result)
|
||||
*result = false;
|
||||
to.hPoly = -1;
|
||||
to.event = tEvent;
|
||||
to.pic = InitInterpretContext(GS_POLYGON,
|
||||
GetPolyScript(hPoly),
|
||||
tEvent,
|
||||
hPoly, // Polygon
|
||||
actor, // Actor
|
||||
NULL, // No Object
|
||||
myEscape);
|
||||
if (to.pic != NULL) {
|
||||
_ctx->pProc = CoroScheduler.createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
|
||||
AttachInterpret(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(TinselVersion <= 1);
|
||||
CoroScheduler.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(TinselVersion <= 1);
|
||||
CoroScheduler.createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* If provisional event was processed, calling this prevents the
|
||||
* subsequent 'real' event.
|
||||
*/
|
||||
void ProcessedProvisional() {
|
||||
g_bProvNotProcessed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the bProvNotProcessed flag
|
||||
*/
|
||||
void ProvNotProcessed() {
|
||||
g_bProvNotProcessed = true;
|
||||
}
|
||||
|
||||
bool GetProvNotProcessed() {
|
||||
return g_bProvNotProcessed;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
146
engines/tinsel/events.h
Normal file
146
engines/tinsel/events.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* User events processing and utility functions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_EVENTS_H
|
||||
#define TINSEL_EVENTS_H
|
||||
|
||||
#include "common/coroutines.h"
|
||||
#include "common/rect.h"
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/*
|
||||
enum BUTEVENT {
|
||||
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 {
|
||||
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,
|
||||
PLR_WHEEL_UP = 19,
|
||||
PLR_WHEEL_DOWN = 20,
|
||||
|
||||
PLR_INVENTORY = 21,
|
||||
PLR_NOTEBOOK = 22,
|
||||
|
||||
// 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
|
||||
*/
|
||||
// ': int' because out-of-range values happen in DW2 and we do enum casts (PVS-Studio V1016)
|
||||
enum TINSEL_EVENT : int {
|
||||
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, PLR_EVENT be);
|
||||
bool GetControl(int param);
|
||||
bool GetControl();
|
||||
bool ControlIsOn();
|
||||
void ControlOn();
|
||||
void ControlOff();
|
||||
void ControlStartOff();
|
||||
|
||||
void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc);
|
||||
void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor);
|
||||
|
||||
void ProcessButEvent(PLR_EVENT be);
|
||||
void ProcessKeyEvent(PLR_EVENT ke);
|
||||
|
||||
|
||||
int GetEscEvents();
|
||||
int GetLeftEvents();
|
||||
bool LeftEventChange(int myleftEvent);
|
||||
|
||||
int getUserEvents();
|
||||
|
||||
uint32 getUserEventTime();
|
||||
void resetUserEventTime();
|
||||
|
||||
void ResetEcount();
|
||||
|
||||
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 ProvNotProcessed();
|
||||
bool GetProvNotProcessed();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_EVENTS_H */
|
||||
221
engines/tinsel/faders.cpp
Normal file
221
engines/tinsel/faders.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Palette Fader and Flasher processes.
|
||||
*/
|
||||
|
||||
#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 {
|
||||
|
||||
/** structure used by the "FadeProcess" process */
|
||||
struct FADE {
|
||||
const long *pColorMultTable; // list of fixed point color multipliers - terminated with negative entry
|
||||
PALQ *pPalQ; // palette queue entry to fade
|
||||
};
|
||||
|
||||
// fixed point fade multiplier tables
|
||||
//const long fadeout[] = {0xf000, 0xd000, 0xb000, 0x9000, 0x7000, 0x5000, 0x3000, 0x1000, 0, -1};
|
||||
//const long fadein[] = {0, 0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xb000, 0xd000, 0x10000L, -1};
|
||||
|
||||
/**
|
||||
* Scale 'color' by the fixed point color multiplier 'colorMult'
|
||||
* @param color Color to scale
|
||||
* @param colorMult Fixed point multiplier
|
||||
*/
|
||||
static COLORREF ScaleColor(COLORREF color, uint32 colorMult) {
|
||||
// apply multiplier to RGB components
|
||||
byte r = (byte)(color & 0xFF);
|
||||
byte g = (byte)((color >> 8) & 0xFF);
|
||||
byte b = (byte)((color >> 16) & 0xFF);
|
||||
uint32 red = ((r * colorMult) << 8) >> 24;
|
||||
uint32 green = ((g * colorMult) << 8) >> 24;
|
||||
uint32 blue = ((b * colorMult) << 8) >> 24;
|
||||
|
||||
// return new color
|
||||
return TINSEL_RGB(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the fixed point multiplier 'mult' to all colors in
|
||||
* 'pOrig' to produce 'pNew'. Each color in the palette will be
|
||||
* multiplied by 'mult'.
|
||||
* @param pNew Pointer to new palette
|
||||
* @param pOrig Pointer to original palette
|
||||
* @param numColors Number of colors in the above palettes
|
||||
* @param mult Fixed point multiplier
|
||||
*/
|
||||
static void FadePalette(COLORREF *pNew, COLORREF *pOrig, int numColors, uint32 mult) {
|
||||
for (int i = 0; i < numColors; i++, pNew++, pOrig++) {
|
||||
if (TinselVersion <= 1)
|
||||
// apply multiplier to RGB components
|
||||
*pNew = ScaleColor(*pOrig, mult);
|
||||
else if (i == (TalkColor() - 1)) {
|
||||
*pNew = GetTalkColorRef();
|
||||
*pNew = ScaleColor(*pNew, mult);
|
||||
} else if (SysVar(SV_TAGCOLOR) && i == (SysVar(SV_TAGCOLOR) - 1)) {
|
||||
*pNew = GetTagColorRef();
|
||||
*pNew = ScaleColor(*pNew, mult);
|
||||
} else {
|
||||
*pNew = ScaleColor(*pOrig, mult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process to fade one palette.
|
||||
* A pointer to a 'FADE' structure must be passed to this process when
|
||||
* it is created.
|
||||
*/
|
||||
static void FadeProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
COLORREF fadeRGB[MAX_COLORS]; // local copy of palette
|
||||
const long *pColMult; // pointer to color multiplier table
|
||||
PALETTE *pPalette; // pointer to palette
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
// get the fade data structure - copied to process when it was created
|
||||
const FADE *pFade = (const FADE *)param;
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
// Note that this palette is being faded
|
||||
FadingPalette(pFade->pPalQ, true);
|
||||
|
||||
// get pointer to palette - reduce pointer indirection a bit
|
||||
_ctx->pPalette = _vm->_handle->GetPalette(pFade->pPalQ->hPal);
|
||||
|
||||
for (_ctx->pColMult = pFade->pColorMultTable; *_ctx->pColMult >= 0; _ctx->pColMult++) {
|
||||
// go through all multipliers in table - until a negative entry
|
||||
|
||||
// fade palette using next multiplier
|
||||
if (TinselVersion >= 2)
|
||||
FadePalette(_ctx->fadeRGB, pFade->pPalQ->palRGB,
|
||||
pFade->pPalQ->numColors, (uint32) *_ctx->pColMult);
|
||||
else
|
||||
FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB,
|
||||
_ctx->pPalette->numColors, (uint32) *_ctx->pColMult);
|
||||
|
||||
// send new palette to video DAC
|
||||
UpdateDACqueue(pFade->pPalQ->posInDAC, _ctx->pPalette->numColors, _ctx->fadeRGB);
|
||||
|
||||
// allow time for video DAC to be updated
|
||||
CORO_SLEEP(1);
|
||||
}
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
// Note that this palette is being faded
|
||||
FadingPalette(pFade->pPalQ, false);
|
||||
|
||||
delete _ctx->pPalette;
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic palette fader/unfader. Creates a 'FadeProcess' process
|
||||
* for each palette that is to fade.
|
||||
* @param multTable Fixed point color multiplier table
|
||||
*/
|
||||
static void Fader(const long multTable[]) {
|
||||
PALQ *pPal; // palette manager iterator
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// The is only ever one cuncurrent fade
|
||||
// But this could be a fade out and the fade in is still going!
|
||||
CoroScheduler.killMatchingProcess(PID_FADER);
|
||||
NoFadingPalettes();
|
||||
}
|
||||
|
||||
// create a process for each palette in the palette queue
|
||||
for (pPal = GetNextPalette(NULL); pPal != NULL; pPal = GetNextPalette(pPal)) {
|
||||
FADE fade;
|
||||
|
||||
// fill in FADE struct
|
||||
fade.pColorMultTable = multTable;
|
||||
fade.pPalQ = pPal;
|
||||
|
||||
// create a fader process for this palette
|
||||
CoroScheduler.createProcess(PID_FADER, FadeProcess, (void *)&fade, sizeof(FADE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades a list of palettes down to black.
|
||||
*/
|
||||
void FadeOutMedium() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades a list of palettes down to black.
|
||||
*/
|
||||
void FadeOutFast() {
|
||||
// Fixed point fade multiplier table
|
||||
static const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
|
||||
|
||||
// call generic fader
|
||||
Fader(fadeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades a list of palettes from black to their current colors.
|
||||
*/
|
||||
void FadeInMedium() {
|
||||
// Fade multiplier table
|
||||
static const long fadein[] = {0, 0x1a00, 0x3400, 0x4e00, 0x6800,
|
||||
0x8200, 0x9c00, 0xb600, 0xd000, 0xea00, 0x10000L, -1};
|
||||
|
||||
// call generic fader
|
||||
Fader(fadein);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades a list of palettes from black to their current colors.
|
||||
*/
|
||||
void FadeInFast() {
|
||||
// Fade multiplier table
|
||||
static const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
|
||||
|
||||
// call generic fader
|
||||
Fader(fadein);
|
||||
}
|
||||
|
||||
void PokeInTagColor() {
|
||||
if (SysVar(SV_TAGCOLOR)) {
|
||||
const COLORREF c = _vm->_actor->GetActorRGB(-1);
|
||||
UpdateDACqueue(SysVar(SV_TAGCOLOR), c);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
49
engines/tinsel/faders.h
Normal file
49
engines/tinsel/faders.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Data structures used by the fader and flasher processes
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_FADERS_H // prevent multiple includes
|
||||
#define TINSEL_FADERS_H
|
||||
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/**
|
||||
* 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 *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
void FadeOutMedium();
|
||||
void FadeOutFast();
|
||||
void FadeInMedium();
|
||||
void FadeInFast();
|
||||
void PokeInTagColor();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_FADERS_H
|
||||
36
engines/tinsel/film.cpp
Normal file
36
engines/tinsel/film.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
MULTI_INIT *FREEL::GetMultiInit() {
|
||||
return (MULTI_INIT *)_vm->_handle->LockMem(FROM_32(mobj));
|
||||
}
|
||||
|
||||
const MULTI_INIT *FREEL::GetMultiInit() const {
|
||||
return (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(mobj));
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
50
engines/tinsel/film.h
Normal file
50
engines/tinsel/film.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_FILM_H // prevent multiple includes
|
||||
#define TINSEL_FILM_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
struct MULTI_INIT;
|
||||
struct FREEL {
|
||||
SCNHANDLE mobj;
|
||||
SCNHANDLE script;
|
||||
|
||||
MULTI_INIT *GetMultiInit();
|
||||
const MULTI_INIT *GetMultiInit() const;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct FILM {
|
||||
int32 frate;
|
||||
int32 numreels;
|
||||
FREEL reels[1];
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
61
engines/tinsel/font.cpp
Normal file
61
engines/tinsel/font.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 {
|
||||
|
||||
void Font::SetTagFontHandle(SCNHANDLE hFont) {
|
||||
_hTagFont = _hRegularTagFont = hFont;
|
||||
if (TinselVersion == 0)
|
||||
SetTalkFontHandle(hFont); // Also re-use for talk text
|
||||
}
|
||||
|
||||
SCNHANDLE Font::GetTalkFontHandle() {
|
||||
// FIXME: Mac Discworld 1 was rendering the talk font as black.
|
||||
// Currently hacking around it by simply using the tooltip font
|
||||
return TinselV1Mac ? _hTagFont : _hTalkFont;
|
||||
}
|
||||
|
||||
void Font::FettleFontPal(SCNHANDLE fontPal) {
|
||||
Handle *h = _vm->_handle;
|
||||
|
||||
assert(fontPal);
|
||||
assert(_hTagFont); // Tag font not declared
|
||||
assert(_hTalkFont); // Talk font not declared
|
||||
|
||||
h->SetImagePalette(h->GetFontImageHandle(_hTagFont), (TinselVersion <= 1) ? fontPal : 0); // get image for char 0
|
||||
h->SetImagePalette(h->GetFontImageHandle(_hTalkFont), (TinselVersion <= 1) ? fontPal : 0); // get image for char 0
|
||||
|
||||
if ((TinselVersion >= 2) && SysVar(SV_TAGCOLOR)) {
|
||||
const COLORREF c = _vm->_actor->GetActorRGB(-1);
|
||||
SetTagColorRef(c);
|
||||
UpdateDACqueue(SysVar(SV_TAGCOLOR), c);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
99
engines/tinsel/font.h
Normal file
99
engines/tinsel/font.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_FONT_H // prevent multiple includes
|
||||
#define TINSEL_FONT_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// A temporary buffer for extracting text into is defined in font.c
|
||||
// Accessed using TextBufferAddr(), this is how big it is:
|
||||
#define TBUFSZ 512
|
||||
|
||||
class Font {
|
||||
public:
|
||||
Font() : _hTagFont(0), _hTalkFont(0), _hRegularTalkFont(0), _hRegularTagFont(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return address of tBuffer
|
||||
*/
|
||||
char* TextBufferAddr() { return _tBuffer; }
|
||||
|
||||
/**
|
||||
* Return hTagFont handle.
|
||||
*/
|
||||
SCNHANDLE GetTagFontHandle() { return _hTagFont; }
|
||||
|
||||
/**
|
||||
* Return hTalkFont handle.
|
||||
*/
|
||||
SCNHANDLE GetTalkFontHandle();
|
||||
|
||||
/**
|
||||
* Called from dec_tagfont() Glitter function. Store the tag font handle.
|
||||
*/
|
||||
void SetTagFontHandle(SCNHANDLE hFont);
|
||||
|
||||
/**
|
||||
* Called from dec_talkfont() Glitter function.
|
||||
* Store the talk font handle.
|
||||
*/
|
||||
void SetTalkFontHandle(SCNHANDLE hFont) {
|
||||
_hTalkFont = _hRegularTalkFont = hFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare a temporary text font (DW2 only).
|
||||
*/
|
||||
void SetTempTagFontHandle(SCNHANDLE hFont) {
|
||||
_hTagFont = hFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare a temporary text font (DW2 only).
|
||||
*/
|
||||
void SetTempTalkFontHandle(SCNHANDLE hFont) {
|
||||
_hTalkFont = hFont;
|
||||
}
|
||||
|
||||
void ResetFontHandles() {
|
||||
_hTagFont = _hRegularTagFont;
|
||||
_hTalkFont = _hRegularTalkFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poke the background palette into character 0's images.
|
||||
*/
|
||||
void FettleFontPal(SCNHANDLE fontPal);
|
||||
|
||||
private:
|
||||
char _tBuffer[TBUFSZ];
|
||||
|
||||
SCNHANDLE _hTagFont, _hTalkFont;
|
||||
SCNHANDLE _hRegularTalkFont, _hRegularTagFont;
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_FONT_H
|
||||
1272
engines/tinsel/graphics.cpp
Normal file
1272
engines/tinsel/graphics.cpp
Normal file
File diff suppressed because it is too large
Load Diff
72
engines/tinsel/graphics.h
Normal file
72
engines/tinsel/graphics.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Low level graphics interface.
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_GRAPHICS_H // prevent multiple includes
|
||||
#define TINSEL_GRAPHICS_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/text.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct PALQ;
|
||||
|
||||
/** draw object structure - only used when drawing objects */
|
||||
struct DRAWOBJECT {
|
||||
char *charBase; // character set base address
|
||||
int transOffset; // transparent character offset
|
||||
int flags; // object flags - see above for list
|
||||
PALQ *pPal; // objects palette Q position
|
||||
short isRLE; // TinselVersion == 3, if image is using run-length encoding
|
||||
short colorFlags; // TinselV3, type of color blending
|
||||
int constant; // which color in palette for monochrome objects
|
||||
int width; // width of object
|
||||
int height; // height of object
|
||||
SCNHANDLE hBits; // image bitmap handle
|
||||
int lineoffset; // offset to next line
|
||||
int leftClip; // amount to clip off object left
|
||||
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
|
||||
uint32 baseCol; // For 4-bit stuff
|
||||
};
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
void ClearScreen();
|
||||
void DrawObject(DRAWOBJECT *pObj);
|
||||
|
||||
// called to update a rectangle on the video screen from a video page
|
||||
void UpdateScreenRect(const Common::Rect &pClip);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
659
engines/tinsel/handle.cpp
Normal file
659
engines/tinsel/handle.cpp
Normal file
@@ -0,0 +1,659 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains the handle based Memory Manager code
|
||||
*/
|
||||
|
||||
#define BODGE
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/drives.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/heapmem.h" // heap memory manager
|
||||
#include "tinsel/palette.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/timers.h" // for DwGetCurrentTime()
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/scene.h"
|
||||
#include "tinsel/noir/lzss.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
struct MEMHANDLE {
|
||||
char szName[12]; ///< file name of graphics file
|
||||
int32 filesize; ///< file size and flags
|
||||
MEM_NODE *_node; ///< memory node for the graphics
|
||||
uint32 flags2;
|
||||
};
|
||||
|
||||
|
||||
/** memory allocation flags - stored in the top bits of the filesize field */
|
||||
enum {
|
||||
fPreload = 0x01000000L, ///< preload memory
|
||||
fDiscard = 0x02000000L, ///< discard memory
|
||||
fSound = 0x04000000L, ///< sound data
|
||||
fGraphic = 0x08000000L, ///< graphic data
|
||||
fCompressed = 0x10000000L, ///< compressed data
|
||||
fLoaded = 0x20000000L, ///< set when file data has been loaded
|
||||
fUnknown = 0x40000000L ///< v3 specific
|
||||
};
|
||||
#define FSIZE_MASK ((TinselVersion == 3) ? 0xFFFFFFFFL : 0x00FFFFFFL) //!< mask to isolate the filesize
|
||||
#define MEMFLAGS(x) ((TinselVersion == 3) ? x->flags2 : x->filesize)
|
||||
#define MEMFLAGSET(x, mask) ((TinselVersion == 3) ? x->flags2 |= mask : x->filesize |= mask)
|
||||
|
||||
Handle::Handle() : _handleTable(0), _numHandles(0), _cdPlayHandle((uint32)-1), _cdBaseHandle(0), _cdTopHandle(0), _cdGraphStream(nullptr) {
|
||||
}
|
||||
|
||||
Handle::~Handle() {
|
||||
free(_handleTable);
|
||||
_handleTable = nullptr;
|
||||
|
||||
delete _cdGraphStream;
|
||||
_cdGraphStream = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the graphics handle table index file and preloads all the
|
||||
* permanent graphics etc.
|
||||
*/
|
||||
void Handle::SetupHandleTable() {
|
||||
bool t2Flag = TinselVersion >= 2;
|
||||
int RECORD_SIZE = t2Flag ? 24 : 20;
|
||||
|
||||
int len;
|
||||
uint i;
|
||||
MEMHANDLE *pH;
|
||||
TinselFile f(TinselV1Mac || TinselV1Saturn);
|
||||
|
||||
Common::String indexFileName = TinselV1PSX ? PSX_INDEX_FILENAME : INDEX_FILENAME;
|
||||
|
||||
if (Common::File::exists("index_"))
|
||||
indexFileName = "index_";
|
||||
|
||||
if (f.open(indexFileName)) {
|
||||
// get size of index file
|
||||
len = f.size();
|
||||
|
||||
if (len > 0) {
|
||||
if ((len % RECORD_SIZE) != 0) {
|
||||
// index file is corrupt
|
||||
error(FILE_IS_CORRUPT, indexFileName.c_str());
|
||||
}
|
||||
|
||||
// calc number of handles
|
||||
_numHandles = len / RECORD_SIZE;
|
||||
|
||||
// allocate memory for the index file
|
||||
_handleTable = (MEMHANDLE *)calloc(_numHandles, sizeof(struct MEMHANDLE));
|
||||
|
||||
// make sure memory allocated
|
||||
assert(_handleTable);
|
||||
|
||||
// load data
|
||||
for (i = 0; i < _numHandles; i++) {
|
||||
f.read(_handleTable[i].szName, 12);
|
||||
_handleTable[i].filesize = f.readUint32();
|
||||
// The pointer should always be NULL. We don't
|
||||
// need to read that from the file.
|
||||
_handleTable[i]._node= nullptr;
|
||||
f.seek(4, SEEK_CUR);
|
||||
// For Discworld 2, read in the flags2 field
|
||||
_handleTable[i].flags2 = t2Flag ? f.readUint32() : 0;
|
||||
}
|
||||
|
||||
if (f.eos() || f.err()) {
|
||||
// index file is corrupt
|
||||
error(FILE_IS_CORRUPT, indexFileName.c_str());
|
||||
}
|
||||
|
||||
// close the file
|
||||
f.close();
|
||||
} else { // index file is corrupt
|
||||
error(FILE_IS_CORRUPT, indexFileName.c_str());
|
||||
}
|
||||
} else { // cannot find the index file
|
||||
error(CANNOT_FIND_FILE, indexFileName.c_str());
|
||||
}
|
||||
|
||||
// allocate memory nodes and load all permanent graphics
|
||||
for (i = 0, pH = _handleTable; i < _numHandles; i++, pH++) {
|
||||
if (MEMFLAGS(pH) & fPreload) {
|
||||
// allocate a fixed memory node for permanent files
|
||||
pH->_node = MemoryAllocFixed((pH->filesize & FSIZE_MASK));
|
||||
|
||||
// make sure memory allocated
|
||||
assert(pH->_node);
|
||||
|
||||
// load the data
|
||||
LoadFile(pH);
|
||||
}
|
||||
#ifdef BODGE
|
||||
else if ((pH->filesize & FSIZE_MASK) == 8) {
|
||||
pH->_node= nullptr;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// allocate a discarded memory node for other files
|
||||
pH->_node = MemoryNoAlloc();
|
||||
|
||||
// make sure memory allocated
|
||||
assert(pH->_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a memory block as a file.
|
||||
*/
|
||||
void Handle::OpenCDGraphFile() {
|
||||
delete _cdGraphStream;
|
||||
|
||||
// As the theory goes, the right CD will be in there!
|
||||
|
||||
_cdGraphStream = new Common::File;
|
||||
if (!_cdGraphStream->open(_szCdPlayFile))
|
||||
error(CANNOT_FIND_FILE, _szCdPlayFile.toString().c_str());
|
||||
}
|
||||
|
||||
void Handle::LoadCDGraphData(MEMHANDLE *pH) {
|
||||
// read the data
|
||||
uint bytes;
|
||||
byte *addr;
|
||||
int retries = 0;
|
||||
|
||||
assert(!(pH->filesize & fCompressed));
|
||||
|
||||
// Can't be preloaded
|
||||
assert(!(MEMFLAGS(pH) & fPreload));
|
||||
|
||||
// discardable - lock the memory
|
||||
addr = (byte *)MemoryLock(pH->_node);
|
||||
|
||||
// make sure address is valid
|
||||
assert(addr);
|
||||
|
||||
// Move to correct place in file and load the required data
|
||||
assert(_cdGraphStream);
|
||||
_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->_node);
|
||||
|
||||
// set the loaded flag
|
||||
MEMFLAGSET(pH, fLoaded);
|
||||
|
||||
// clear the loading flag
|
||||
// pH->filesize &= ~fLoading;
|
||||
|
||||
if (bytes != ((_cdTopHandle - _cdBaseHandle) & OFFSETMASK))
|
||||
// file is corrupt
|
||||
error(FILE_READ_ERROR, "CD play file");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately 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 Handle::LoadExtraGraphData(SCNHANDLE start, SCNHANDLE next) {
|
||||
OpenCDGraphFile();
|
||||
|
||||
MemoryDiscard((_handleTable + _cdPlayHandle)->_node); // Free it
|
||||
|
||||
// It must always be the same
|
||||
assert(_cdPlayHandle == (start >> SCNHANDLE_SHIFT));
|
||||
assert(_cdPlayHandle == (next >> SCNHANDLE_SHIFT));
|
||||
|
||||
_cdBaseHandle = start;
|
||||
_cdTopHandle = next;
|
||||
}
|
||||
|
||||
void Handle::SetCdPlaySceneDetails(const char *fileName) {
|
||||
_szCdPlayFile = fileName;
|
||||
}
|
||||
|
||||
void Handle::SetCdPlayHandle(int fileNum) {
|
||||
_cdPlayHandle = fileNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a memory block as a file.
|
||||
* @param pH Memory block pointer
|
||||
*/
|
||||
void Handle::LoadFile(MEMHANDLE *pH) {
|
||||
Common::File f;
|
||||
char szFilename[sizeof(pH->szName) + 1];
|
||||
|
||||
// extract and zero terminate the filename
|
||||
memcpy(szFilename, pH->szName, sizeof(pH->szName));
|
||||
szFilename[sizeof(pH->szName)] = 0;
|
||||
|
||||
if ((TinselVersion != 3) && MEMFLAGS(pH) & fCompressed) {
|
||||
error("Compression handling has been removed - %s", szFilename);
|
||||
}
|
||||
|
||||
if (f.open(szFilename)) {
|
||||
// read the data
|
||||
int bytes;
|
||||
uint8 *addr;
|
||||
|
||||
// lock the memory
|
||||
addr = (uint8 *)MemoryLock(pH->_node);
|
||||
|
||||
// make sure address is valid
|
||||
assert(addr);
|
||||
|
||||
if ((TinselVersion == 3) && MEMFLAGS(pH) & fCompressed) {
|
||||
bytes = decompressLZSS(f, addr);
|
||||
} else {
|
||||
bytes = f.read(addr, pH->filesize & FSIZE_MASK);
|
||||
}
|
||||
|
||||
// close the file
|
||||
f.close();
|
||||
|
||||
// discardable - unlock the memory
|
||||
MemoryUnlock(pH->_node);
|
||||
|
||||
// set the loaded flag
|
||||
MEMFLAGSET(pH, fLoaded);
|
||||
|
||||
if (bytes == int(pH->filesize & FSIZE_MASK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// file is corrupt
|
||||
error(FILE_IS_CORRUPT, szFilename);
|
||||
}
|
||||
|
||||
// cannot find file
|
||||
error(CANNOT_FIND_FILE, szFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a font specified by a SCNHANDLE
|
||||
* Handles endianess internally
|
||||
* @param offset Handle and offset to data
|
||||
* @return FONT structure
|
||||
*/
|
||||
FONT *Handle::GetFont(SCNHANDLE offset) {
|
||||
byte *data = LockMem(offset);
|
||||
const bool isBE = TinselV1Mac || TinselV1Saturn;
|
||||
const uint32 size = ((TinselVersion == 3) ? 12 * 4 : 11 * 4) + 300 * 4; // FONT struct size
|
||||
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size, isBE);
|
||||
|
||||
FONT *font = new FONT();
|
||||
font->xSpacing = stream->readSint32();
|
||||
font->ySpacing = stream->readSint32();
|
||||
font->xShadow = stream->readSint32();
|
||||
font->yShadow = stream->readSint32();
|
||||
font->spaceSize = stream->readSint32();
|
||||
font->baseColor = (TinselVersion == 3) ? stream->readSint32() : 0;
|
||||
font->fontInit.hObjImg = stream->readUint32();
|
||||
font->fontInit.objFlags = stream->readSint32();
|
||||
font->fontInit.objID = stream->readSint32();
|
||||
font->fontInit.objX = stream->readSint32();
|
||||
font->fontInit.objY = stream->readSint32();
|
||||
font->fontInit.objZ = stream->readSint32();
|
||||
for (int i = 0; i < 300; i++)
|
||||
font->fontDef[i] = stream->readUint32();
|
||||
|
||||
delete stream;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a palette specified by a SCNHANDLE
|
||||
* Handles endianess internally
|
||||
* @param offset Handle and offset to data
|
||||
* @return PALETTE structure
|
||||
*/
|
||||
PALETTE *Handle::GetPalette(SCNHANDLE offset) {
|
||||
byte *data = LockMem(offset);
|
||||
const bool isBE = TinselV1Mac || TinselV1Saturn;
|
||||
const uint32 size = 4 + 256 * 4; // numColors + 256 COLORREF (max)
|
||||
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size, isBE);
|
||||
|
||||
PALETTE *pal = new PALETTE();
|
||||
|
||||
pal->numColors = stream->readSint32();
|
||||
for (int32 i = 0; i < pal->numColors; i++) {
|
||||
pal->palRGB[i] = stream->readUint32();
|
||||
|
||||
// get the RGB color model values
|
||||
pal->palette[i * 3] = (byte)(pal->palRGB[i] & 0xFF);
|
||||
pal->palette[i * 3 + 1] = (byte)((pal->palRGB[i] >> 8) & 0xFF);
|
||||
pal->palette[i * 3 + 2] = (byte)((pal->palRGB[i] >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
return pal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an image specified by a SCNHANDLE
|
||||
* Handles endianess internally
|
||||
* @param offset Handle and offset to data
|
||||
* @return IMAGE structure
|
||||
*/
|
||||
const IMAGE *Handle::GetImage(SCNHANDLE offset) {
|
||||
byte *data = LockMem(offset);
|
||||
const bool isBE = TinselV1Mac || TinselV1Saturn;
|
||||
const uint32 size = 16; // IMAGE struct size
|
||||
|
||||
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size, isBE);
|
||||
|
||||
IMAGE *img = new IMAGE();
|
||||
|
||||
img->imgWidth = stream->readSint16();
|
||||
img->imgHeight = stream->readUint16();
|
||||
img->anioffX = stream->readSint16();
|
||||
img->anioffY = stream->readSint16();
|
||||
img->hImgBits = stream->readUint32();
|
||||
|
||||
if (TinselVersion != 3) {
|
||||
img->hImgPal = stream->readUint32();
|
||||
} else {
|
||||
img->isRLE = stream->readSint16();
|
||||
img->colorFlags = stream->readSint16();
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
void Handle::SetImagePalette(SCNHANDLE offset, SCNHANDLE palHandle) {
|
||||
byte *img = LockMem(offset);
|
||||
WRITE_32(img + 12, palHandle); // hImgPal
|
||||
}
|
||||
|
||||
SCNHANDLE Handle::GetFontImageHandle(SCNHANDLE offset) {
|
||||
FONT *font = GetFont(offset);
|
||||
SCNHANDLE handle = font->fontInit.hObjImg;
|
||||
delete font;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an actor's data specified by a SCNHANDLE
|
||||
* Handles endianess internally
|
||||
* @param offset Handle and offset to data
|
||||
* @param count Data count
|
||||
* @return IMAGE structure
|
||||
*/
|
||||
const ACTORDATA *Handle::GetActorData(SCNHANDLE offset, uint32 count) {
|
||||
byte *data = LockMem(offset);
|
||||
const bool isBE = TinselV1Mac || TinselV1Saturn;
|
||||
const uint32 size = (TinselVersion >= 2) ? 20 : 12; // ACTORDATA struct size
|
||||
|
||||
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size * count, isBE);
|
||||
|
||||
ACTORDATA *actorData = new ACTORDATA[count];
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
if (TinselVersion <= 1) {
|
||||
actorData[i].masking = stream->readSint32();
|
||||
actorData[i].hActorId = stream->readUint32();
|
||||
actorData[i].hActorCode = stream->readUint32();
|
||||
} else {
|
||||
actorData[i].hActorId = stream->readUint32();
|
||||
actorData[i].hTagText = stream->readUint32();
|
||||
actorData[i].tagPortionV = stream->readSint32();
|
||||
actorData[i].tagPortionH = stream->readSint32();
|
||||
actorData[i].hActorCode = stream->readUint32();
|
||||
}
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
return actorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a process specified by a SCNHANDLE
|
||||
* Handles endianess internally
|
||||
* @param offset Handle and offset to data
|
||||
* @param count Data count
|
||||
* @return PROCESS_STRUC structure
|
||||
*/
|
||||
const PROCESS_STRUC *Handle::GetProcessData(SCNHANDLE offset, uint32 count) {
|
||||
byte *data = LockMem(offset);
|
||||
const bool isBE = TinselV1Mac || TinselV1Saturn;
|
||||
const uint32 size = 8; // PROCESS_STRUC struct size
|
||||
|
||||
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size * count, isBE);
|
||||
|
||||
PROCESS_STRUC *processData = new PROCESS_STRUC[count];
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
processData[i].processId = stream->readUint32();
|
||||
processData[i].hProcessCode = stream->readUint32();
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
return processData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and return the address specified by a SCNHANDLE.
|
||||
* @param offset Handle and offset to data
|
||||
*/
|
||||
byte *Handle::LockMem(SCNHANDLE offset) {
|
||||
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
|
||||
debug(8, "Locking offset of type %ld (%lx), offset %ld, handle %d", (offset & HANDLEMASK) >> SCNHANDLE_SHIFT, (offset & HANDLEMASK) >> SCNHANDLE_SHIFT, offset & OFFSETMASK, handle);
|
||||
MEMHANDLE *pH; // points to table entry
|
||||
|
||||
// range check the memory handle
|
||||
assert(handle < _numHandles);
|
||||
|
||||
pH = _handleTable + handle;
|
||||
|
||||
if (MEMFLAGS(pH) & fPreload) {
|
||||
// permanent files are already loaded, nothing to be done
|
||||
} else if (handle == _cdPlayHandle) {
|
||||
// Must be in currently loaded/loadable range
|
||||
if (offset < _cdBaseHandle || offset >= _cdTopHandle)
|
||||
error("Overlapping (in time) CD-plays");
|
||||
|
||||
// May have been discarded, if so, we have to reload
|
||||
if (!MemoryDeref(pH->_node)) {
|
||||
// Data was discarded, we have to reload
|
||||
MemoryReAlloc(pH->_node, _cdTopHandle - _cdBaseHandle);
|
||||
|
||||
LoadCDGraphData(pH);
|
||||
|
||||
// update the LRU time (new in this file)
|
||||
MemoryTouch(pH->_node);
|
||||
}
|
||||
|
||||
// make sure address is valid
|
||||
assert(MEMFLAGS(pH) & fLoaded);
|
||||
|
||||
offset -= _cdBaseHandle;
|
||||
} else {
|
||||
if (!MemoryDeref(pH->_node)) {
|
||||
// Data was discarded, we have to reload
|
||||
MemoryReAlloc(pH->_node, pH->filesize & FSIZE_MASK);
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
SetCD(pH->flags2 & fAllCds);
|
||||
CdCD(Common::nullContext);
|
||||
}
|
||||
LoadFile(pH);
|
||||
}
|
||||
|
||||
// make sure address is valid
|
||||
assert(MEMFLAGS(pH) & fLoaded);
|
||||
}
|
||||
|
||||
return MemoryDeref(pH->_node) + (offset & OFFSETMASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to lock the current scene and make it non-discardable.
|
||||
* @param offset Handle and offset to data
|
||||
*/
|
||||
void Handle::LockScene(SCNHANDLE offset) {
|
||||
|
||||
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
|
||||
MEMHANDLE *pH; // points to table entry
|
||||
|
||||
// range check the memory handle
|
||||
assert(handle < _numHandles);
|
||||
|
||||
pH = _handleTable + handle;
|
||||
|
||||
if ((MEMFLAGS(pH) & fPreload) == 0) {
|
||||
// Ensure the scene handle is allocated.
|
||||
MemoryReAlloc(pH->_node, pH->filesize & FSIZE_MASK);
|
||||
|
||||
// Now lock it to make sure it stays allocated and in a fixed position.
|
||||
MemoryLock(pH->_node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to make the current scene discardable again.
|
||||
* @param offset Handle and offset to data
|
||||
*/
|
||||
void Handle::UnlockScene(SCNHANDLE offset) {
|
||||
|
||||
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
|
||||
MEMHANDLE *pH; // points to table entry
|
||||
|
||||
// range check the memory handle
|
||||
assert(handle < _numHandles);
|
||||
|
||||
pH = _handleTable + handle;
|
||||
|
||||
if ((MEMFLAGS(pH) & fPreload) == 0) {
|
||||
// unlock the scene data
|
||||
MemoryUnlock(pH->_node);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
#ifdef BODGE
|
||||
|
||||
/**
|
||||
* Validates that a specified handle pointer is valid
|
||||
* @param offset Handle and offset to data
|
||||
*/
|
||||
bool Handle::ValidHandle(SCNHANDLE offset) {
|
||||
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
|
||||
MEMHANDLE *pH; // points to table entry
|
||||
|
||||
// range check the memory handle
|
||||
assert(handle < _numHandles);
|
||||
|
||||
pH = _handleTable + handle;
|
||||
|
||||
return (pH->filesize & FSIZE_MASK) != 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* TouchMem
|
||||
* @param offset Handle and offset to data
|
||||
*/
|
||||
void Handle::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!
|
||||
if (pH->_node)
|
||||
MemoryTouch(pH->_node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given handle is into the cd graph data
|
||||
* @param offset Handle and offset to data
|
||||
*/
|
||||
bool Handle::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 Handle::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 (TinselVersion <= 1)
|
||||
return 1;
|
||||
|
||||
return GetCD(pH->flags2 & fAllCds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a resource by name and returns the handle to it.
|
||||
*
|
||||
* @param fileName Name of the resource to search for
|
||||
*/
|
||||
SCNHANDLE Handle::FindLanguageSceneHandle(const char *fileName) {
|
||||
Common::String nameString{fileName};
|
||||
|
||||
for (uint i = 0; i < _numHandles; ++i) {
|
||||
if (nameString == Common::String{_handleTable[i].szName}) {
|
||||
return i << SCNHANDLE_SHIFT;
|
||||
}
|
||||
}
|
||||
error("Can't find handle for language scene\n");
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
108
engines/tinsel/handle.h
Normal file
108
engines/tinsel/handle.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Graphics Memory Manager data structures
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_HANDLE_H
|
||||
#define TINSEL_HANDLE_H
|
||||
|
||||
#include "common/path.h"
|
||||
#include "tinsel/dw.h" // new data types
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct FONT;
|
||||
struct MEMHANDLE;
|
||||
struct PALETTE;
|
||||
struct IMAGE;
|
||||
struct ACTORDATA;
|
||||
struct PROCESS_STRUC;
|
||||
|
||||
class Handle {
|
||||
public:
|
||||
Handle();
|
||||
virtual ~Handle();
|
||||
|
||||
/**
|
||||
* Loads the graphics handle table index file and preloads all the permanent graphics etc.
|
||||
*/
|
||||
void SetupHandleTable();
|
||||
|
||||
FONT *GetFont(SCNHANDLE offset);
|
||||
PALETTE *GetPalette(SCNHANDLE offset);
|
||||
const IMAGE *GetImage(SCNHANDLE offset);
|
||||
void SetImagePalette(SCNHANDLE offset, SCNHANDLE palHandle);
|
||||
SCNHANDLE GetFontImageHandle(SCNHANDLE offset);
|
||||
const ACTORDATA *GetActorData(SCNHANDLE offset, uint32 count);
|
||||
const PROCESS_STRUC *GetProcessData(SCNHANDLE offset, uint32 count);
|
||||
byte *LockMem(SCNHANDLE offset);
|
||||
|
||||
void LockScene(SCNHANDLE offset);
|
||||
void UnlockScene(SCNHANDLE offset);
|
||||
|
||||
bool IsCdPlayHandle(SCNHANDLE offset);
|
||||
|
||||
void TouchMem(SCNHANDLE offset);
|
||||
|
||||
// Called at scene startup
|
||||
void SetCdPlaySceneDetails(const char *fileName);
|
||||
|
||||
// Called at game startup
|
||||
void SetCdPlayHandle(int fileNum);
|
||||
|
||||
void LoadExtraGraphData(
|
||||
SCNHANDLE start, // Handle of start of range
|
||||
SCNHANDLE next); // Handle of end of range + 1
|
||||
|
||||
int CdNumber(SCNHANDLE offset);
|
||||
|
||||
// Noir
|
||||
SCNHANDLE FindLanguageSceneHandle(const char *fileName);
|
||||
|
||||
#ifdef BODGE
|
||||
bool ValidHandle(SCNHANDLE offset);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void LoadFile(MEMHANDLE *pH); // load a memory block as a file
|
||||
void OpenCDGraphFile();
|
||||
void LoadCDGraphData(MEMHANDLE *pH);
|
||||
|
||||
// handle table gets loaded from index file at runtime
|
||||
MEMHANDLE *_handleTable;
|
||||
|
||||
// number of handles in the handle table
|
||||
uint _numHandles;
|
||||
|
||||
uint32 _cdPlayHandle;
|
||||
|
||||
SCNHANDLE _cdBaseHandle, _cdTopHandle;
|
||||
Common::File *_cdGraphStream;
|
||||
|
||||
Common::Path _szCdPlayFile;
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_HANDLE_H
|
||||
478
engines/tinsel/heapmem.cpp
Normal file
478
engines/tinsel/heapmem.cpp
Normal file
@@ -0,0 +1,478 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains the handle based Memory Manager code.
|
||||
*/
|
||||
|
||||
#include "tinsel/heapmem.h"
|
||||
#include "tinsel/timers.h" // For DwGetCurrentTime
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
|
||||
#define NUM_MNODES 192 // the number of memory management nodes (was 128, then 192)
|
||||
|
||||
|
||||
// internal allocation flags
|
||||
#define DWM_USED 0x0001 ///< the objects memory block is in use
|
||||
#define DWM_DISCARDED 0x0002 ///< the objects memory block has been discarded
|
||||
#define DWM_LOCKED ((TinselVersion == 3) ? 0x0200 : 0x0004) ///< the objects memory block is locked
|
||||
#define DWM_SENTINEL 0x0008 ///< the objects memory block is a sentinel
|
||||
// Noir
|
||||
#define DWM_V3X20 0x0020 ///< unknown
|
||||
#define DWM_V3X4 0x0004 ///< unknown
|
||||
|
||||
struct MEM_NODE {
|
||||
MEM_NODE *pNext; // link to the next node in the list
|
||||
MEM_NODE *pPrev; // link to the previous node in the list
|
||||
uint8 *pBaseAddr; // base address of the memory object
|
||||
long size; // size of the memory object
|
||||
uint32 lruTime; // time when memory object was last accessed
|
||||
int flags; // allocation attributes
|
||||
};
|
||||
|
||||
|
||||
// Specifies the total amount of memory required for DW1 demo, DW1, or DW2 respectively.
|
||||
// Currently this is set at 5MB for the DW1 demo and DW1 and 10MB for DW2
|
||||
// This could probably be reduced somewhat
|
||||
// If the memory is not enough, the engine throws an "Out of memory" error in handle.cpp inside LockMem()
|
||||
static const uint32 MemoryPoolSize[4] = {5 * 1024 * 1024, 5 * 1024 * 1024, 10 * 1024 * 1024, 512 * 1024 * 1024};
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
// list of all memory nodes
|
||||
MEM_NODE g_mnodeList[NUM_MNODES];
|
||||
|
||||
// pointer to the linked list of free mnodes
|
||||
static MEM_NODE *g_pFreeMemNodes;
|
||||
|
||||
// list of all fixed memory nodes
|
||||
MEM_NODE g_s_fixedMnodesList[5];
|
||||
|
||||
// the mnode heap sentinel
|
||||
static MEM_NODE g_heapSentinel;
|
||||
|
||||
//
|
||||
static MEM_NODE *AllocMemNode();
|
||||
|
||||
#ifdef DEBUG
|
||||
static void MemoryStats() {
|
||||
int usedNodes = 0;
|
||||
int allocedNodes = 0;
|
||||
int lockedNodes = 0;
|
||||
int lockedSize = 0;
|
||||
int totalSize = 0;
|
||||
|
||||
const MEM_NODE *pHeap = &g_heapSentinel;
|
||||
MEM_NODE *pCur;
|
||||
|
||||
for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
|
||||
usedNodes++;
|
||||
totalSize += pCur->size;
|
||||
if (pCur->flags)
|
||||
allocedNodes++;
|
||||
if (pCur->flags & DWM_LOCKED) {
|
||||
lockedNodes++;
|
||||
lockedSize += pCur->size;
|
||||
}
|
||||
}
|
||||
|
||||
debug("%d nodes used, %d alloced, %d locked; %d bytes locked, %d used",
|
||||
usedNodes, allocedNodes, lockedNodes, lockedSize, totalSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes the memory manager.
|
||||
*/
|
||||
void MemoryInit() {
|
||||
// place first node on free list
|
||||
g_pFreeMemNodes = g_mnodeList;
|
||||
|
||||
// link all other objects after first
|
||||
memset(g_mnodeList, 0, sizeof(g_mnodeList));
|
||||
for (int i = 1; i < NUM_MNODES; i++) {
|
||||
g_mnodeList[i - 1].pNext = g_mnodeList + i;
|
||||
}
|
||||
|
||||
// null the last mnode
|
||||
g_mnodeList[NUM_MNODES - 1].pNext = nullptr;
|
||||
|
||||
// clear list of fixed memory nodes
|
||||
memset(g_s_fixedMnodesList, 0, sizeof(g_s_fixedMnodesList));
|
||||
|
||||
// set cyclic links to the sentinel
|
||||
g_heapSentinel.pPrev = &g_heapSentinel;
|
||||
g_heapSentinel.pNext = &g_heapSentinel;
|
||||
|
||||
// flag sentinel as locked
|
||||
g_heapSentinel.flags = DWM_LOCKED | DWM_SENTINEL;
|
||||
|
||||
// store the current heap size in the sentinel
|
||||
uint32 size = MemoryPoolSize[0];
|
||||
if (TinselVersion == 1) size = MemoryPoolSize[1];
|
||||
else if (TinselVersion == 2) size = MemoryPoolSize[2];
|
||||
else if (TinselVersion == 3) {
|
||||
warning("TODO: Find the correct memory pool size for Noir, using 512 MiB for now");
|
||||
size = MemoryPoolSize[3];
|
||||
}
|
||||
g_heapSentinel.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitializes the memory manager.
|
||||
*/
|
||||
void MemoryDeinit() {
|
||||
const MEM_NODE *pHeap = &g_heapSentinel;
|
||||
MEM_NODE *pCur;
|
||||
|
||||
pCur = g_s_fixedMnodesList;
|
||||
for (int i = 0; i < ARRAYSIZE(g_s_fixedMnodesList); ++i, ++pCur) {
|
||||
free(pCur->pBaseAddr);
|
||||
pCur->pBaseAddr = 0;
|
||||
}
|
||||
|
||||
for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
|
||||
free(pCur->pBaseAddr);
|
||||
pCur->pBaseAddr = 0;
|
||||
}
|
||||
|
||||
memset(g_mnodeList, 0, sizeof(g_mnodeList));
|
||||
memset(g_s_fixedMnodesList, 0, sizeof(g_s_fixedMnodesList));
|
||||
g_pFreeMemNodes = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a mnode from the free list.
|
||||
*/
|
||||
static MEM_NODE *AllocMemNode() {
|
||||
// get the first free mnode
|
||||
MEM_NODE *pMemNode = g_pFreeMemNodes;
|
||||
|
||||
// make sure a mnode is available
|
||||
assert(pMemNode); // Out of memory nodes
|
||||
|
||||
// the next free mnode
|
||||
g_pFreeMemNodes = pMemNode->pNext;
|
||||
|
||||
// wipe out the mnode
|
||||
memset(pMemNode, 0, sizeof(MEM_NODE));
|
||||
|
||||
// return new mnode
|
||||
return pMemNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mnode back to the free list.
|
||||
* @param pMemNode Node of the memory object
|
||||
*/
|
||||
void FreeMemNode(MEM_NODE *pMemNode) {
|
||||
// validate mnode pointer
|
||||
assert(pMemNode >= g_mnodeList && pMemNode <= g_mnodeList + NUM_MNODES - 1);
|
||||
|
||||
// place free list in mnode next
|
||||
pMemNode->pNext = g_pFreeMemNodes;
|
||||
|
||||
// add mnode to top of free list
|
||||
g_pFreeMemNodes = pMemNode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to make space for the specified number of bytes on the specified heap.
|
||||
* @param size Number of bytes to free up
|
||||
* @return true if any blocks were discarded, false otherwise
|
||||
*/
|
||||
static bool HeapCompact(long size) {
|
||||
const MEM_NODE *pHeap = &g_heapSentinel;
|
||||
MEM_NODE *pCur, *pOldest;
|
||||
uint32 oldest; // time of the oldest discardable block
|
||||
|
||||
while (g_heapSentinel.size < size) {
|
||||
|
||||
// find the oldest discardable block
|
||||
oldest = DwGetCurrentTime();
|
||||
pOldest = nullptr;
|
||||
for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
|
||||
if (pCur->flags == DWM_USED) {
|
||||
// found a non-discarded discardable block
|
||||
if (pCur->lruTime < oldest) {
|
||||
oldest = pCur->lruTime;
|
||||
pOldest = pCur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pOldest)
|
||||
// discard the oldest block
|
||||
MemoryDiscard(pOldest);
|
||||
else
|
||||
// cannot discard any blocks
|
||||
return false;
|
||||
}
|
||||
|
||||
// we have freed enough memory
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates the specified number of bytes from the heap.
|
||||
* @param flags Allocation attributes
|
||||
* @param size Number of bytes to allocate
|
||||
*/
|
||||
static MEM_NODE *MemoryAlloc(long size) {
|
||||
MEM_NODE *pHeap = &g_heapSentinel;
|
||||
|
||||
#ifdef SCUMM_NEED_ALIGNMENT
|
||||
const int alignPadding = sizeof(void *) - 1;
|
||||
size = (size + alignPadding) & ~alignPadding; //round up to nearest multiple of sizeof(void *), this ensures the addresses that are returned are alignment-safe.
|
||||
#endif
|
||||
|
||||
// compact the heap to make up room for 'size' bytes, if necessary
|
||||
if (!HeapCompact(size))
|
||||
return 0;
|
||||
|
||||
// success! we may allocate a new node of the right size
|
||||
|
||||
// Allocate a node.
|
||||
MEM_NODE *pNode = AllocMemNode();
|
||||
|
||||
// Allocate memory for the node.
|
||||
pNode->pBaseAddr = (byte *)malloc(size);
|
||||
|
||||
// Verify that we got the memory.
|
||||
// TODO: If this fails, we should first try to compact the heap some further.
|
||||
assert(pNode->pBaseAddr);
|
||||
|
||||
// Subtract size of new block from total
|
||||
g_heapSentinel.size -= size;
|
||||
|
||||
#ifdef DEBUG
|
||||
MemoryStats();
|
||||
#endif
|
||||
|
||||
// Set flags, LRU time and size
|
||||
pNode->flags = DWM_USED;
|
||||
pNode->lruTime = DwGetCurrentTime() + 1;
|
||||
pNode->size = size;
|
||||
|
||||
// set mnode at the end of the list
|
||||
pNode->pPrev = pHeap->pPrev;
|
||||
pNode->pNext = pHeap;
|
||||
|
||||
// fix links to this mnode
|
||||
pHeap->pPrev->pNext = pNode;
|
||||
pHeap->pPrev = pNode;
|
||||
|
||||
return pNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a discarded MEM_NODE. Actual memory can be assigned to it
|
||||
* by using MemoryReAlloc().
|
||||
*/
|
||||
MEM_NODE *MemoryNoAlloc() {
|
||||
MEM_NODE *pHeap = &g_heapSentinel;
|
||||
|
||||
// chain a discarded node onto the end of the heap
|
||||
MEM_NODE *pNode = AllocMemNode();
|
||||
pNode->flags = DWM_USED | DWM_DISCARDED;
|
||||
pNode->lruTime = DwGetCurrentTime();
|
||||
pNode->size = 0;
|
||||
|
||||
// set mnode at the end of the list
|
||||
pNode->pPrev = pHeap->pPrev;
|
||||
pNode->pNext = pHeap;
|
||||
|
||||
// fix links to this mnode
|
||||
pHeap->pPrev->pNext = pNode;
|
||||
pHeap->pPrev = pNode;
|
||||
|
||||
// return the discarded node
|
||||
return pNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a fixed block of data.
|
||||
* @todo We really should keep track of the allocated pointers,
|
||||
* so that we can discard them later on, when the engine quits.
|
||||
*/
|
||||
MEM_NODE *MemoryAllocFixed(long size) {
|
||||
|
||||
#ifdef SCUMM_NEED_ALIGNMENT
|
||||
const int alignPadding = sizeof(void *) - 1;
|
||||
size = (size + alignPadding) & ~alignPadding; //round up to nearest multiple of sizeof(void *), this ensures the addresses that are returned are alignment-safe.
|
||||
#endif
|
||||
|
||||
// Search for a free entry in s_fixedMnodesList
|
||||
MEM_NODE *pNode = g_s_fixedMnodesList;
|
||||
for (int i = 0; i < ARRAYSIZE(g_s_fixedMnodesList); ++i, ++pNode) {
|
||||
if (!pNode->pBaseAddr) {
|
||||
pNode->pNext = 0;
|
||||
pNode->pPrev = 0;
|
||||
pNode->pBaseAddr = (byte *)malloc(size);
|
||||
pNode->size = size;
|
||||
pNode->lruTime = DwGetCurrentTime() + 1;
|
||||
pNode->flags = DWM_USED;
|
||||
|
||||
// Subtract size of new block from total
|
||||
g_heapSentinel.size -= size;
|
||||
|
||||
return pNode;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Discards the specified memory object.
|
||||
* @param pMemNode Node of the memory object
|
||||
*/
|
||||
void MemoryDiscard(MEM_NODE *pMemNode) {
|
||||
// validate mnode pointer
|
||||
assert(pMemNode >= g_mnodeList && pMemNode <= g_mnodeList + NUM_MNODES - 1);
|
||||
|
||||
// object must be in use and locked
|
||||
assert((pMemNode->flags & (DWM_USED | DWM_LOCKED)) == DWM_USED);
|
||||
|
||||
// discard it if it isn't already
|
||||
if ((pMemNode->flags & DWM_DISCARDED) == 0) {
|
||||
// free memory
|
||||
free(pMemNode->pBaseAddr);
|
||||
g_heapSentinel.size += pMemNode->size;
|
||||
|
||||
#ifdef DEBUG
|
||||
MemoryStats();
|
||||
#endif
|
||||
|
||||
// mark the node as discarded
|
||||
pMemNode->flags |= DWM_DISCARDED;
|
||||
pMemNode->pBaseAddr = nullptr;
|
||||
pMemNode->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a memory object and returns a pointer to the first byte
|
||||
* of the objects memory block.
|
||||
* @param pMemNode Node of the memory object
|
||||
*/
|
||||
void *MemoryLock(MEM_NODE *pMemNode) {
|
||||
// make sure memory object is not already locked
|
||||
assert((pMemNode->flags & DWM_LOCKED) == 0);
|
||||
|
||||
// check for a discarded or null memory object
|
||||
if ((pMemNode->flags & DWM_DISCARDED) || pMemNode->size == 0)
|
||||
return NULL;
|
||||
|
||||
// set the lock flag
|
||||
pMemNode->flags |= DWM_LOCKED;
|
||||
|
||||
#ifdef DEBUG
|
||||
MemoryStats();
|
||||
#endif
|
||||
|
||||
// return memory objects base address
|
||||
return pMemNode->pBaseAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks a memory object.
|
||||
* @param pMemNode Node of the memory object
|
||||
*/
|
||||
void MemoryUnlock(MEM_NODE *pMemNode) {
|
||||
// make sure memory object is already locked
|
||||
assert(pMemNode->flags & DWM_LOCKED);
|
||||
|
||||
// clear the lock flag
|
||||
pMemNode->flags &= ~DWM_LOCKED;
|
||||
|
||||
#ifdef DEBUG
|
||||
MemoryStats();
|
||||
#endif
|
||||
|
||||
// update the LRU time
|
||||
pMemNode->lruTime = DwGetCurrentTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the size of a specified memory object and re-allocate it if necessary.
|
||||
* @param pMemNode Node of the memory object
|
||||
* @param size New size of block
|
||||
*/
|
||||
void MemoryReAlloc(MEM_NODE *pMemNode, long size) {
|
||||
MEM_NODE *pNew;
|
||||
|
||||
// validate mnode pointer
|
||||
assert(pMemNode >= g_mnodeList && pMemNode <= g_mnodeList + NUM_MNODES - 1);
|
||||
|
||||
// align the size to machine boundary requirements
|
||||
size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
|
||||
|
||||
// validate the size
|
||||
assert(size);
|
||||
|
||||
if (size != pMemNode->size) {
|
||||
// make sure memory object is discarded and not locked
|
||||
assert(pMemNode->flags == (DWM_USED | DWM_DISCARDED));
|
||||
assert(pMemNode->size == 0);
|
||||
|
||||
// unlink the mnode from the current heap
|
||||
pMemNode->pNext->pPrev = pMemNode->pPrev;
|
||||
pMemNode->pPrev->pNext = pMemNode->pNext;
|
||||
|
||||
// allocate a new node
|
||||
pNew = MemoryAlloc(size);
|
||||
|
||||
// make sure memory allocated
|
||||
assert(pNew != NULL);
|
||||
|
||||
// copy the node to the current node
|
||||
memcpy(pMemNode, pNew, sizeof(MEM_NODE));
|
||||
|
||||
// relink the mnode into the list
|
||||
pMemNode->pPrev->pNext = pMemNode;
|
||||
pMemNode->pNext->pPrev = pMemNode;
|
||||
|
||||
// free the new node
|
||||
FreeMemNode(pNew);
|
||||
}
|
||||
|
||||
assert(pMemNode->pBaseAddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch a memory object by updating its LRU time.
|
||||
* @param pMemNode Node of the memory object
|
||||
*/
|
||||
void MemoryTouch(MEM_NODE *pMemNode) {
|
||||
// update the LRU time
|
||||
pMemNode->lruTime = DwGetCurrentTime();
|
||||
}
|
||||
|
||||
uint8 *MemoryDeref(MEM_NODE *pMemNode) {
|
||||
return pMemNode->pBaseAddr;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
67
engines/tinsel/heapmem.h
Normal file
67
engines/tinsel/heapmem.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains the handle based Memory Manager defines
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_HEAPMEM_H
|
||||
#define TINSEL_HEAPMEM_H
|
||||
|
||||
#include "tinsel/dw.h" // new data types
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct MEM_NODE;
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Memory Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
void MemoryInit(); // initializes the memory manager
|
||||
void MemoryDeinit(); // deinitializes the memory manager
|
||||
|
||||
// reserves a memory node for a movable & discardable block
|
||||
MEM_NODE *MemoryNoAlloc();
|
||||
|
||||
// allocates a fixed block with the specified number of bytes
|
||||
MEM_NODE *MemoryAllocFixed(long size);
|
||||
|
||||
void MemoryDiscard( // discards the specified memory object
|
||||
MEM_NODE *pMemNode); // node of the memory object
|
||||
|
||||
void *MemoryLock( // locks a memory object and returns a pointer to the first byte of the objects memory block
|
||||
MEM_NODE *pMemNode); // node of the memory object
|
||||
|
||||
void MemoryReAlloc( // changes the size or attributes of a specified memory object
|
||||
MEM_NODE *pMemNode, // node of the memory object
|
||||
long size); // new size of block
|
||||
|
||||
void MemoryUnlock( // unlocks a memory object
|
||||
MEM_NODE *pMemNode); // node of the memory object
|
||||
|
||||
// 'touch' the memory object, i.e., update its "least recently used" counter.
|
||||
void MemoryTouch(MEM_NODE *pMemNode);
|
||||
|
||||
// Dereference a given memory node
|
||||
uint8 *MemoryDeref(MEM_NODE *pMemNode);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
107
engines/tinsel/inv_objects.cpp
Normal file
107
engines/tinsel/inv_objects.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tinsel/inv_objects.h"
|
||||
#include "common/memstream.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
InventoryObject::InventoryObject(Common::MemoryReadStreamEndian &stream) {
|
||||
_id = stream.readUint32();
|
||||
_hIconFilm = stream.readUint32();
|
||||
_hScript = stream.readUint32();
|
||||
if (TinselVersion == 0) {
|
||||
_attribute = 0;
|
||||
} else {
|
||||
_attribute = stream.readUint32();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class InventoryObjectsImpl : public InventoryObjects {
|
||||
public:
|
||||
InventoryObjectsImpl(const byte *objects, int numObjects) {
|
||||
bool bigEndian = (TinselV1Mac || TinselV1Saturn);
|
||||
auto stream = new Common::MemoryReadStreamEndian(objects, T::SIZE() * numObjects, bigEndian, DisposeAfterUse::NO);
|
||||
for (int i = 0; i < numObjects; i++) {
|
||||
_objects.push_back(T(*stream));
|
||||
}
|
||||
assert((!stream->eos()) && stream->pos() == stream->size());
|
||||
delete stream;
|
||||
}
|
||||
~InventoryObjectsImpl(){};
|
||||
const InventoryObject *GetInvObject(int id) override {
|
||||
auto index = GetObjectIndexIfExists(id);
|
||||
if (index != -1) {
|
||||
return _objects.data() + index;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
const InventoryObjectT3 *GetInvObjectT3(int id) override;
|
||||
|
||||
const InventoryObject *GetObjectByIndex(int index) const override {
|
||||
assert(index >= 0 && index < numObjects());
|
||||
return _objects.data() + index;
|
||||
}
|
||||
void SetObjectFilm(int id, SCNHANDLE hFilm) override {
|
||||
int index = GetObjectIndexIfExists(id);
|
||||
_objects[index].setIconFilm(hFilm);
|
||||
}
|
||||
int GetObjectIndexIfExists(int id) const override {
|
||||
for (uint i = 0; i < _objects.size(); i++) {
|
||||
if (_objects[i].getId() == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
int numObjects() const override {
|
||||
return _objects.size();
|
||||
}
|
||||
private:
|
||||
Common::Array<T> _objects;
|
||||
};
|
||||
|
||||
template<>
|
||||
const InventoryObjectT3 *InventoryObjectsImpl<InventoryObjectT3>::GetInvObjectT3(int id) {
|
||||
auto index = GetObjectIndexIfExists(id);
|
||||
if (index != -1) {
|
||||
return _objects.data() + index;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
const InventoryObjectT3 *InventoryObjectsImpl<InventoryObject>::GetInvObjectT3(int id) {
|
||||
error("Can't query Noir inventory objects from non Noir-game");
|
||||
}
|
||||
|
||||
InventoryObjects *InstantiateInventoryObjects(const byte *invObjects, int numObjects) {
|
||||
switch (TinselVersion) {
|
||||
case 3:
|
||||
return new InventoryObjectsImpl<InventoryObjectT3>(invObjects, numObjects);
|
||||
default:
|
||||
return new InventoryObjectsImpl<InventoryObject>(invObjects, numObjects);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
120
engines/tinsel/inv_objects.h
Normal file
120
engines/tinsel/inv_objects.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_INV_OBJECT_H
|
||||
#define TINSEL_INV_OBJECT_H
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// attribute values - not a bit bit field to prevent portability problems
|
||||
enum class InvObjAttr {
|
||||
IO_ONLYINV1 = 0x01,
|
||||
IO_ONLYINV2 = 0x02,
|
||||
IO_DROPCODE = 0x04,
|
||||
DEFINV1 = 0x08,
|
||||
DEFINV2 = 0x10,
|
||||
PERMACONV = 0x20,
|
||||
CONVENDITEM = 0x40,
|
||||
|
||||
// Noir only
|
||||
V3ATTR_X80 = 0x80,
|
||||
NOTEBOOK_CLUE = 0x200,
|
||||
V3ATTR_X400 = 0x400,
|
||||
NOTEBOOK_TITLE = 0x800, // is a notebook title
|
||||
V3ATTR_X1000 = 0x1000,
|
||||
V3ATTR_X2000 = 0x2000,
|
||||
};
|
||||
|
||||
class InventoryObject {
|
||||
public:
|
||||
InventoryObject(Common::MemoryReadStreamEndian &stream);
|
||||
int32 getId() const { return _id; }
|
||||
SCNHANDLE getIconFilm() const { return _hIconFilm; };
|
||||
void setIconFilm(SCNHANDLE hIconFilm) { _hIconFilm = hIconFilm; }
|
||||
SCNHANDLE getScript() const { return _hScript; }
|
||||
// Tinsel1+
|
||||
bool hasAttribute(InvObjAttr attribute) const {
|
||||
return getAttribute() & (int32)attribute;
|
||||
}
|
||||
|
||||
// Data size consumed by constructor
|
||||
static int SIZE() {
|
||||
return (TinselVersion == 0 ? T0_SIZE : T1_SIZE);
|
||||
}
|
||||
protected:
|
||||
static const int T0_SIZE = 3 * 4;
|
||||
static const int T1_SIZE = T0_SIZE + 4; // Versions above 0 have attributes
|
||||
// Tinsel 1+
|
||||
int32 getAttribute() const {
|
||||
return _attribute;
|
||||
};
|
||||
private:
|
||||
int32 _id; // inventory objects id
|
||||
SCNHANDLE _hIconFilm; // inventory objects animation film
|
||||
SCNHANDLE _hScript; // inventory objects event handling script
|
||||
int32 _attribute = 0;
|
||||
};
|
||||
|
||||
class InventoryObjectT3 : public InventoryObject {
|
||||
public:
|
||||
InventoryObjectT3(Common::MemoryReadStreamEndian &stream) : InventoryObject(stream) {
|
||||
_unknown = stream.readUint32();
|
||||
_title = stream.readUint32();
|
||||
}
|
||||
// Noir:
|
||||
bool isNotebookTitle() const {
|
||||
return (getAttribute() & (int)InvObjAttr::NOTEBOOK_TITLE) != 0;
|
||||
}
|
||||
int32 getUnknown() const {
|
||||
return _unknown;
|
||||
}
|
||||
int32 getTitle() const {
|
||||
return _title;
|
||||
}
|
||||
// Data size consumed by constructor
|
||||
static int SIZE() {
|
||||
return InventoryObject::SIZE() + 8;
|
||||
}
|
||||
private:
|
||||
int32 _unknown;
|
||||
int32 _title;
|
||||
};
|
||||
|
||||
class InventoryObjects {
|
||||
public:
|
||||
virtual ~InventoryObjects() {};
|
||||
virtual const InventoryObject *GetInvObject(int id) = 0;
|
||||
virtual const InventoryObjectT3 *GetInvObjectT3(int id) = 0;
|
||||
virtual const InventoryObject *GetObjectByIndex(int index) const = 0;
|
||||
virtual void SetObjectFilm(int id, SCNHANDLE hFilm) = 0;
|
||||
virtual int GetObjectIndexIfExists(int id) const = 0;
|
||||
virtual int numObjects() const = 0;
|
||||
};
|
||||
|
||||
InventoryObjects *InstantiateInventoryObjects(const byte *invObjects, int numObjects);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_INV_OBJECT_H
|
||||
188
engines/tinsel/mareels.cpp
Normal file
188
engines/tinsel/mareels.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Functions to set up moving actors' reels.
|
||||
*/
|
||||
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/pcode.h" // For D_UP, D_DOWN
|
||||
#include "tinsel/movers.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
enum {
|
||||
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
|
||||
};
|
||||
|
||||
struct SCIdataStruct {
|
||||
int actor;
|
||||
int scale;
|
||||
int direction;
|
||||
SCNHANDLE reels[4];
|
||||
};
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
static SCIdataStruct g_SCIdata[MAX_SCRENTRIES];
|
||||
static int g_scrEntries = 0;
|
||||
|
||||
/**
|
||||
* Sets an actor's walk reels
|
||||
*/
|
||||
|
||||
void SetWalkReels(MOVER *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(MOVER *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(MOVER *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 GetMoverTalkReel(MOVER *pActor, TFTYPE dirn) {
|
||||
assert(1 <= pActor->scale && pActor->scale <= TOTAL_SCALES);
|
||||
switch (dirn) {
|
||||
case TF_NONE:
|
||||
return pActor->talkReels[pActor->scale-1][pActor->direction];
|
||||
|
||||
case TF_UP:
|
||||
return pActor->talkReels[pActor->scale-1][AWAY];
|
||||
|
||||
case TF_DOWN:
|
||||
return pActor->talkReels[pActor->scale-1][FORWARD];
|
||||
|
||||
case TF_LEFT:
|
||||
return pActor->talkReels[pActor->scale-1][LEFTREEL];
|
||||
|
||||
case TF_RIGHT:
|
||||
return pActor->talkReels[pActor->scale-1][RIGHTREEL];
|
||||
|
||||
default:
|
||||
error("GetMoverTalkReel() - illegal direction");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scalingreels
|
||||
*/
|
||||
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) &&
|
||||
!(scale == NUM_MAINSCALES && direction == D_DOWN)); // illegal direction from scale
|
||||
|
||||
assert(g_scrEntries < MAX_SCRENTRIES); // Scaling reels limit reached!
|
||||
|
||||
g_SCIdata[g_scrEntries].actor = actor;
|
||||
g_SCIdata[g_scrEntries].scale = scale;
|
||||
g_SCIdata[g_scrEntries].direction = direction;
|
||||
g_SCIdata[g_scrEntries].reels[LEFTREEL] = left;
|
||||
g_SCIdata[g_scrEntries].reels[RIGHTREEL] = right;
|
||||
g_SCIdata[g_scrEntries].reels[FORWARD] = forward;
|
||||
g_SCIdata[g_scrEntries].reels[AWAY] = away;
|
||||
g_scrEntries++;
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalingReel
|
||||
*/
|
||||
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel) {
|
||||
int d; // Direction
|
||||
|
||||
// The smaller the number, the bigger the scale
|
||||
if (scale1 < scale2)
|
||||
d = D_DOWN;
|
||||
else
|
||||
d = D_UP;
|
||||
|
||||
for (int i = 0; i < g_scrEntries; i++) {
|
||||
if (g_SCIdata[i].actor == ano && g_SCIdata[i].scale == scale1 && g_SCIdata[i].direction == d) {
|
||||
if (g_SCIdata[i].reels[reel] == TF_NONE)
|
||||
return 0;
|
||||
else
|
||||
return g_SCIdata[i].reels[reel];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* RebootScalingReels
|
||||
*/
|
||||
void RebootScalingReels() {
|
||||
g_scrEntries = 0;
|
||||
memset(g_SCIdata, 0, sizeof(g_SCIdata));
|
||||
}
|
||||
|
||||
/**
|
||||
* Discourage them from being ditched.
|
||||
*/
|
||||
void TouchMoverReels() {
|
||||
MOVER *pMover = NextMover(NULL);
|
||||
int scale;
|
||||
|
||||
do {
|
||||
for (scale = 0; scale < TOTAL_SCALES; scale++) {
|
||||
_vm->_handle->TouchMem(pMover->walkReels[scale][LEFTREEL]);
|
||||
}
|
||||
} while ((pMover = NextMover(pMover)) != NULL);
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
52
engines/tinsel/mareels.h
Normal file
52
engines/tinsel/mareels.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_MAREELS_H // prevent multiple includes
|
||||
#define TINSEL_MAREELS_H
|
||||
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
#include "tinsel/movers.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
void SetWalkReels(MOVER *pMover, int scale,
|
||||
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
|
||||
|
||||
void SetStandReels(MOVER *pMover, int scale,
|
||||
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
|
||||
|
||||
void SetTalkReels(MOVER *pMover, int scale,
|
||||
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
|
||||
|
||||
SCNHANDLE GetMoverTalkReel(MOVER *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 TouchMoverReels();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
440
engines/tinsel/metaengine.cpp
Normal file
440
engines/tinsel/metaengine.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/savefile.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "tinsel/bmv.h"
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/savescn.h" // needed by TinselMetaEngine::
|
||||
|
||||
#include "tinsel/detection.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_CROP_HEIGHT_480_TO_432,
|
||||
{
|
||||
_s("Remove Black Bars"),
|
||||
_s("The game originally renders at 640x432 which is then presented letterboxed at 640x480. Enabling this option removes the forced letterbox effect, so that the game fits better on screens with an aspect ratio wider than 4:3."),
|
||||
"crop_black_bars",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
uint32 TinselEngine::getGameID() const {
|
||||
return _gameDescription->gameID;
|
||||
}
|
||||
|
||||
uint32 TinselEngine::getFeatures() const {
|
||||
return _gameDescription->features;
|
||||
}
|
||||
|
||||
Common::Language TinselEngine::getLanguage() const {
|
||||
return _gameDescription->desc.language;
|
||||
}
|
||||
|
||||
Common::Platform TinselEngine::getPlatform() const {
|
||||
return _gameDescription->desc.platform;
|
||||
}
|
||||
|
||||
uint16 TinselEngine::getVersion() const {
|
||||
return _gameDescription->version;
|
||||
}
|
||||
|
||||
bool TinselEngine::getIsADGFDemo() const {
|
||||
return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
|
||||
}
|
||||
|
||||
bool TinselEngine::isV1CD() const {
|
||||
return (bool)(_gameDescription->desc.flags & ADGF_CD);
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
class TinselMetaEngine : public AdvancedMetaEngine<Tinsel::TinselGameDescription> {
|
||||
public:
|
||||
const char *getName() const override{
|
||||
return "tinsel";
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Tinsel::TinselGameDescription *desc) const override;
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
|
||||
// TODO: Add getSavegameFile(). See comments in loadGameState and removeSaveState
|
||||
|
||||
void registerDefaultSettings(const Common::String &target) const override;
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSimpleSavesNames) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportPlayTime) ||
|
||||
(f == kSavesSupportCreationDate);
|
||||
}
|
||||
|
||||
bool Tinsel::TinselEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher) ||
|
||||
(f == kSupportsLoadingDuringRuntime);
|
||||
}
|
||||
|
||||
SaveStateDescriptor TinselMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::String fileName;
|
||||
fileName = Common::String::format("%s.%03u", target, slot);
|
||||
|
||||
Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
|
||||
|
||||
if (!file) {
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
file->readUint32LE(); // skip id
|
||||
file->readUint32LE(); // skip size
|
||||
uint32 ver = file->readUint32LE();
|
||||
char saveDesc[SG_DESC_LEN];
|
||||
file->read(saveDesc, sizeof(saveDesc));
|
||||
|
||||
saveDesc[SG_DESC_LEN - 1] = 0;
|
||||
SaveStateDescriptor desc(this, slot, saveDesc);
|
||||
|
||||
int8 tm_year = file->readUint16LE();
|
||||
int8 tm_mon = file->readSByte();
|
||||
int8 tm_mday = file->readSByte();
|
||||
int8 tm_hour = file->readSByte();
|
||||
int8 tm_min = file->readSByte();
|
||||
file->readSByte(); // skip secs
|
||||
|
||||
desc.setSaveDate(1900 + tm_year, 1 + tm_mon, tm_mday);
|
||||
desc.setSaveTime(tm_hour, tm_min);
|
||||
|
||||
if (ver >= 3) {
|
||||
uint32 playTime = file->readUint32LE(); // playTime in seconds
|
||||
desc.setPlayTime(playTime);
|
||||
}
|
||||
|
||||
delete file;
|
||||
return desc;
|
||||
}
|
||||
|
||||
namespace Tinsel {
|
||||
extern int getList(Common::SaveFileManager *saveFileMan, const Common::String &target);
|
||||
}
|
||||
|
||||
SaveStateList TinselMetaEngine::listSaves(const char *target) const {
|
||||
Common::String pattern = target;
|
||||
pattern = pattern + ".###";
|
||||
Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern);
|
||||
|
||||
SaveStateList saveList;
|
||||
int slotNum = 0;
|
||||
for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
slotNum = atoi(file->c_str() + file->size() - 3);
|
||||
|
||||
const Common::String &fname = *file;
|
||||
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fname);
|
||||
if (in) {
|
||||
in->readUint32LE(); // skip id
|
||||
in->readUint32LE(); // skip size
|
||||
in->readUint32LE(); // skip version
|
||||
char saveDesc[SG_DESC_LEN];
|
||||
in->read(saveDesc, sizeof(saveDesc));
|
||||
|
||||
saveDesc[SG_DESC_LEN - 1] = 0;
|
||||
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, saveDesc));
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
Common::Error TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const Tinsel::TinselGameDescription *desc) const {
|
||||
*engine = new Tinsel::TinselEngine(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
int TinselMetaEngine::getMaximumSaveSlot() const { return 99; }
|
||||
|
||||
bool TinselMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
Tinsel::setNeedLoad();
|
||||
// Same issue here as with loadGameState(): we need the physical savegame
|
||||
// slot. Refer to bug #5819.
|
||||
int listSlot = -1;
|
||||
const int numStates = Tinsel::getList(g_system->getSavefileManager(), target);
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
const char *fileName = Tinsel::ListEntry(i, Tinsel::LE_NAME);
|
||||
const int saveSlot = atoi(fileName + strlen(fileName) - 3);
|
||||
|
||||
if (saveSlot == slot) {
|
||||
listSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = g_system->getSavefileManager()->removeSavefile(Tinsel::ListEntry(listSlot, Tinsel::LE_NAME));
|
||||
Tinsel::setNeedLoad();
|
||||
Tinsel::getList(g_system->getSavefileManager(), target);
|
||||
return success;
|
||||
}
|
||||
|
||||
void TinselMetaEngine::registerDefaultSettings(const Common::String &target) const {
|
||||
ConfMan.registerDefault("crop_black_bars", false); // show the black bars by default (original behaviour)
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *TinselMetaEngine::getAdvancedExtraGuiOptions() const {
|
||||
return optionsList;
|
||||
}
|
||||
|
||||
Common::KeymapArray TinselMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Tinsel;
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "tinsel-default", _("Default keymappings"));
|
||||
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
|
||||
Keymap *saveloadKeyMap = new Keymap(Keymap::kKeymapTypeGame, "saveload-shortcuts", _("Save/Load keymappings"));
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action("ESCAPE", _("Escape"));
|
||||
act->setCustomEngineActionEvent(kActionEscape);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action("MOVEUP", _("Move up"));
|
||||
act->setCustomEngineActionEvent(kActionMoveUp);
|
||||
act->addDefaultInputMapping("UP");
|
||||
act->addDefaultInputMapping("KP8");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("MOVEDOWN", _("Move down"));
|
||||
act->setCustomEngineActionEvent(kActionMoveDown);
|
||||
act->addDefaultInputMapping("DOWN");
|
||||
act->addDefaultInputMapping("KP2");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("MOVELEFT", _("Move left"));
|
||||
act->setCustomEngineActionEvent(kActionMoveLeft);
|
||||
act->addDefaultInputMapping("LEFT");
|
||||
act->addDefaultInputMapping("KP4");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("MOVERIGHT", _("Move right"));
|
||||
act->setCustomEngineActionEvent(kActionMoveRight);
|
||||
act->addDefaultInputMapping("RIGHT");
|
||||
act->addDefaultInputMapping("KP6");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
// I18N: Walk to where cursor points
|
||||
act = new Action("WALK", _("Walk to"));
|
||||
act->setCustomEngineActionEvent(kActionWalkTo);
|
||||
act->addDefaultInputMapping("SPACE");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
// I18N: Perform action on object where cursor points
|
||||
act = new Action("ACTION", _("Action"));
|
||||
act->setCustomEngineActionEvent(kActionAction);
|
||||
act->addDefaultInputMapping("RETURN");
|
||||
act->addDefaultInputMapping("KP_ENTER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("LOOK", _("Look"));
|
||||
act->setCustomEngineActionEvent(kActionLook);
|
||||
act->addDefaultInputMapping("l");
|
||||
act->addDefaultInputMapping("LCTRL");
|
||||
act->addDefaultInputMapping("RCTRL");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("OPTIONSDIALOG", _("Options menu"));
|
||||
act->setCustomEngineActionEvent(kActionOptionsDialog);
|
||||
act->addDefaultInputMapping("F1");
|
||||
act->addDefaultInputMapping("1");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("INVENTORY", _("Inventory"));
|
||||
act->setCustomEngineActionEvent(kActionInventory);
|
||||
act->addDefaultInputMapping("F2");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("NOTEBOOK", _("Notebook"));
|
||||
act->setCustomEngineActionEvent(kActionNotebook);
|
||||
act->addDefaultInputMapping("F3");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SAVE", _("Save"));
|
||||
act->setCustomEngineActionEvent(kActionSave);
|
||||
act->addDefaultInputMapping("F5");
|
||||
act->addDefaultInputMapping("5");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("LOAD", _("Load"));
|
||||
act->setCustomEngineActionEvent(kActionLoad);
|
||||
act->addDefaultInputMapping("7");
|
||||
act->addDefaultInputMapping("F7");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("QUIT", _("Quit"));
|
||||
act->setCustomEngineActionEvent(kActionQuit);
|
||||
act->addDefaultInputMapping("C+q");
|
||||
act->addDefaultInputMapping("A+q");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("PGUPMENU", _("Page up in save / load menu"));
|
||||
act->setCustomEngineActionEvent(kActionPageUp);
|
||||
act->addDefaultInputMapping("PAGEUP");
|
||||
act->addDefaultInputMapping("KP9");
|
||||
saveloadKeyMap->addAction(act);
|
||||
|
||||
act = new Action("PGDOWNMENU", _("Page down in save / load menu"));
|
||||
act->setCustomEngineActionEvent(kActionPageDown);
|
||||
act->addDefaultInputMapping("PAGEDOWN");
|
||||
act->addDefaultInputMapping("KP3");
|
||||
saveloadKeyMap->addAction(act);
|
||||
|
||||
act = new Action("STARTOFMENU", _("Go to start of save / load menu"));
|
||||
act->setCustomEngineActionEvent(kActionHome);
|
||||
act->addDefaultInputMapping("HOME");
|
||||
act->addDefaultInputMapping("KP7");
|
||||
saveloadKeyMap->addAction(act);
|
||||
|
||||
act = new Action("ENDOFMENU", _("Go to end of save / load menu"));
|
||||
act->setCustomEngineActionEvent(kActionEnd);
|
||||
act->addDefaultInputMapping("END");
|
||||
act->addDefaultInputMapping("KP1");
|
||||
saveloadKeyMap->addAction(act);
|
||||
|
||||
KeymapArray keymaps(3);
|
||||
keymaps[0] = engineKeyMap;
|
||||
keymaps[1] = gameKeyMap;
|
||||
keymaps[2] = saveloadKeyMap;
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(TINSEL)
|
||||
REGISTER_PLUGIN_DYNAMIC(TINSEL, PLUGIN_TYPE_ENGINE, TinselMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(TINSEL, PLUGIN_TYPE_ENGINE, TinselMetaEngine);
|
||||
#endif
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
Common::Error TinselEngine::loadGameState(int slot) {
|
||||
// FIXME: Hopefully this is only used when loading games via
|
||||
// the launcher, since we do a hacky savegame slot to savelist
|
||||
// entry mapping here.
|
||||
//
|
||||
// You might wonder why is needed and here is the answer:
|
||||
// The save/load dialog of the GMM operates with the physical
|
||||
// savegame slots, while Tinsel internally uses entry numbers in
|
||||
// a savelist (which is sorted latest to first). Now to allow
|
||||
// proper loading of (especially Discworld2) saves we need to
|
||||
// get a savelist entry number instead of the physical slot.
|
||||
//
|
||||
// There are different possible solutions:
|
||||
//
|
||||
// One way to fix this would be to pass the filename instead of
|
||||
// the savelist entry number to RestoreGame, though it could make
|
||||
// problems how DW2 handles CD switches. Normally DW2 would pass
|
||||
// '-2' as slot when it changes CDs.
|
||||
//
|
||||
// Another way would be to convert all of Tinsel to use physical
|
||||
// slot numbers instead of savelist entry numbers for loading.
|
||||
// This would also allow '-2' as slot for CD changes without
|
||||
// any major hackery.
|
||||
|
||||
int listSlot = -1;
|
||||
const int numStates = Tinsel::getList();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
const char *fileName = Tinsel::ListEntry(i, Tinsel::LE_NAME);
|
||||
const int saveSlot = atoi(fileName + strlen(fileName) - 3);
|
||||
|
||||
if (saveSlot == slot) {
|
||||
listSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (listSlot == -1)
|
||||
return Common::kUnknownError; // TODO: proper error code
|
||||
|
||||
RestoreGame(listSlot);
|
||||
return Common::kNoError; // TODO: return success/failure
|
||||
}
|
||||
|
||||
#if 0
|
||||
Common::Error TinselEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
Common::String saveName = _vm->getSavegameFilename((int16)(slot + 1));
|
||||
char saveDesc[SG_DESC_LEN];
|
||||
Common::strlcpy(saveDesc, desc, SG_DESC_LEN);
|
||||
SaveGame((char *)saveName.c_str(), saveDesc);
|
||||
ProcessSRQueue(); // This shouldn't be needed, but for some reason it is...
|
||||
return Common::kNoError; // TODO: return success/failure
|
||||
}
|
||||
#endif
|
||||
|
||||
bool TinselEngine::canLoadGameStateCurrently(Common::U32String *msg) { return !_bmv->MoviePlaying(); }
|
||||
|
||||
#if 0
|
||||
bool TinselEngine::canSaveGameStateCurrently(Common::U32String *msg) { return isCursorShown(); }
|
||||
#endif
|
||||
|
||||
} // End of namespace Tinsel
|
||||
66
engines/tinsel/module.mk
Normal file
66
engines/tinsel/module.mk
Normal file
@@ -0,0 +1,66 @@
|
||||
MODULE := engines/tinsel
|
||||
|
||||
MODULE_OBJS := \
|
||||
noir/lzss.o \
|
||||
actors.o \
|
||||
adpcm.o \
|
||||
anim.o \
|
||||
background.o \
|
||||
bg.o \
|
||||
bmv.o \
|
||||
cliprect.o \
|
||||
config.o \
|
||||
cursor.o \
|
||||
debugger.o \
|
||||
dialogs.o \
|
||||
drives.o \
|
||||
effect.o \
|
||||
events.o \
|
||||
faders.o \
|
||||
film.o \
|
||||
font.o \
|
||||
graphics.o \
|
||||
handle.o \
|
||||
heapmem.o \
|
||||
inv_objects.o \
|
||||
mareels.o \
|
||||
metaengine.o \
|
||||
move.o \
|
||||
movers.o \
|
||||
multiobj.o \
|
||||
music.o \
|
||||
object.o \
|
||||
palette.o \
|
||||
pcode.o \
|
||||
pdisplay.o \
|
||||
play.o \
|
||||
polygons.o \
|
||||
saveload.o \
|
||||
savescn.o \
|
||||
scene.o \
|
||||
sched.o \
|
||||
scn.o \
|
||||
scroll.o \
|
||||
sound.o \
|
||||
strres.o \
|
||||
sysvar.o \
|
||||
text.o \
|
||||
timers.o \
|
||||
tinlib.o \
|
||||
tinsel.o \
|
||||
token.o \
|
||||
noir/notebook.o \
|
||||
noir/notebook_page.o \
|
||||
noir/sysreel.o \
|
||||
noir/spriter.o \
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_TINSEL), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
1787
engines/tinsel/move.cpp
Normal file
1787
engines/tinsel/move.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
engines/tinsel/move.h
Normal file
46
engines/tinsel/move.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_MOVE_H // prevent multiple includes
|
||||
#define TINSEL_MOVE_H
|
||||
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
#include "tinsel/movers.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct MOVER;
|
||||
|
||||
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();
|
||||
int GetLastLeadYdest();
|
||||
|
||||
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 */
|
||||
1089
engines/tinsel/movers.cpp
Normal file
1089
engines/tinsel/movers.cpp
Normal file
File diff suppressed because it is too large
Load Diff
230
engines/tinsel/movers.h
Normal file
230
engines/tinsel/movers.h
Normal file
@@ -0,0 +1,230 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Should really be called "moving actors.h"
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_MOVERS_H // prevent multiple includes
|
||||
#define TINSEL_MOVERS_H
|
||||
|
||||
#include "tinsel/anim.h" // for ANIM
|
||||
#include "tinsel/scene.h" // for TFTYPE
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct OBJECT;
|
||||
|
||||
enum NPS {NOT_IN, GOING_UP, GOING_DOWN, LEAVING, ENTERING};
|
||||
|
||||
enum IND {NO_PROB, TRY_CENTER, TRY_CORNER, TRY_NEXTCORNER};
|
||||
|
||||
enum DIRECTION { LEFTREEL, RIGHTREEL, FORWARD, AWAY };
|
||||
|
||||
#define NUM_MAINSCALES ((TinselVersion >= 2) ? 10 : 5)
|
||||
#define NUM_AUXSCALES 5
|
||||
#define TOTAL_SCALES (NUM_MAINSCALES + NUM_AUXSCALES)
|
||||
#define REQ_MAIN_SCALES 10
|
||||
#define REQ_TOTAL_SCALES 15
|
||||
|
||||
#define BOGUS_BRIGHTNESS -1
|
||||
|
||||
enum MOVER_TYPE { MOVER_2D, MOVER_3D };
|
||||
|
||||
struct MOVER {
|
||||
int objX, objY; /* Co-ordinates object */
|
||||
|
||||
int targetX, targetY;
|
||||
int ItargetX, ItargetY; /* Intermediate destination */
|
||||
int UtargetX, UtargetY; /* Ultimate destination */
|
||||
|
||||
HPOLYGON hIpath; /* Intermediate path */
|
||||
HPOLYGON hUpath; /* Ultimate path */
|
||||
HPOLYGON hCpath; /* Current path */
|
||||
|
||||
bool over;
|
||||
int walkNumber;
|
||||
|
||||
IND InDifficulty;
|
||||
|
||||
/* For use in 'follow nodes' polygons */
|
||||
HPOLYGON hFnpath;
|
||||
NPS npstatus;
|
||||
int line;
|
||||
|
||||
int Tline; // NEW
|
||||
|
||||
// TODO: TagReelRunning may be the same as bSpecReel
|
||||
bool bSpecReel;
|
||||
|
||||
/* Used internally */
|
||||
DIRECTION direction; // Current reel
|
||||
int scale; // Current scale
|
||||
|
||||
int stepCount; // Step count for walking reel synchronisation
|
||||
|
||||
int walkedFromX, walkedFromY;
|
||||
|
||||
bool bMoving; // Set this to TRUE during a walk
|
||||
|
||||
bool bNoPath;
|
||||
bool bIgPath;
|
||||
bool bWalkReel;
|
||||
|
||||
OBJECT *actorObj; // Actor's object
|
||||
ANIM actorAnim; // Actor's animation script
|
||||
|
||||
SCNHANDLE hLastFilm; // } Used by AlterMover()
|
||||
SCNHANDLE hPushedFilm; // }
|
||||
|
||||
int actorID;
|
||||
int actorToken;
|
||||
|
||||
SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
|
||||
SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
|
||||
SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
|
||||
|
||||
bool bActive;
|
||||
|
||||
int SlowFactor; // Slow down movement while hidden
|
||||
|
||||
bool bStop;
|
||||
|
||||
/* NOTE: If effect polys can overlap, this needs improving */
|
||||
bool bInEffect;
|
||||
|
||||
Common::PROCESS *pProc;
|
||||
|
||||
// Discworld 2 specific fields
|
||||
int32 zOverride;
|
||||
bool bHidden;
|
||||
int brightness; // Current brightness
|
||||
int startColor;
|
||||
int paletteLength;
|
||||
HPOLYGON hRpath; // Recent path
|
||||
|
||||
// Noir specific fields
|
||||
MOVER_TYPE type;
|
||||
SCNHANDLE hModelName;
|
||||
SCNHANDLE hTextureName;
|
||||
|
||||
int posX, posY, posZ;
|
||||
int animSpeed;
|
||||
uint nextIdleAnim;
|
||||
};
|
||||
|
||||
struct MAINIT {
|
||||
int X;
|
||||
int Y;
|
||||
MOVER *pMover;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
void MoverProcessCreate(int X, int Y, int id, MOVER *pMover);
|
||||
|
||||
|
||||
enum AR_FUNCTION { AR_NORMAL, AR_PUSHREEL, AR_POPREEL, AR_WALKREEL };
|
||||
|
||||
void StoreMoverPalette(MOVER *pMover, int startColor, int length);
|
||||
|
||||
void MoverBrightness(MOVER *pMover, int brightness);
|
||||
|
||||
MOVER *GetMover(int ano);
|
||||
MOVER *RegisterMover(int ano);
|
||||
void KillMover(MOVER *pMover);
|
||||
MOVER *GetLiveMover(int index);
|
||||
|
||||
bool getMActorState(MOVER *psActor);
|
||||
int GetMoverId(MOVER *pMover);
|
||||
void SetMoverZ(MOVER *pMover, int y, uint32 zFactor);
|
||||
void SetMoverZoverride(MOVER *pMover, uint32 zFactor);
|
||||
|
||||
void HideMover(MOVER *pMover, int sf = 0);
|
||||
bool MoverHidden(MOVER *pMover);
|
||||
bool MoverIs(MOVER *pMover);
|
||||
bool MoverIsSWalking(MOVER *pMover);
|
||||
bool MoverMoving(MOVER *pMover);
|
||||
int GetWalkNumber(MOVER *pMover);
|
||||
void UnHideMover(MOVER *pMover);
|
||||
void DropMovers();
|
||||
void PositionMover(MOVER *pMover, int x, int y);
|
||||
|
||||
void GetMoverPosition(MOVER *pMover, int *aniX, int *aniY);
|
||||
void GetMoverMidTop(MOVER *pMover, int *aniX, int *aniY);
|
||||
int GetMoverLeft(MOVER *pMover);
|
||||
int GetMoverRight(MOVER *pMover);
|
||||
int GetMoverTop(MOVER *pMover);
|
||||
int GetMoverBottom(MOVER *pMover);
|
||||
|
||||
bool MoverIsInPolygon(MOVER *pMover, HPOLYGON hPoly);
|
||||
void AlterMover(MOVER *pMover, SCNHANDLE film, AR_FUNCTION fn);
|
||||
DIRECTION GetMoverDirection(MOVER *pMover);
|
||||
int GetMoverScale(MOVER *pMover);
|
||||
void SetMoverDirection(MOVER *pMover, DIRECTION dirn);
|
||||
void SetMoverStanding(MOVER *pMover);
|
||||
void SetMoverWalkReel(MOVER *pMover, DIRECTION reel, int scale, bool force);
|
||||
|
||||
MOVER *InMoverBlock(MOVER *pMover, int x, int y);
|
||||
|
||||
void RebootMovers();
|
||||
|
||||
bool IsMAinEffectPoly(int index);
|
||||
void SetMoverInEffect(int index, bool tf);
|
||||
|
||||
void StopMover(MOVER *pMover);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
struct SAVED_MOVER {
|
||||
|
||||
int actorID;
|
||||
int objX;
|
||||
int objY;
|
||||
SCNHANDLE hLastfilm;
|
||||
|
||||
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 startColor;
|
||||
int paletteLength;
|
||||
};
|
||||
|
||||
void SaveMovers(SAVED_MOVER *sMoverInfo);
|
||||
void RestoreAuxScales(SAVED_MOVER *sMoverInfo);
|
||||
|
||||
MOVER *NextMover(MOVER *pMover);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
enum {
|
||||
MAGICX = -101,
|
||||
MAGICY = -102
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_MOVERS_H */
|
||||
608
engines/tinsel/multiobj.cpp
Normal file
608
engines/tinsel/multiobj.cpp
Normal file
@@ -0,0 +1,608 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains utilities to handle multi-part objects.
|
||||
*/
|
||||
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/noir/sysreel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
const FRAME *MULTI_INIT::GetFrame() const {
|
||||
return (const FRAME *)_vm->_handle->LockMem(FROM_32(hMulFrame));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a multi-part object using a list of images to init
|
||||
* each object piece. One object is created for each image in the list.
|
||||
* All objects are given the same palette as the first image. A pointer
|
||||
* to the first (master) object created is returned.
|
||||
* @param pInitTbl Pointer to multi-object initialisation table
|
||||
*/
|
||||
OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) {
|
||||
OBJ_INIT obj_init; // object init table
|
||||
OBJECT *pFirst, *pObj; // object pointers
|
||||
const FRAME *pFrame; // list of images for the multi-part object
|
||||
|
||||
if (FROM_32(pInitTbl->hMulFrame)) {
|
||||
// we have a frame handle
|
||||
pFrame = pInitTbl->GetFrame();
|
||||
|
||||
obj_init.hObjImg = READ_32(pFrame); // first objects shape
|
||||
} else { // this must be a animation list for a NULL object
|
||||
pFrame = nullptr;
|
||||
obj_init.hObjImg = 0; // first objects shape
|
||||
}
|
||||
|
||||
// init the object init table
|
||||
obj_init.objFlags = (int)FROM_32(pInitTbl->mulFlags); // all objects have same flags
|
||||
obj_init.objID = (int)FROM_32(pInitTbl->mulID); // all objects have same ID
|
||||
obj_init.objX = (int)FROM_32(pInitTbl->mulX); // all objects have same X ani pos
|
||||
obj_init.objY = (int)FROM_32(pInitTbl->mulY); // all objects have same Y ani pos
|
||||
obj_init.objZ = (int)FROM_32(pInitTbl->mulZ); // all objects have same Z pos
|
||||
|
||||
// create and init the first object
|
||||
pObj = pFirst = InitObject(&obj_init);
|
||||
|
||||
if (pFrame) {
|
||||
// if we have any animation frames
|
||||
|
||||
pFrame++;
|
||||
|
||||
while (READ_32(pFrame) != 0) {
|
||||
// set next objects shape
|
||||
obj_init.hObjImg = READ_32(pFrame);
|
||||
|
||||
// create next object and link to previous
|
||||
pObj = pObj->pSlave = InitObject(&obj_init);
|
||||
|
||||
pFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
// null end of list for final object
|
||||
pObj->pSlave = nullptr;
|
||||
|
||||
// return master object
|
||||
return pFirst;
|
||||
}
|
||||
|
||||
OBJECT *InsertReelObj(const FREEL *reels) {
|
||||
const MULTI_INIT *pmi = reels->GetMultiInit();
|
||||
// Verify that there is an image defined
|
||||
const FRAME *frame = pmi->GetFrame();
|
||||
const IMAGE *image = (const IMAGE*)_vm->_handle->LockMem(*frame);
|
||||
assert(image);
|
||||
|
||||
auto pInsObj = MultiInitObject(pmi);
|
||||
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), pInsObj);
|
||||
return pInsObj; // Result
|
||||
}
|
||||
|
||||
const FILM *GetSystemReelFilm(SysReel reelIndex) {
|
||||
SCNHANDLE hFilm = _vm->_systemReel->get(reelIndex);
|
||||
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
|
||||
return pfilm;
|
||||
}
|
||||
|
||||
OBJECT *InsertSystemReelObj(SysReel reelIndex) {
|
||||
return InsertReelObj(GetSystemReelFilm(reelIndex)->reels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the multi-part object onto the specified object list.
|
||||
* @param pObjList List to insert multi-part object onto
|
||||
* @param pInsObj Head of multi-part object to insert
|
||||
|
||||
*/
|
||||
|
||||
void MultiInsertObject(OBJECT **pObjList, OBJECT *pInsObj) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pInsObj));
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// add next part to the specified list
|
||||
InsertObject(pObjList, pInsObj);
|
||||
|
||||
// next obj in list
|
||||
pInsObj = pInsObj->pSlave;
|
||||
} while (pInsObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the pieces of a multi-part object from the
|
||||
* specified object list.
|
||||
* @param pObjList List to delete multi-part object from
|
||||
* @param pMultiObj Multi-part object to be deleted
|
||||
*/
|
||||
|
||||
void MultiDeleteObject(OBJECT **pObjList, OBJECT *pMultiObj) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// delete object
|
||||
DelObject(pObjList, pMultiObj);
|
||||
|
||||
// next obj in list
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
} while (pMultiObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the pieces of a multi-part object from the
|
||||
* specified playfield's object list, then sets the pointer to nullptr.
|
||||
* @param which The playfield whos object list we delete from.
|
||||
* @param pMultiObj Multi-part object to be deleted
|
||||
*/
|
||||
void MultiDeleteObjectIfExists(unsigned int playfield, OBJECT **pMultiObj) {
|
||||
if (*pMultiObj) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(playfield), *pMultiObj);
|
||||
*pMultiObj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides a multi-part object by giving each object a "NullImage"
|
||||
* image pointer.
|
||||
* @param pMultiObj Multi-part object to be hidden
|
||||
*/
|
||||
|
||||
void MultiHideObject(OBJECT *pMultiObj) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// set master shape to null animation frame
|
||||
pMultiObj->hShape = 0;
|
||||
|
||||
// change all objects
|
||||
MultiReshape(pMultiObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Horizontally flip a multi-part object.
|
||||
* @param pFlipObj Head of multi-part object to flip
|
||||
*/
|
||||
|
||||
void MultiHorizontalFlip(OBJECT *pFlipObj) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pFlipObj));
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// horizontally flip the next part
|
||||
AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPH,
|
||||
pFlipObj->hImg);
|
||||
|
||||
// next obj in list
|
||||
pFlipObj = pFlipObj->pSlave;
|
||||
} while (pFlipObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertically flip a multi-part object.
|
||||
* @param pFlipObj Head of multi-part object to flip
|
||||
*/
|
||||
|
||||
void MultiVerticalFlip(OBJECT *pFlipObj) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pFlipObj));
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// vertically flip the next part
|
||||
AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPV,
|
||||
pFlipObj->hImg);
|
||||
|
||||
// next obj in list
|
||||
pFlipObj = pFlipObj->pSlave;
|
||||
} while (pFlipObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the coordinates of a multi-part object. The adjustments
|
||||
* take into account the orientation of the object.
|
||||
* @param pMultiObj Multi-part object to be adjusted
|
||||
* @param deltaX X adjustment
|
||||
* @param deltaY Y adjustment
|
||||
*/
|
||||
|
||||
void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
if (deltaX == 0 && deltaY == 0)
|
||||
return; // ignore no change
|
||||
|
||||
if (TinselVersion <= 1) {
|
||||
// *** 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;
|
||||
}
|
||||
}
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// signal a change in the object
|
||||
pMultiObj->flags |= DMA_CHANGED;
|
||||
|
||||
// adjust the x position
|
||||
pMultiObj->xPos += intToFrac(deltaX);
|
||||
|
||||
// adjust the y position
|
||||
pMultiObj->yPos += intToFrac(deltaY);
|
||||
|
||||
// next obj in list
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
|
||||
} while (pMultiObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves all the pieces of a multi-part object by the specified
|
||||
* amount. Does not take into account the objects orientation.
|
||||
* @param pMultiObj Multi-part object to be adjusted
|
||||
* @param deltaX X movement
|
||||
* @param deltaY Y movement
|
||||
*/
|
||||
|
||||
void MultiMoveRelXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
if (deltaX == 0 && deltaY == 0)
|
||||
return; // ignore no change
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// signal a change in the object
|
||||
pMultiObj->flags |= DMA_CHANGED;
|
||||
|
||||
// adjust the x position
|
||||
pMultiObj->xPos += intToFrac(deltaX);
|
||||
|
||||
// adjust the y position
|
||||
pMultiObj->yPos += intToFrac(deltaY);
|
||||
|
||||
// next obj in list
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
|
||||
} while (pMultiObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x & y anim position of all pieces of a multi-part object.
|
||||
* @param pMultiObj Multi-part object whose position is to be changed
|
||||
* @param newAniX New x animation position
|
||||
* @param newAniY New y animation position
|
||||
*/
|
||||
|
||||
void MultiSetAniXY(OBJECT *pMultiObj, int newAniX, int newAniY) {
|
||||
int curAniX, curAniY; // objects current animation position
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// get master objects current animation position
|
||||
GetAniPosition(pMultiObj, &curAniX, &curAniY);
|
||||
|
||||
// calc difference between current and new positions
|
||||
newAniX -= curAniX;
|
||||
newAniY -= curAniY;
|
||||
|
||||
// move all pieces by the difference
|
||||
MultiMoveRelXY(pMultiObj, newAniX, newAniY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x & y anim position of all pieces of a multi-part object, as well as the Z Position.
|
||||
* @param pMultiObj Multi-part object whose position is to be changed
|
||||
* @param newAniX New x animation position
|
||||
* @param newAniY New y animation position
|
||||
*/
|
||||
void MultiSetAniXYZ(OBJECT *pMultiObj, int newAniX, int newAniY, int zPosition) {
|
||||
MultiSetAniXY(pMultiObj, newAniX, newAniY);
|
||||
MultiSetZPosition(pMultiObj, zPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x anim position of all pieces of a multi-part object.
|
||||
* @param pMultiObj Multi-part object whose x position is to be changed
|
||||
* @param newAniX New x animation position
|
||||
*/
|
||||
|
||||
void MultiSetAniX(OBJECT *pMultiObj, int newAniX) {
|
||||
int curAniX, curAniY; // objects current animation position
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// get master objects current animation position
|
||||
GetAniPosition(pMultiObj, &curAniX, &curAniY);
|
||||
|
||||
// calc x difference between current and new positions
|
||||
newAniX -= curAniX;
|
||||
curAniY = 0;
|
||||
|
||||
// move all pieces by the difference
|
||||
MultiMoveRelXY(pMultiObj, newAniX, curAniY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the y anim position of all pieces of a multi-part object.
|
||||
* @param pMultiObj Multi-part object whose x position is to be changed
|
||||
* @param newAniX New y animation position
|
||||
*/
|
||||
|
||||
void MultiSetAniY(OBJECT *pMultiObj, int newAniY) {
|
||||
int curAniX, curAniY; // objects current animation position
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// get master objects current animation position
|
||||
GetAniPosition(pMultiObj, &curAniX, &curAniY);
|
||||
|
||||
// calc y difference between current and new positions
|
||||
curAniX = 0;
|
||||
newAniY -= curAniY;
|
||||
|
||||
// move all pieces by the difference
|
||||
MultiMoveRelXY(pMultiObj, curAniX, newAniY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Z position of all pieces of a multi-part object.
|
||||
* @param pMultiObj Multi-part object to be adjusted
|
||||
* @param newZ New Z order
|
||||
*/
|
||||
|
||||
void MultiSetZPosition(OBJECT *pMultiObj, int newZ) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// for all the objects that make up this multi-part
|
||||
do {
|
||||
// signal a change in the object
|
||||
pMultiObj->flags |= DMA_CHANGED;
|
||||
|
||||
// set the new z position
|
||||
pMultiObj->zPos = newZ;
|
||||
|
||||
// next obj in list
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
} while (pMultiObj != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reshape a multi-part object.
|
||||
* @param pMultiObj Multi-part object to re-shape
|
||||
*/
|
||||
|
||||
void MultiReshape(OBJECT *pMultiObj) {
|
||||
SCNHANDLE hFrame;
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// get objects current anim frame
|
||||
hFrame = pMultiObj->hShape;
|
||||
|
||||
if (hFrame != 0 && hFrame != pMultiObj->hMirror) {
|
||||
// a valid shape frame which is different from previous
|
||||
|
||||
// get pointer to frame
|
||||
const FRAME *pFrame = (const FRAME *)_vm->_handle->LockMem(hFrame);
|
||||
|
||||
// update previous
|
||||
pMultiObj->hMirror = hFrame;
|
||||
|
||||
while (READ_32(pFrame) != 0 && pMultiObj != NULL) {
|
||||
// a normal image - update the current object with this image
|
||||
AnimateObject(pMultiObj, READ_32(pFrame));
|
||||
|
||||
// move to next image for this frame
|
||||
pFrame++;
|
||||
|
||||
// move to next part of object
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
}
|
||||
|
||||
// null the remaining object parts
|
||||
while (pMultiObj != NULL) {
|
||||
// set a null image for this object part
|
||||
AnimateObject(pMultiObj, 0);
|
||||
|
||||
// move to next part of object
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
}
|
||||
} else if (hFrame == 0) {
|
||||
// update previous
|
||||
pMultiObj->hMirror = hFrame;
|
||||
|
||||
// null all the object parts
|
||||
while (pMultiObj != NULL) {
|
||||
// set a null image for this object part
|
||||
AnimateObject(pMultiObj, 0);
|
||||
|
||||
// move to next part of object
|
||||
pMultiObj = pMultiObj->pSlave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the left-most point of a multi-part object.
|
||||
* @param pMulti Multi-part object
|
||||
*/
|
||||
|
||||
int MultiLeftmost(OBJECT *pMulti) {
|
||||
int left;
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMulti));
|
||||
|
||||
// init leftmost point to first object
|
||||
left = fracToInt(pMulti->xPos);
|
||||
|
||||
// for all the objects in this multi
|
||||
while ((pMulti = pMulti->pSlave) != NULL) {
|
||||
if (pMulti->hImg != 0) {
|
||||
// non null object part
|
||||
|
||||
if (fracToInt(pMulti->xPos) < left)
|
||||
// this object is further left
|
||||
left = fracToInt(pMulti->xPos);
|
||||
}
|
||||
}
|
||||
|
||||
// return left-most point
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the right-most point of a multi-part object.
|
||||
* @param pMulti Multi-part object
|
||||
*/
|
||||
|
||||
int MultiRightmost(OBJECT *pMulti) {
|
||||
int right;
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMulti));
|
||||
|
||||
// init right-most point to first object
|
||||
right = fracToInt(pMulti->xPos) + pMulti->width;
|
||||
|
||||
// for all the objects in this multi
|
||||
while ((pMulti = pMulti->pSlave) != NULL) {
|
||||
if (pMulti->hImg != 0) {
|
||||
// non null object part
|
||||
|
||||
if (fracToInt(pMulti->xPos) + pMulti->width > right)
|
||||
// this object is further right
|
||||
right = fracToInt(pMulti->xPos) + pMulti->width;
|
||||
}
|
||||
}
|
||||
|
||||
// return right-most point
|
||||
return right - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest point of a multi-part object.
|
||||
* @param pMulti Multi-part object
|
||||
*/
|
||||
|
||||
int MultiHighest(OBJECT *pMulti) {
|
||||
int highest;
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMulti));
|
||||
|
||||
// init highest point to first object
|
||||
highest = fracToInt(pMulti->yPos);
|
||||
|
||||
// for all the objects in this multi
|
||||
while ((pMulti = pMulti->pSlave) != NULL) {
|
||||
if (pMulti->hImg != 0) {
|
||||
// non null object part
|
||||
|
||||
if (fracToInt(pMulti->yPos) < highest)
|
||||
// this object is higher
|
||||
highest = fracToInt(pMulti->yPos);
|
||||
}
|
||||
}
|
||||
|
||||
// return highest point
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lowest point of a multi-part object.
|
||||
* @param pMulti Multi-part object
|
||||
*/
|
||||
|
||||
int MultiLowest(OBJECT *pMulti) {
|
||||
int lowest;
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMulti));
|
||||
|
||||
// init lowest point to first object
|
||||
lowest = fracToInt(pMulti->yPos) + pMulti->height;
|
||||
|
||||
// for all the objects in this multi
|
||||
while ((pMulti = pMulti->pSlave) != NULL) {
|
||||
if (pMulti->hImg != 0) {
|
||||
// non null object part
|
||||
|
||||
if (fracToInt(pMulti->yPos) + pMulti->height > lowest)
|
||||
// this object is lower
|
||||
lowest = fracToInt(pMulti->yPos) + pMulti->height;
|
||||
}
|
||||
}
|
||||
|
||||
// return lowest point
|
||||
return lowest - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the object currently has an image.
|
||||
* @param pMulti Multi-part object
|
||||
*/
|
||||
|
||||
bool MultiHasShape(OBJECT *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(OBJECT *pMultiObj) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pMultiObj));
|
||||
|
||||
// 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
|
||||
146
engines/tinsel/multiobj.h
Normal file
146
engines/tinsel/multiobj.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Multi-part object definitions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_MULTIOBJ_H // prevent multiple includes
|
||||
#define TINSEL_MULTIOBJ_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/object.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct OBJECT;
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
/**
|
||||
* multi-object initialisation structure (parallels OBJ_INIT struct)
|
||||
*/
|
||||
struct MULTI_INIT {
|
||||
SCNHANDLE hMulFrame; ///< multi-objects shape - NULL terminated list of IMAGE structures
|
||||
int32 mulFlags; ///< multi-objects flags
|
||||
int32 mulID; ///< multi-objects id
|
||||
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
|
||||
|
||||
const FRAME *GetFrame() const;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Multi Object Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
OBJECT *MultiInitObject( // Initialize a multi-part object
|
||||
const MULTI_INIT *pInitTbl); // pointer to multi-object initialisation table
|
||||
|
||||
void MultiInsertObject( // Insert a multi-part object onto a object list
|
||||
OBJECT **pObjList, // list to insert multi-part object onto
|
||||
OBJECT *pInsObj); // head of multi-part object to insert
|
||||
|
||||
void MultiDeleteObject( // Delete all the pieces of a multi-part object
|
||||
OBJECT **pObjList, // list to delete multi-part object from
|
||||
OBJECT *pMultiObj); // multi-part object to be deleted
|
||||
|
||||
void MultiDeleteObjectIfExists( // Delete all the pieces of a multi-part object (if it exists)
|
||||
unsigned int playfield, // playfield to delete the objects from
|
||||
OBJECT **pMultiObj); // multi-part object to be deleted
|
||||
|
||||
void MultiHideObject( // Hide a multi-part object
|
||||
OBJECT *pMultiObj); // multi-part object to be hidden
|
||||
|
||||
void MultiHorizontalFlip( // Hortizontally flip a multi-part object
|
||||
OBJECT *pFlipObj); // head of multi-part object to flip
|
||||
|
||||
void MultiVerticalFlip( // Vertically flip a multi-part object
|
||||
OBJECT *pFlipObj); // head of multi-part object to flip
|
||||
|
||||
void MultiAdjustXY( // Adjust coords of a multi-part object. Takes into account the orientation
|
||||
OBJECT *pMultiObj, // multi-part object to be adjusted
|
||||
int deltaX, // x adjustment
|
||||
int deltaY); // y adjustment
|
||||
|
||||
void MultiMoveRelXY( // Move multi-part object relative. Does not take into account the orientation
|
||||
OBJECT *pMultiObj, // multi-part object to be moved
|
||||
int deltaX, // x movement
|
||||
int deltaY); // y movement
|
||||
|
||||
void MultiSetAniXY( // Set the x & y anim position of a multi-part object
|
||||
OBJECT *pMultiObj, // multi-part object whose position is to be changed
|
||||
int newAniX, // new x animation position
|
||||
int newAniY); // new y animation position
|
||||
|
||||
void MultiSetAniXYZ( // Set the x & y anim position of a multi-part object
|
||||
OBJECT *pMultiObj, // multi-part object whose position is to be changed
|
||||
int newAniX, // new x animation position
|
||||
int newAniY, // new y animation position
|
||||
int zPosition); // new Z order
|
||||
|
||||
void MultiSetAniX( // Set the x anim position of a multi-part object
|
||||
OBJECT *pMultiObj, // multi-part object whose x position is to be changed
|
||||
int newAniX); // new x animation position
|
||||
|
||||
void MultiSetAniY( // Set the y anim position of a multi-part object
|
||||
OBJECT *pMultiObj, // multi-part object whose y position is to be adjusted
|
||||
int newAniY); // new y animation position
|
||||
|
||||
void MultiSetZPosition( // Sets the z position of a multi-part object
|
||||
OBJECT *pMultiObj, // multi-part object to be adjusted
|
||||
int newZ); // new Z order
|
||||
|
||||
void MultiReshape( // Reshape a multi-part object
|
||||
OBJECT *pMultiObj); // multi-part object to re-shape
|
||||
|
||||
Common::Rect MultiBounds( // Returns the bounds of a multi-part object
|
||||
OBJECT *pMulti); // multi-part object
|
||||
|
||||
int MultiLeftmost( // Returns the left-most point of a multi-part object
|
||||
OBJECT *pMulti); // multi-part object
|
||||
|
||||
int MultiRightmost( // Returns the right-most point of a multi-part object
|
||||
OBJECT *pMulti); // multi-part object
|
||||
|
||||
int MultiHighest( // Returns the highest point of a multi-part object
|
||||
OBJECT *pMulti); // 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
|
||||
OBJECT *pMulti); // multi-part object
|
||||
|
||||
void MultiForceRedraw(
|
||||
OBJECT *pMultiObj); // multi-part object to be forced
|
||||
|
||||
struct FREEL;
|
||||
OBJECT *InsertReelObj(const FREEL *reels);
|
||||
struct FILM;
|
||||
enum class SysReel;
|
||||
const FILM *GetSystemReelFilm(SysReel reelIndex);
|
||||
OBJECT *InsertSystemReelObj(SysReel reelIndex);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_MULTIOBJ_H
|
||||
1147
engines/tinsel/music.cpp
Normal file
1147
engines/tinsel/music.cpp
Normal file
File diff suppressed because it is too large
Load Diff
207
engines/tinsel/music.h
Normal file
207
engines/tinsel/music.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Music class
|
||||
|
||||
#ifndef TINSEL_MUSIC_H
|
||||
#define TINSEL_MUSIC_H
|
||||
|
||||
#include "audio/midiplayer.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "common/mutex.h"
|
||||
|
||||
class MidiParser;
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
class Music {
|
||||
public:
|
||||
Music() : _currentMidi(0), _currentLoop(false) {
|
||||
_midiBuffer.pDat = nullptr;
|
||||
_midiBuffer.size = 0;
|
||||
}
|
||||
|
||||
bool PlayMidiSequence( // Plays the specified MIDI sequence through the sound driver
|
||||
uint32 dwFileOffset, // handle of MIDI sequence data
|
||||
bool bLoop); // Whether to loop the sequence
|
||||
|
||||
bool MidiPlaying(); // Returns TRUE if a Midi tune is currently playing
|
||||
|
||||
bool StopMidi(); // Stops any currently playing midi
|
||||
|
||||
void SetMidiVolume( // Sets the volume of the MIDI music. Returns the old volume
|
||||
int vol); // new volume - 0..MAXMIDIVOL
|
||||
|
||||
int GetMidiVolume();
|
||||
|
||||
void OpenMidiFiles();
|
||||
void DeleteMidiBuffer();
|
||||
|
||||
void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop);
|
||||
void RestoreMidiFacts(SCNHANDLE Midi, bool Loop);
|
||||
|
||||
int GetTrackNumber(SCNHANDLE hMidi);
|
||||
SCNHANDLE GetTrackOffset(int trackNumber);
|
||||
|
||||
uint8 *GetMidiBuffer() { return _midiBuffer.pDat; }
|
||||
|
||||
uint8* ResizeMidiBuffer(uint32 newSize) {
|
||||
if (_midiBuffer.size < newSize) {
|
||||
_midiBuffer.pDat = (byte*)realloc(_midiBuffer.pDat, newSize);
|
||||
assert(_midiBuffer.pDat);
|
||||
}
|
||||
|
||||
return _midiBuffer.pDat;
|
||||
}
|
||||
|
||||
void dumpMusic();
|
||||
|
||||
private:
|
||||
// sound buffer structure used for MIDI data and samples
|
||||
struct SOUND_BUFFER {
|
||||
uint8 *pDat; // pointer to actual buffer
|
||||
uint32 size; // size of the buffer
|
||||
};
|
||||
|
||||
// MIDI buffer
|
||||
SOUND_BUFFER _midiBuffer;
|
||||
|
||||
SCNHANDLE _currentMidi;
|
||||
bool _currentLoop;
|
||||
|
||||
// We allocate 155 entries because that's the maximum, used in the SCN version
|
||||
SCNHANDLE _midiOffsets[155];
|
||||
};
|
||||
|
||||
class MidiMusicPlayer : public Audio::MidiPlayer {
|
||||
public:
|
||||
MidiMusicPlayer(TinselEngine *vm);
|
||||
|
||||
void setVolume(int volume) override;
|
||||
|
||||
void playMIDI(uint32 size, bool loop);
|
||||
|
||||
// void stop();
|
||||
void pause() override;
|
||||
void resume() override;
|
||||
|
||||
// MidiDriver_BASE interface implementation
|
||||
void send(uint32 b) override;
|
||||
|
||||
// The original sets the "sequence timing" to 109 Hz, whatever that
|
||||
// means. The default is 120.
|
||||
uint32 getBaseTempo() { return _driver ? (109 * _driver->getBaseTempo()) / 120 : 0; }
|
||||
|
||||
bool _milesAudioMode;
|
||||
|
||||
private:
|
||||
void playXMIDI(uint32 size, bool loop);
|
||||
void playSEQ(uint32 size, bool loop);
|
||||
};
|
||||
|
||||
class PCMMusicPlayer : public Audio::AudioStream {
|
||||
public:
|
||||
PCMMusicPlayer();
|
||||
~PCMMusicPlayer() override;
|
||||
|
||||
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) override;
|
||||
bool isStereo() const override;
|
||||
bool endOfData() const override { return _end; }
|
||||
bool endOfStream() const override { return false; }
|
||||
int getRate() const override;
|
||||
|
||||
protected:
|
||||
enum State {
|
||||
S_IDLE,
|
||||
S_NEW,
|
||||
S_MID,
|
||||
S_END1,
|
||||
S_END2,
|
||||
S_END3,
|
||||
S_NEXT,
|
||||
S_STOP
|
||||
};
|
||||
|
||||
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;
|
||||
Common::Path _filename;
|
||||
Common::File _file;
|
||||
|
||||
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();
|
||||
|
||||
void loadMusicFromSegment(int segmentNum);
|
||||
void loadADPCMMusicFromSegment(int segmentNum);
|
||||
void loadMP3MusicFromSegment(int segmentNum);
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
125
engines/tinsel/noir/lzss.cpp
Normal file
125
engines/tinsel/noir/lzss.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Prototypes of actor functions
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "tinsel/noir/lzss.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
static byte HIGH_BITS(byte byteValue, int numBits) {
|
||||
unsigned int mask = ((1 << numBits) - 1) << (8 - numBits);
|
||||
return (byteValue & mask) >> (8 - numBits);
|
||||
}
|
||||
|
||||
static byte LOW_BITS(byte byteValue, int numBits) {
|
||||
unsigned int mask = ((1 << numBits) - 1);
|
||||
return byteValue & mask;
|
||||
}
|
||||
|
||||
int decompressLZSS(Common::SeekableReadStream &input, byte *output) {
|
||||
static const int kDictionarySize = 4096;
|
||||
byte dictionary[kDictionarySize] = {};
|
||||
int dictionaryOffset = 1;
|
||||
int outputOffset = 0;
|
||||
|
||||
byte *data = new byte[input.size()];
|
||||
input.read(data, input.size());
|
||||
unsigned int offset = 0;
|
||||
|
||||
int bitShift = 0;
|
||||
int bytesWritten = 0;
|
||||
|
||||
while (true) {
|
||||
byte value = data[offset];
|
||||
byte bitMask = 0x80 >> bitShift++;
|
||||
// First bit:
|
||||
// 0 -> Copy data from dictionary
|
||||
// 1 -> Copy raw byte from input
|
||||
bool useRawByte = value & bitMask;
|
||||
if (bitShift == 8) {
|
||||
bitShift = 0;
|
||||
offset++;
|
||||
}
|
||||
if (!useRawByte) {
|
||||
unsigned int bitsFromFirst = 8 - bitShift;
|
||||
unsigned int bitsFromLast = 16 - 8 - bitsFromFirst;
|
||||
|
||||
// The dictionary lookup is 16 bit:
|
||||
// 12 bits for the offset
|
||||
// 4 bits for the run-length
|
||||
|
||||
// Combined with the first bit this makes for 17 bits,
|
||||
// So we will be reading from three bytes, except when
|
||||
// the first bit was read from the end of a byte, then
|
||||
// bitShift will be 0, and bitsFromLast will be 8.
|
||||
|
||||
// We make the assumption that we can dereference the third byte
|
||||
// even if we aren't using it. We will check "offset" after decompression
|
||||
// to verify this assumption.
|
||||
unsigned int byte1 = LOW_BITS(data[offset], bitsFromFirst);
|
||||
unsigned int byte2 = data[offset + 1];
|
||||
unsigned int byte3 = HIGH_BITS(data[offset + 2], bitsFromLast);
|
||||
|
||||
unsigned int lookup = (byte1 << (8 + bitsFromLast)) | (byte2 << bitsFromLast) | byte3;
|
||||
|
||||
int lookupOffset = (lookup >> 4) & 0xFFF;
|
||||
if (lookupOffset == 0) {
|
||||
break;
|
||||
}
|
||||
int lookupRunLength = (lookup & 0xF) + 2;
|
||||
for (int j = 0; j < lookupRunLength; j++) {
|
||||
output[outputOffset++] = dictionary[(lookupOffset + j) % kDictionarySize];
|
||||
dictionary[dictionaryOffset++] = dictionary[(lookupOffset + j) % kDictionarySize];
|
||||
dictionaryOffset %= kDictionarySize;
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
bytesWritten += lookupRunLength;
|
||||
} else {
|
||||
// Raw byte, but since we spent a bit first,
|
||||
// we must reassemble it from potentially two bytes.
|
||||
unsigned int bitsFromFirst = 8 - bitShift;
|
||||
unsigned int bitsFromLast = 8 - bitsFromFirst;
|
||||
|
||||
byte byteValue = LOW_BITS(data[offset], bitsFromFirst) << bitsFromLast;
|
||||
byteValue |= HIGH_BITS(data[offset + 1], bitsFromLast);
|
||||
|
||||
offset++;
|
||||
|
||||
output[outputOffset++] = byteValue;
|
||||
dictionary[dictionaryOffset++] = byteValue;
|
||||
dictionaryOffset %= kDictionarySize;
|
||||
|
||||
bytesWritten++;
|
||||
}
|
||||
|
||||
}
|
||||
delete[] data;
|
||||
|
||||
if ((int32)offset > input.size()) {
|
||||
error("Read too far during decompression");
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
}
|
||||
34
engines/tinsel/noir/lzss.h
Normal file
34
engines/tinsel/noir/lzss.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Prototypes of actor functions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_NOIR_LZSS_H
|
||||
#define TINSEL_NOIR_LZSS_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
int decompressLZSS(Common::SeekableReadStream &input, byte *output);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
322
engines/tinsel/noir/notebook.cpp
Normal file
322
engines/tinsel/noir/notebook.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tinsel/noir/notebook.h"
|
||||
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/dialogs.h"
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/noir/sysreel.h"
|
||||
#include "tinsel/pdisplay.h"
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/timers.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
Notebook::Notebook() {
|
||||
_polygons = instantiateNoteBookPolygons();
|
||||
}
|
||||
|
||||
Notebook::~Notebook() {
|
||||
delete _polygons;
|
||||
}
|
||||
|
||||
void Notebook::addHyperlink(int32 id1, int32 id2) {
|
||||
auto *invObject = _vm->_dialogs->getInvObjectT3(id1);
|
||||
|
||||
if (invObject->getTitle() != 0) {
|
||||
error("A clue can only be hyperlinked if it only has one title!");
|
||||
return;
|
||||
}
|
||||
|
||||
invObject = _vm->_dialogs->getInvObjectT3(id2);
|
||||
|
||||
if (invObject->getTitle() != 0) {
|
||||
error("A clue can only be hyperlinked if it only has one title!");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 i;
|
||||
for (i = 0; i < MAX_HYPERS; ++i) {
|
||||
int32 curr_id1 = _hyperlinks[i].id1;
|
||||
if (curr_id1 == 0) {
|
||||
_hyperlinks[i].id1 = id1;
|
||||
_hyperlinks[i].id2 = id2;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((curr_id1 == id1) || (id1 == _hyperlinks[i].id2)) {
|
||||
if ((curr_id1 != id2) && (id2 != _hyperlinks[i].id2)) {
|
||||
error("A clue/title can only be hyperlinked to one other clue/title!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No free hyperlink entry was found
|
||||
error("Too many hyperlinks");
|
||||
}
|
||||
|
||||
void Notebook::clearNotebookPage() {
|
||||
if (_prevPage != -1) {
|
||||
_pages[_prevPage].clear();
|
||||
}
|
||||
_prevPage = -1;
|
||||
_pages[_currentPage].clear();
|
||||
}
|
||||
|
||||
void Notebook::refresh() {
|
||||
auto reel = (_currentPage == 0 ? SysReel::NOTEPAD_CLOSED : SysReel::NOTEPAD_OPEN);
|
||||
auto film = GetSystemReelFilm(reel);
|
||||
InitStepAnimScript(&_anim, _object, film->reels->script, ONE_SECOND / film->frate);
|
||||
clearNotebookPage();
|
||||
if (_currentPage != 0) {
|
||||
_pages[_currentPage].fillIn();
|
||||
}
|
||||
}
|
||||
|
||||
int Notebook::addTitle(const InventoryObjectT3 &invObject) {
|
||||
int id = invObject.getId();
|
||||
assert(invObject.isNotebookTitle());
|
||||
for (uint32 i = 0; i < _numPages; i++) {
|
||||
if (_pages[i].getTitle() == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
int linkedFromPage = invObject.getUnknown();
|
||||
|
||||
// 0 page is the closed notebook, has no entries.
|
||||
if (linkedFromPage != 0) {
|
||||
// Allocate a line on the linked from page.
|
||||
assert(_pages[linkedFromPage].getTitle() != 0);
|
||||
_pages[linkedFromPage].addLine(id);
|
||||
}
|
||||
int pageIndex = _numPages++;
|
||||
_pages[pageIndex].setTitle(id);
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
void Notebook::addClue(const InventoryObjectT3 &invObject) {
|
||||
if (invObject.getUnknown() == 0) {
|
||||
// This affects two clues, that should get special treatment.
|
||||
warning("TODO: Handle clues with no parent page");
|
||||
return;
|
||||
}
|
||||
// Add title if missing, otherwise just get the page it's on.
|
||||
auto titleObject = _vm->_dialogs->getInvObjectT3(invObject.getUnknown());
|
||||
int pageIndex = addTitle(*titleObject);
|
||||
_pages[pageIndex].addLine(invObject.getId());
|
||||
if (invObject.getTitle() != 0) {
|
||||
auto secondTitleObject = _vm->_dialogs->getInvObjectT3(invObject.getTitle());
|
||||
pageIndex = addTitle(*secondTitleObject);
|
||||
_pages[pageIndex].addLine(invObject.getId());
|
||||
}
|
||||
}
|
||||
|
||||
void Notebook::addClue(int id) {
|
||||
auto invObject = _vm->_dialogs->getInvObjectT3(id);
|
||||
if (invObject->isNotebookTitle()) {
|
||||
addTitle(*invObject);
|
||||
} else {
|
||||
addClue(*invObject);
|
||||
}
|
||||
}
|
||||
|
||||
int Notebook::getPageWithTitle(int id) {
|
||||
for (uint32 i = 0; i < _numPages; i++) {
|
||||
if (_pages[i].getTitle() == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Notebook::crossClue(int id) {
|
||||
auto invObject = _vm->_dialogs->getInvObjectT3(id);
|
||||
if (invObject->isNotebookTitle()) {
|
||||
return;
|
||||
}
|
||||
int titles[2] = {
|
||||
invObject->getUnknown(),
|
||||
invObject->getTitle()
|
||||
};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (titles[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
int page = getPageWithTitle(titles[i]);
|
||||
if (page != -1) {
|
||||
_pages[page].crossClue(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitNotebookAnim(OBJECT **obj, ANIM &anim, SysReel reel, int zPosition) {
|
||||
auto film = GetSystemReelFilm(reel);
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, obj);
|
||||
*obj = InsertReelObj(film->reels);
|
||||
MultiSetZPosition(*obj, zPosition);
|
||||
InitStepAnimScript(&anim, *obj, film->reels->script, ONE_SECOND / film->frate);
|
||||
}
|
||||
|
||||
void Notebook::setNextPage(int pageIndex) {
|
||||
assert(_prevPage == -1 || _prevPage == (int32)_currentPage); // Check that we've cleaned any outstanding page.
|
||||
_prevPage = _currentPage;
|
||||
_currentPage = pageIndex;
|
||||
}
|
||||
|
||||
void Notebook::pageFlip(bool up) {
|
||||
int nextPage = _currentPage + (up ? -1 : 1);
|
||||
if (nextPage <= 0) {
|
||||
setNextPage(0);
|
||||
refresh();
|
||||
return;
|
||||
} else if (nextPage == 1) {
|
||||
// TODO: Should possibly just call whatever function we use to open.
|
||||
InitNotebookAnim(&_object, _anim, SysReel::NOTEPAD_OPENING, Z_INV_RFRAME);
|
||||
_state = BOOKSTATE::OPEN_ANIMATING;
|
||||
setNextPage(nextPage);
|
||||
return;
|
||||
}
|
||||
setNextPage(nextPage);
|
||||
SysReel reel = (up ? SysReel::NOTEPAD_FLIPUP : SysReel::NOTEPAD_FLIPDOWN);
|
||||
InitNotebookAnim(&_pageObject, _pageAnim, reel, 19);
|
||||
_state = BOOKSTATE::PAGEFLIP;
|
||||
}
|
||||
|
||||
void Notebook::show(bool isOpen) {
|
||||
auto reel = (isOpen ? SysReel::NOTEPAD_OPEN : SysReel::NOTEPAD_OPENING);
|
||||
InitNotebookAnim(&_object, _anim, reel, Z_INV_MFRAME);
|
||||
|
||||
_state = (isOpen ? BOOKSTATE::OPENED : BOOKSTATE::OPEN_ANIMATING);
|
||||
setNextPage(1);
|
||||
refresh();
|
||||
DisableTags(); // Tags disabled in Notebook
|
||||
DisablePointing(); // Pointing disabled in Notebook
|
||||
}
|
||||
|
||||
bool Notebook::isOpen() const {
|
||||
return _state != BOOKSTATE::CLOSED;
|
||||
}
|
||||
|
||||
void Notebook::close() {
|
||||
clearNotebookPage();
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_object);
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_pageObject);
|
||||
_state = BOOKSTATE::CLOSED;
|
||||
if (_vm->_dialogs->inventoryOrNotebookActive()) {
|
||||
EnablePointing();
|
||||
EnableTags();
|
||||
}
|
||||
}
|
||||
|
||||
void Notebook::stepAnimScripts() {
|
||||
if (_state == BOOKSTATE::OPEN_ANIMATING) {
|
||||
auto state = StepAnimScript(&_anim);
|
||||
if (state == ScriptFinished) {
|
||||
_state = BOOKSTATE::OPENED;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
if (_state == BOOKSTATE::PAGEFLIP) {
|
||||
auto state = StepAnimScript(&_pageAnim);
|
||||
if (state == ScriptFinished) {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_pageObject);
|
||||
_state = BOOKSTATE::OPENED;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 Notebook::GetPointedClue(const Common::Point &point) const {
|
||||
if (_currentPage == 0 || _currentPage > _numPages) {
|
||||
return 0;
|
||||
}
|
||||
return _pages[_currentPage].getClueForLine(_polygons->lineHit(point));
|
||||
}
|
||||
|
||||
bool Notebook::handlePointer(const Common::Point &point) {
|
||||
if (!isOpen()) {
|
||||
return 0;
|
||||
}
|
||||
auto inside = _polygons->isInsideNotebook(point);
|
||||
if (inside) {
|
||||
auto hit = _polygons->lineHit(point);
|
||||
_pages[_currentPage].handlePointAtLine(hit);
|
||||
return true; // We handled the pointer
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Notebook::handleEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
|
||||
if (!isOpen()) { // TODO: Clicking outside should close the notebook
|
||||
return false;
|
||||
}
|
||||
auto inside = _polygons->isInsideNotebook(coOrds);
|
||||
switch(pEvent) {
|
||||
case PLR_ACTION:
|
||||
if (inside) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case PLR_LOOK:
|
||||
if (inside) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case PLR_WALKTO: {
|
||||
// Handle clue-clicks
|
||||
auto poly = _polygons->mostSpecificHit(coOrds);
|
||||
switch (poly) {
|
||||
case NoteBookPoly::NEXT:
|
||||
handleEvent(PLR_PGUP, coOrds);
|
||||
return true;
|
||||
case NoteBookPoly::PREV:
|
||||
handleEvent(PLR_PGDN, coOrds);
|
||||
return true;
|
||||
case NoteBookPoly::NONE:
|
||||
handleEvent(PLR_ESCAPE, coOrds);
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
case PLR_ESCAPE:
|
||||
close();
|
||||
return true;
|
||||
case PLR_PGUP:
|
||||
pageFlip(true);
|
||||
return true;
|
||||
case PLR_PGDN:
|
||||
pageFlip(false);
|
||||
return true;
|
||||
case PLR_HOME:
|
||||
case PLR_END:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
118
engines/tinsel/noir/notebook.h
Normal file
118
engines/tinsel/noir/notebook.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_NOTEBOOK_H // prevent multiple includes
|
||||
#define TINSEL_NOTEBOOK_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "notebook_page.h"
|
||||
#include "tinsel/anim.h"
|
||||
#include "tinsel/events.h"
|
||||
#include "tinsel/object.h"
|
||||
|
||||
namespace Tinsel {
|
||||
// links two clue/title objects together
|
||||
struct HYPERLINK {
|
||||
int32 id1 = 0;
|
||||
int32 id2 = 0;
|
||||
};
|
||||
|
||||
// 6 bytes large
|
||||
struct ENTRY {
|
||||
int32 id;
|
||||
bool active;
|
||||
int32 page1;
|
||||
int32 indexOnPage1;
|
||||
int32 page2;
|
||||
int32 indexOnPage2;
|
||||
};
|
||||
|
||||
enum class BOOKSTATE {
|
||||
CLOSED = 0,
|
||||
OPEN_UNKNOWN = 1,
|
||||
OPEN_ANIMATING = 2,
|
||||
OPENED = 3,
|
||||
PAGEFLIP = 4
|
||||
};
|
||||
|
||||
class NoteBookPolygons;
|
||||
class InventoryObjectT3;
|
||||
|
||||
class Notebook {
|
||||
public:
|
||||
Notebook();
|
||||
~Notebook();
|
||||
|
||||
// Adds a connection between a clue/title
|
||||
void addHyperlink(int32 id1, int32 id2);
|
||||
void addClue(int id);
|
||||
void crossClue(int id);
|
||||
// Called within InventoryProcess loop
|
||||
void redraw();
|
||||
|
||||
// Called from OPENNOTEBOOK
|
||||
void show(bool isOpen);
|
||||
bool isOpen() const;
|
||||
void close();
|
||||
|
||||
bool handlePointer(const Common::Point &point);
|
||||
bool handleEvent(PLR_EVENT pEvent, const Common::Point &coOrds);
|
||||
void stepAnimScripts();
|
||||
void refresh();
|
||||
|
||||
NoteBookPolygons *_polygons = nullptr;
|
||||
private:
|
||||
int addTitle(const InventoryObjectT3 &invObject);
|
||||
void addClue(const InventoryObjectT3 &invObject);
|
||||
int getPageWithTitle(int id);
|
||||
|
||||
void pageFlip(bool up);
|
||||
|
||||
int32 GetPointedClue(const Common::Point &point) const;
|
||||
|
||||
void clearNotebookPage();
|
||||
|
||||
void setNextPage(int pageIndex);
|
||||
|
||||
const static uint32 MAX_PAGES = 0x15;
|
||||
const static uint32 MAX_HYPERS = 0xf;
|
||||
|
||||
HYPERLINK _hyperlinks[MAX_HYPERS];
|
||||
|
||||
uint32 _numPages = 1;
|
||||
int32 _prevPage = -1;
|
||||
uint32 _currentPage = 1;
|
||||
|
||||
NotebookPage _pages[MAX_PAGES] = {};
|
||||
|
||||
ANIM _anim = {};
|
||||
OBJECT *_object = nullptr;
|
||||
|
||||
ANIM _pageAnim = {};
|
||||
OBJECT *_pageObject = nullptr;
|
||||
|
||||
BOOKSTATE _state = BOOKSTATE::CLOSED;
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
162
engines/tinsel/noir/notebook_page.cpp
Normal file
162
engines/tinsel/noir/notebook_page.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tinsel/noir/notebook_page.h"
|
||||
#include "tinsel/dialogs.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/timers.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/noir/sysreel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
void NotebookLine::clear() {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_obj);
|
||||
}
|
||||
|
||||
bool HasReelFrame(SCNHANDLE pReel) {
|
||||
if (pReel) {
|
||||
const FREEL* reel = (const FREEL*)_vm->_handle->LockMem(pReel);
|
||||
if (reel && reel->mobj) {
|
||||
const MULTI_INIT* pmi = reel->GetMultiInit();
|
||||
if (pmi) {
|
||||
return pmi->GetFrame() != nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int FindReelIndexForEntry(const FILM *pFilm, int pageLine) {
|
||||
if (HasReelFrame(pFilm->reels[pageLine].mobj)) {
|
||||
return pageLine;
|
||||
}
|
||||
for (int i = pageLine; i < pFilm->numreels; i++) {
|
||||
if (HasReelFrame(pFilm->reels[i].mobj)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (int i = pageLine; i >= 0; i--) {
|
||||
if (HasReelFrame(pFilm->reels[i].mobj)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void NotebookLine::fillIn(int pageLine) {
|
||||
const FILM *pFilm = _vm->_dialogs->getObjectFilm(_id);
|
||||
if (!pFilm)
|
||||
return;
|
||||
|
||||
int reelIndex = FindReelIndexForEntry(pFilm, pageLine);
|
||||
assert(reelIndex >= 0);
|
||||
const FREEL *reel = &pFilm->reels[reelIndex];
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_obj);
|
||||
_obj = InsertReelObj(reel);
|
||||
|
||||
MultiSetZPosition(_obj, 17);
|
||||
InitStepAnimScript(&_anim, _obj, pFilm->reels[reelIndex].script, ONE_SECOND / pFilm->frate);
|
||||
|
||||
if (_crossedOut) {
|
||||
auto scribbleFilm = GetSystemReelFilm(SysReel::SCRIBBLES);
|
||||
_scribbles = InsertReelObj(&scribbleFilm->reels[reelIndex]);
|
||||
MultiSetZPosition(_scribbles, 18);
|
||||
|
||||
InitStepAnimScript(&_scribbleAnim, _scribbles, scribbleFilm->reels[reelIndex].script, ONE_SECOND / pFilm->frate);
|
||||
}
|
||||
}
|
||||
|
||||
void NotebookLine::crossOut() {
|
||||
_crossedOut = true;
|
||||
}
|
||||
|
||||
void NotebookPage::handlePointAtLine(int line) {
|
||||
auto objId = getClueForLine(line);
|
||||
if (objId != 0 && objId != _pointedClue) {
|
||||
auto obj = _vm->_dialogs->getInvObject(objId);
|
||||
_vm->_dialogs->invPointEvent(obj, -1);
|
||||
_pointedClue = objId;
|
||||
}
|
||||
}
|
||||
|
||||
int NotebookPage::indexOfClue(int id) const {
|
||||
for (uint32 i = 0; i < _numLines; i++) {
|
||||
if (_lines[i]._id == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool NotebookPage::containsClue(int id) {
|
||||
return indexOfClue(id) != -1;
|
||||
}
|
||||
|
||||
void NotebookPage::crossClue(int id) {
|
||||
int index = indexOfClue(id);
|
||||
assert(index != -1);
|
||||
_lines[index].crossOut();
|
||||
}
|
||||
|
||||
void NotebookPage::addLine(int id) {
|
||||
if (containsClue(id)) {
|
||||
return;
|
||||
}
|
||||
assert(_numLines < MAX_ENTRIES_PER_PAGE);
|
||||
_lines[_numLines++]._id = id;
|
||||
}
|
||||
|
||||
void NotebookPage::setTitle(int id) {
|
||||
_lines[0]._id = id;
|
||||
if (_numLines == 0) {
|
||||
_numLines++;
|
||||
}
|
||||
}
|
||||
int32 NotebookPage::getTitle() const {
|
||||
return _lines[0]._id;
|
||||
}
|
||||
|
||||
void NotebookPage::fillIn() {
|
||||
for (uint32 i = 0; i < _numLines; i++) {
|
||||
_lines[i].fillIn(i);
|
||||
}
|
||||
}
|
||||
|
||||
void NotebookPage::clear() {
|
||||
for (uint32 i = 0; i < _numLines; i++) {
|
||||
_lines[i].clear();
|
||||
}
|
||||
_pointedClue = -1;
|
||||
}
|
||||
|
||||
int NotebookPage::getClueForLine(int line) const {
|
||||
if (line >= (int)_numLines) {
|
||||
return 0;
|
||||
}
|
||||
return _lines[line]._id;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
69
engines/tinsel/noir/notebook_page.h
Normal file
69
engines/tinsel/noir/notebook_page.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_NOTEBOOK_PAGE_H // prevent multiple includes
|
||||
#define TINSEL_NOTEBOOK_PAGE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "tinsel/anim.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/object.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
class NotebookLine {
|
||||
public:
|
||||
int _id = 0;
|
||||
void crossOut();
|
||||
void clear();
|
||||
void fillIn(int pageLine);
|
||||
private:
|
||||
bool _crossedOut = false;
|
||||
ANIM _anim = {};
|
||||
OBJECT *_obj = nullptr;
|
||||
OBJECT *_scribbles = nullptr;
|
||||
ANIM _scribbleAnim = {};
|
||||
};
|
||||
|
||||
class NotebookPage {
|
||||
public:
|
||||
bool containsClue(int id);
|
||||
void crossClue(int id);
|
||||
void addLine(int id);
|
||||
void setTitle(int id);
|
||||
int32 getTitle() const;
|
||||
void fillIn();
|
||||
void clear();
|
||||
int getPointedClue(const Common::Point &point) const;
|
||||
int getClueForLine(int line) const;
|
||||
void handlePointAtLine(int line);
|
||||
private:
|
||||
int indexOfClue(int id) const;
|
||||
|
||||
int _pointedClue = -1;
|
||||
const static uint32 MAX_ENTRIES_PER_PAGE = 8;
|
||||
NotebookLine _lines[MAX_ENTRIES_PER_PAGE] = {};
|
||||
uint32 _numLines = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // SCUMMVM_NOTEBOOK_PAGE_H
|
||||
1207
engines/tinsel/noir/spriter.cpp
Normal file
1207
engines/tinsel/noir/spriter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
300
engines/tinsel/noir/spriter.h
Normal file
300
engines/tinsel/noir/spriter.h
Normal file
@@ -0,0 +1,300 @@
|
||||
/* 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.
|
||||
*
|
||||
* Spriter - 3D Renderer
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SPRITER_H
|
||||
#define TINSEL_SPRITER_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stack.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
#include "math/vector2d.h"
|
||||
#include "math/matrix4.h"
|
||||
|
||||
#if defined(USE_TINYGL)
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
#endif
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
typedef Common::FixedStack<Math::Matrix4, 30> MatrixStack;
|
||||
|
||||
enum RenderProgramOp : uint16 {
|
||||
MATRIX_DUPLICATE = 1,
|
||||
MATRIX_REMOVE = 2,
|
||||
UNUSED = 3,
|
||||
TRANSFORM = 4,
|
||||
TRANSLATE_X = 5,
|
||||
TRANSLATE_Y = 6,
|
||||
TRANSLATE_Z = 7,
|
||||
TRANSLATE_XYZ = 8,
|
||||
ROTATE_X = 9,
|
||||
ROTATE_Y = 10,
|
||||
ROTATE_Z = 11,
|
||||
ROTATE_XYZ = 17,
|
||||
STOP = 16,
|
||||
};
|
||||
|
||||
struct AnimationInfo {
|
||||
Common::String name;
|
||||
|
||||
uint meshNum;
|
||||
|
||||
uint translateTablesHunk;
|
||||
uint translateTables;
|
||||
uint translateNum;
|
||||
|
||||
uint rotateTablesHunk;
|
||||
uint rotateTables;
|
||||
uint rotateNum;
|
||||
|
||||
uint scaleTablesHunk;
|
||||
uint scaleTables;
|
||||
uint scaleNum;
|
||||
|
||||
uint maxFrame;
|
||||
};
|
||||
|
||||
struct MeshInfo {
|
||||
uint meshTablesHunk;
|
||||
uint meshTables;
|
||||
|
||||
uint programHunk;
|
||||
uint program;
|
||||
};
|
||||
|
||||
struct Hunk {
|
||||
Common::Array<uint8> data;
|
||||
Common::Array<uint> mappingIdx;
|
||||
uint size;
|
||||
uint flags;
|
||||
};
|
||||
|
||||
typedef Common::Array<Hunk> Hunks;
|
||||
|
||||
typedef Common::Array<Math::Vector3d> Vectors;
|
||||
|
||||
struct Primitive {
|
||||
uint indices[8];
|
||||
uint color;
|
||||
Math::Vector2d uv[4];
|
||||
uint texture;
|
||||
};
|
||||
|
||||
enum MeshPartType {
|
||||
MESH_PART_TYPE_COLOR,
|
||||
MESH_PART_TYPE_SOLID,
|
||||
MESH_PART_TYPE_TEXTURE,
|
||||
};
|
||||
|
||||
struct MeshPart {
|
||||
MeshPartType type;
|
||||
uint cull;
|
||||
uint numVertices;
|
||||
Common::Array<Primitive> primitives;
|
||||
};
|
||||
|
||||
struct Mesh {
|
||||
Common::Array<Math::Vector3d> vertices;
|
||||
Common::Array<Math::Vector3d> normals;
|
||||
Common::Array<MeshPart> parts;
|
||||
Common::Array<MeshPart> parts2;
|
||||
};
|
||||
|
||||
struct Meshes {
|
||||
uint vertexCount;
|
||||
uint normalCount;
|
||||
|
||||
Common::Array<Mesh> meshes;
|
||||
};
|
||||
|
||||
typedef Common::Array<Vectors> AnimationData;
|
||||
|
||||
struct ModelTables {
|
||||
Vectors translations;
|
||||
Vectors rotations;
|
||||
Vectors scales;
|
||||
Meshes meshes;
|
||||
};
|
||||
|
||||
enum ModelFlags {
|
||||
MODEL_HAS_TRANSLATION_TABLE = 1,
|
||||
MODEL_HAS_SCALE_TABLE = 2,
|
||||
MODEL_HAS_ROTATION_TABLE = 4
|
||||
};
|
||||
|
||||
struct Model {
|
||||
Hunks hunks;
|
||||
Hunks hunksOverlay; // The game script can load additional data to supplement the main model. E.g. a special animation.
|
||||
uint animationCount;
|
||||
uint field_0xe;
|
||||
uint field_0xf;
|
||||
uint8* program;
|
||||
|
||||
// animation tables
|
||||
AnimationData startTranslateTables;
|
||||
AnimationData startRotateTables;
|
||||
AnimationData startScaleTables;
|
||||
AnimationData endTranslateTables;
|
||||
AnimationData endRotateTables;
|
||||
AnimationData endScaleTables;
|
||||
int startFrame;
|
||||
int endFrame;
|
||||
|
||||
uint flags;
|
||||
uint field_0x32;
|
||||
uint field_0x33;
|
||||
|
||||
ModelTables tables;
|
||||
|
||||
Math::Vector3d position;
|
||||
Math::Vector3d rotation;
|
||||
Math::Vector3d scale;
|
||||
|
||||
uint time; // interpolant
|
||||
};
|
||||
|
||||
struct Viewport {
|
||||
int ap;
|
||||
float width;
|
||||
float height;
|
||||
|
||||
Common::Rect rect;
|
||||
};
|
||||
|
||||
struct View {
|
||||
int centerX;
|
||||
int centerY;
|
||||
|
||||
Common::Rect viewRect;
|
||||
Common::Rect screenRect;
|
||||
|
||||
Viewport viewport;
|
||||
|
||||
Math::Vector3d position;
|
||||
Math::Vector3d rotation;
|
||||
};
|
||||
|
||||
class Spriter {
|
||||
private:
|
||||
MatrixStack _modelMatrix;
|
||||
MatrixStack* _currentMatrix;
|
||||
|
||||
Common::Array<AnimationInfo> _animMain;
|
||||
AnimationInfo _animShadow;
|
||||
|
||||
MeshInfo _meshMain;
|
||||
Common::Array<MeshInfo> _meshShadow;
|
||||
|
||||
View _view;
|
||||
|
||||
Common::Array<uint8> _palette;
|
||||
Common::Array<uint8> _textureData;
|
||||
|
||||
bool _textureGenerated;
|
||||
#if defined(USE_TINYGL)
|
||||
uint _texture[4];
|
||||
#endif
|
||||
|
||||
bool _modelIdle;
|
||||
|
||||
uint _animId;
|
||||
uint _animSpeed;
|
||||
uint _animDelay;
|
||||
uint _animDelayMax;
|
||||
|
||||
uint _sequencesCount;
|
||||
|
||||
uint _direction;
|
||||
|
||||
public:
|
||||
Model _modelMain;
|
||||
Model _modelShadow;
|
||||
|
||||
public:
|
||||
Spriter();
|
||||
virtual ~Spriter();
|
||||
|
||||
void Init(int width, int height);
|
||||
void SetCamera(int rotX, int rotY, int rotZ, int posX, int posY, int posZ, int cameraAp);
|
||||
void TransformSceneXYZ(int x, int y, int z, int& xOut, int& yOut);
|
||||
void Load(const Common::String& modelName, const Common::String& textureName);
|
||||
|
||||
void SetPalette(SCNHANDLE hPalette);
|
||||
|
||||
void SetSequence(uint animId, uint delay);
|
||||
Common::Rect Draw(int direction, int x, int y, int z, int tDelta);
|
||||
|
||||
private:
|
||||
const Math::Matrix4& MatrixCurrent() const;
|
||||
|
||||
void MatrixReset();
|
||||
|
||||
void MatrixPop();
|
||||
void MatrixPush();
|
||||
void MatrixTranslate(float x, float y, float z);
|
||||
void MatrixScale(float x, float y, float z);
|
||||
void MatrixRotateX(float angle);
|
||||
void MatrixRotateY(float angle);
|
||||
void MatrixRotateZ(float angle);
|
||||
|
||||
void SetViewport(int ap);
|
||||
|
||||
// Loading of the model
|
||||
void LoadH(const Common::String& modelName);
|
||||
void LoadGBL(const Common::String& modelName);
|
||||
void LoadRBH(const Common::String& modelName, Hunks& hunks);
|
||||
void LoadVMC(const Common::String& textureName);
|
||||
|
||||
void UpdateTextures();
|
||||
|
||||
Meshes LoadMeshes(const Hunks &hunks, uint hunk, uint offset, int frame);
|
||||
template<bool convert>
|
||||
AnimationData LoadAnimationData(const Hunks &hunks, uint hunk, uint offset);
|
||||
void InitModel(Model& model, MeshInfo& meshInfo, Common::Array<AnimationInfo>& animInfo, uint flags);
|
||||
|
||||
// Processing of the model
|
||||
void RunRenderProgram(Model &model, bool preprocess);
|
||||
|
||||
void FindSimilarVertices(Mesh& mesh, Vectors& vertices, Common::Array<uint16>& sameVertices) const;
|
||||
void MergeVertices(Mesh& mesh, Common::Array<uint16>& sameVertices);
|
||||
|
||||
void TransformMesh(Mesh& mesh, Vectors& vertices);
|
||||
void CalculateNormals(Mesh& mesh, Vectors& vertices, Vectors &normals);
|
||||
|
||||
// Rendering
|
||||
void RenderModel(Model& model);
|
||||
void RenderMesh(Mesh& mesh, Vectors& vertices, Vectors &normals);
|
||||
void RenderMeshPartColor(MeshPart& part, Vectors& vertices, Vectors &normals);
|
||||
void RenderMeshPartTexture(MeshPart& part, Vectors& vertices, Vectors &normals);
|
||||
|
||||
// Animation
|
||||
bool SetStartFrame(Model &model, const AnimationInfo &anim, int frame);
|
||||
bool SetEndFrame(Model &model, const AnimationInfo &anim, int frame);
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_SPRITER_H
|
||||
67
engines/tinsel/noir/sysreel.cpp
Normal file
67
engines/tinsel/noir/sysreel.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tinsel/noir/sysreel.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/pid.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/**
|
||||
* Returns the handle to the sysreel at the given index.
|
||||
*
|
||||
* @param index reel to get the handle to
|
||||
*/
|
||||
SCNHANDLE SystemReel::get(SysReel index) {
|
||||
assert((int)index >= 0 && (int)index < MAX_SYSREELS);
|
||||
|
||||
return _reels[(int)index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a reel at an index and if the index is a cursor
|
||||
*
|
||||
* @param index where to store the reel
|
||||
* @param reel handle to the reel
|
||||
*/
|
||||
void SystemReel::set(int32 index, SCNHANDLE reel) {
|
||||
assert(index >= 0 && index < MAX_SYSREELS);
|
||||
|
||||
if (index == (int)SysReel::LOADSCREEN) {
|
||||
if (CoroScheduler.getCurrentPID() != PID_SCENE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_reels[index] = reel;
|
||||
|
||||
// Noir actually calls a function specifically for doing DwInitCursor on
|
||||
// system reel 11.
|
||||
if (index == (int)SysReel::CURSOR && reel != 0) {
|
||||
_vm->_cursor->DwInitCursor(reel);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
66
engines/tinsel/noir/sysreel.h
Normal file
66
engines/tinsel/noir/sysreel.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SYSREEL_H // prevent multiple includes
|
||||
#define TINSEL_SYSREEL_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
enum class SysReel {
|
||||
NOTEPAD_OPENING = 4,
|
||||
NOTEPAD_OPEN = 5,
|
||||
NOTEPAD_CLOSED = 6,
|
||||
NOTEPAD_FLIPDOWN = 7,
|
||||
NOTEPAD_FLIPUP = 8,
|
||||
SCRIBBLES = 9,
|
||||
CURSOR = 11,
|
||||
INVMAIN = 15,
|
||||
SLIDER = 16,
|
||||
CONVERSATION_FRAME = 19,
|
||||
OPTIONS_MENU = 21,
|
||||
LOADSAVE_MENU = 22,
|
||||
QUIT_MENU = 23,
|
||||
SUBTITLES_MENU = 24,
|
||||
SLIDER_BG = 25,
|
||||
SLIDER_HI = 26,
|
||||
LEFT = 29,
|
||||
RIGHT = 30,
|
||||
LOADSCREEN = 31
|
||||
};
|
||||
|
||||
class SystemReel {
|
||||
public:
|
||||
SystemReel() = default;
|
||||
|
||||
SCNHANDLE get(SysReel index);
|
||||
void set(int32 index, SCNHANDLE reel);
|
||||
|
||||
private:
|
||||
const static int32 MAX_SYSREELS = 0x28;
|
||||
|
||||
SCNHANDLE _reels[MAX_SYSREELS];
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
557
engines/tinsel/object.cpp
Normal file
557
engines/tinsel/object.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains the Object Manager code.
|
||||
*/
|
||||
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/background.h"
|
||||
#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"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#define OID_EFFECTS 0x2000 // generic special effects object id
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
// list of all objects
|
||||
static OBJECT *objectList = nullptr;
|
||||
|
||||
// pointer to free object list
|
||||
static OBJECT *pFreeObjects = nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
// diagnostic object counters
|
||||
static int numObj = 0;
|
||||
static int maxObj = 0;
|
||||
#endif
|
||||
|
||||
void FreeObjectList() {
|
||||
free(objectList);
|
||||
objectList= nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills all objects and places them on the free list.
|
||||
*/
|
||||
|
||||
void KillAllObjects() {
|
||||
int i;
|
||||
|
||||
#ifdef DEBUG
|
||||
// clear number of objects in use
|
||||
numObj = 0;
|
||||
#endif
|
||||
|
||||
if (objectList == NULL) {
|
||||
// first time - allocate memory for object list
|
||||
objectList = (OBJECT *)calloc(NUM_OBJECTS, sizeof(OBJECT));
|
||||
|
||||
// make sure memory allocated
|
||||
if (objectList == NULL) {
|
||||
error("Cannot allocate memory for object data");
|
||||
}
|
||||
}
|
||||
|
||||
// place first object on free list
|
||||
pFreeObjects = objectList;
|
||||
|
||||
// link all other objects after first
|
||||
for (i = 1; i < NUM_OBJECTS; i++) {
|
||||
objectList[i - 1].pNext = objectList + i;
|
||||
}
|
||||
|
||||
// null the last object
|
||||
objectList[NUM_OBJECTS - 1].pNext= nullptr;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Shows the maximum number of objects used at once.
|
||||
*/
|
||||
|
||||
void ObjectStats() {
|
||||
debug("%i objects of %i used", maxObj, NUM_OBJECTS);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allocate a object from the free list.
|
||||
*/
|
||||
OBJECT *AllocObject() {
|
||||
OBJECT *pObj = pFreeObjects; // get a free object
|
||||
|
||||
// check for no free objects
|
||||
assert(pObj != NULL);
|
||||
|
||||
// a free object exists
|
||||
|
||||
// get link to next free object
|
||||
pFreeObjects = pObj->pNext;
|
||||
|
||||
// clear out object
|
||||
pObj->reset();
|
||||
|
||||
// set default drawing mode and set changed bit
|
||||
pObj->flags = DMA_WNZ | DMA_CHANGED;
|
||||
|
||||
#ifdef DEBUG
|
||||
// one more object in use
|
||||
if (++numObj > maxObj)
|
||||
maxObj = numObj;
|
||||
#endif
|
||||
|
||||
// return new object
|
||||
return pObj;
|
||||
}
|
||||
|
||||
bool isValidObject(OBJECT *obj) {
|
||||
return (obj >= objectList && obj <= objectList + NUM_OBJECTS - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy one object to another.
|
||||
* @param pDest Destination object
|
||||
* @param pSrc Source object
|
||||
*/
|
||||
void CopyObject(OBJECT *pDest, OBJECT *pSrc) {
|
||||
// save previous dimensions etc.
|
||||
Common::Rect rcSave = pDest->rcPrev;
|
||||
|
||||
// make a copy
|
||||
memcpy(pDest, pSrc, sizeof(OBJECT));
|
||||
|
||||
// restore previous dimensions etc.
|
||||
pDest->rcPrev = rcSave;
|
||||
|
||||
// set changed flag in destination
|
||||
pDest->flags |= DMA_CHANGED;
|
||||
|
||||
// null the links
|
||||
pDest->pNext = pDest->pSlave= nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an object onto the specified object list. The object
|
||||
* lists are sorted in Z Y order.
|
||||
* @param pObjList List to insert object onto
|
||||
* @param pInsObj Object to insert
|
||||
*/
|
||||
|
||||
void InsertObject(OBJECT **pObjList, OBJECT *pInsObj) {
|
||||
OBJECT **pAnchor, *pObj; // object list traversal pointers
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pInsObj));
|
||||
|
||||
for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
|
||||
// check Z order
|
||||
if (pInsObj->zPos < pObj->zPos) {
|
||||
// object Z is lower than list Z - insert here
|
||||
break;
|
||||
} else if (pInsObj->zPos == pObj->zPos) {
|
||||
// Z values are the same - sort on Y
|
||||
if (fracToDouble(pInsObj->yPos) <= fracToDouble(pObj->yPos)) {
|
||||
// object Y is lower than or same as list Y - insert here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert obj between pAnchor and pObj
|
||||
pInsObj->pNext = pObj;
|
||||
*pAnchor = pInsObj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes an object from the specified object list and places it
|
||||
* on the free list.
|
||||
* @param pObjList List to delete object from
|
||||
* @param pDelObj Object to delete
|
||||
*/
|
||||
void DelObject(OBJECT **pObjList, OBJECT *pDelObj) {
|
||||
OBJECT **pAnchor, *pObj; // object list traversal pointers
|
||||
const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
// validate object pointer
|
||||
assert(isValidObject(pDelObj));
|
||||
|
||||
#ifdef DEBUG
|
||||
// one less object in use
|
||||
--numObj;
|
||||
assert(numObj >= 0);
|
||||
#endif
|
||||
|
||||
for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
|
||||
if (pObj == pDelObj) {
|
||||
// found object to delete
|
||||
|
||||
if (IntersectRectangle(pDelObj->rcPrev, pDelObj->rcPrev, rcScreen)) {
|
||||
// allocate a clipping rect for objects previous pos
|
||||
AddClipRect(pDelObj->rcPrev);
|
||||
}
|
||||
|
||||
// make PREV next = OBJ next - removes OBJ from list
|
||||
*pAnchor = pObj->pNext;
|
||||
|
||||
// place free list in OBJ next
|
||||
pObj->pNext = pFreeObjects;
|
||||
|
||||
// add OBJ to top of free list
|
||||
pFreeObjects = pObj;
|
||||
|
||||
// delete objects palette
|
||||
if (pObj->pPal)
|
||||
FreePalette(pObj->pPal);
|
||||
|
||||
// quit
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we get to here - object has not been found on the list
|
||||
// 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)!'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the specified object list in Z Y order.
|
||||
* @param pObjList List to sort
|
||||
*/
|
||||
void SortObjectList(OBJECT **pObjList) {
|
||||
OBJECT *pPrev, *pObj; // object list traversal pointers
|
||||
OBJECT head; // temporary head of list - because pObjList is not usually a OBJECT
|
||||
|
||||
// put at head of list
|
||||
head.pNext = *pObjList;
|
||||
|
||||
// set head of list dummy OBJ Z Y values to lowest possible
|
||||
head.yPos = intToFrac(MIN_INT16);
|
||||
head.zPos = MIN_INT;
|
||||
|
||||
for (pPrev = &head, pObj = head.pNext; pObj != NULL; pPrev = pObj, pObj = pObj->pNext) {
|
||||
// check Z order
|
||||
if (pObj->zPos < pPrev->zPos) {
|
||||
// object Z is lower than previous Z
|
||||
|
||||
// remove object from list
|
||||
pPrev->pNext = pObj->pNext;
|
||||
|
||||
// re-insert object on list
|
||||
InsertObject(pObjList, pObj);
|
||||
|
||||
// back to beginning of list
|
||||
pPrev = &head;
|
||||
pObj = head.pNext;
|
||||
} else if (pObj->zPos == pPrev->zPos) {
|
||||
// Z values are the same - sort on Y
|
||||
if (fracToDouble(pObj->yPos) < fracToDouble(pPrev->yPos)) {
|
||||
// object Y is lower than previous Y
|
||||
|
||||
// remove object from list
|
||||
pPrev->pNext = pObj->pNext;
|
||||
|
||||
// re-insert object on list
|
||||
InsertObject(pObjList, pObj);
|
||||
|
||||
// back to beginning of list
|
||||
pPrev = &head;
|
||||
pObj = head.pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the animation offsets of a image, dependent on the
|
||||
* images orientation flags.
|
||||
* @param hImg Iimage to get animation offset of
|
||||
* @param flags Images current flags
|
||||
* @param pAniX Gets set to new X animation offset
|
||||
* @param pAniY Gets set to new Y animation offset
|
||||
*/
|
||||
void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) {
|
||||
if (hImg) {
|
||||
const IMAGE *pImg = _vm->_handle->GetImage(hImg);
|
||||
|
||||
// set ani X
|
||||
*pAniX = (int16) pImg->anioffX;
|
||||
|
||||
// set ani Y
|
||||
*pAniY = (int16) pImg->anioffY;
|
||||
|
||||
if (flags & DMA_FLIPH) {
|
||||
// we are flipped horizontally
|
||||
|
||||
// set ani X = -ani X + width - 1
|
||||
*pAniX = -*pAniX + pImg->imgWidth - 1;
|
||||
}
|
||||
|
||||
if (flags & DMA_FLIPV) {
|
||||
// we are flipped vertically
|
||||
|
||||
// set ani Y = -ani Y + height - 1
|
||||
*pAniY = -*pAniY + (pImg->imgHeight & ~C16_FLAG_MASK) - 1;
|
||||
}
|
||||
|
||||
delete pImg;
|
||||
} else
|
||||
// null image
|
||||
*pAniX = *pAniY = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the x,y position of an objects animation point.
|
||||
* @param pObj Pointer to object
|
||||
* @param pPosX Gets set to objects X animation position
|
||||
* @param pPosY Gets set to objects Y animation position
|
||||
*/
|
||||
void GetAniPosition(OBJECT *pObj, int *pPosX, int *pPosY) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pObj));
|
||||
|
||||
// get the animation offset of the object
|
||||
GetAniOffset(pObj->hImg, pObj->flags, pPosX, pPosY);
|
||||
|
||||
// from animation offset and objects position - determine objects animation point
|
||||
*pPosX += fracToInt(pObj->xPos);
|
||||
*pPosY += fracToInt(pObj->yPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a object using a OBJ_INIT structure to supply parameters.
|
||||
* @param pInitTbl Pointer to object initialisation table
|
||||
*/
|
||||
OBJECT *InitObject(const OBJ_INIT *pInitTbl) {
|
||||
// allocate a new object
|
||||
OBJECT *pObj = AllocObject();
|
||||
|
||||
// make sure object created
|
||||
assert(pObj != NULL);
|
||||
|
||||
// set objects shape
|
||||
pObj->hImg = pInitTbl->hObjImg;
|
||||
|
||||
// set objects ID
|
||||
pObj->oid = pInitTbl->objID;
|
||||
|
||||
// set objects flags
|
||||
pObj->flags = DMA_CHANGED | pInitTbl->objFlags;
|
||||
|
||||
// set objects Z position
|
||||
pObj->zPos = pInitTbl->objZ;
|
||||
|
||||
// get pointer to image
|
||||
if (pInitTbl->hObjImg) {
|
||||
int aniX, aniY; // objects animation offsets
|
||||
PALQ *pPalQ= nullptr; // palette queue pointer
|
||||
const IMAGE *pImg = _vm->_handle->GetImage(pInitTbl->hObjImg); // handle to image
|
||||
|
||||
if (TinselVersion != 3) {
|
||||
if (pImg->hImgPal) {
|
||||
// allocate a palette for this object
|
||||
pPalQ = AllocPalette(pImg->hImgPal);
|
||||
|
||||
// make sure palette allocated
|
||||
assert(pPalQ != NULL);
|
||||
}
|
||||
|
||||
// assign palette to object
|
||||
pObj->pPal = pPalQ;
|
||||
} else {
|
||||
if ((pImg->colorFlags & 0x0C) == 0) { // bits 0b1100 are used to select blending mode
|
||||
pObj->flags = pObj->flags & ~DMA_GHOST;
|
||||
} else {
|
||||
assert((pObj->flags & DMA_WNZ) != 0);
|
||||
pObj->flags |= DMA_GHOST;
|
||||
}
|
||||
pObj->isRLE = pImg->isRLE;
|
||||
pObj->colorFlags = pImg->colorFlags;
|
||||
}
|
||||
|
||||
// set objects size
|
||||
pObj->width = pImg->imgWidth;
|
||||
pObj->height = pImg->imgHeight & ~C16_FLAG_MASK;
|
||||
pObj->flags &= ~C16_FLAG_MASK;
|
||||
pObj->flags |= pImg->imgHeight & C16_FLAG_MASK;
|
||||
|
||||
// set objects bitmap definition
|
||||
pObj->hBits = pImg->hImgBits;
|
||||
|
||||
delete pImg;
|
||||
|
||||
// get animation offset of object
|
||||
GetAniOffset(pObj->hImg, pInitTbl->objFlags, &aniX, &aniY);
|
||||
|
||||
// set objects X position - subtract ani offset
|
||||
pObj->xPos = intToFrac(pInitTbl->objX - aniX);
|
||||
|
||||
// set objects Y position - subtract ani offset
|
||||
pObj->yPos = intToFrac(pInitTbl->objY - aniY);
|
||||
} else { // no image handle - null image
|
||||
|
||||
// set objects X position
|
||||
pObj->xPos = intToFrac(pInitTbl->objX);
|
||||
|
||||
// set objects Y position
|
||||
pObj->yPos = intToFrac(pInitTbl->objY);
|
||||
}
|
||||
|
||||
// return new object
|
||||
return pObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a object a new image and new orientation flags.
|
||||
* @param pAniObj Object to be updated
|
||||
* @param newflags Objects new flags
|
||||
* @param hNewImg Objects new image
|
||||
*/
|
||||
void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) {
|
||||
// validate object pointer
|
||||
assert(isValidObject(pAniObj));
|
||||
|
||||
if (pAniObj->hImg != hNewImg
|
||||
|| (pAniObj->flags & DMA_HARDFLAGS) != (newflags & DMA_HARDFLAGS)) {
|
||||
// something has changed
|
||||
|
||||
int oldAniX, oldAniY; // objects old animation offsets
|
||||
int newAniX, newAniY; // objects new animation offsets
|
||||
|
||||
// get objects old animation offsets
|
||||
GetAniOffset(pAniObj->hImg, pAniObj->flags, &oldAniX, &oldAniY);
|
||||
|
||||
// get objects new animation offsets
|
||||
GetAniOffset(hNewImg, newflags, &newAniX, &newAniY);
|
||||
|
||||
if (hNewImg) {
|
||||
// get pointer to image
|
||||
const IMAGE *pNewImg = _vm->_handle->GetImage(hNewImg);
|
||||
|
||||
// setup new shape
|
||||
pAniObj->width = pNewImg->imgWidth;
|
||||
pAniObj->height = pNewImg->imgHeight & ~C16_FLAG_MASK;
|
||||
newflags &= ~C16_FLAG_MASK;
|
||||
newflags |= pNewImg->imgHeight & C16_FLAG_MASK;
|
||||
|
||||
// set objects bitmap definition
|
||||
pAniObj->hBits = pNewImg->hImgBits;
|
||||
|
||||
delete pNewImg;
|
||||
} else { // null image
|
||||
pAniObj->width = 0;
|
||||
pAniObj->height = 0;
|
||||
pAniObj->hBits = 0;
|
||||
}
|
||||
|
||||
// set objects flags and signal a change
|
||||
pAniObj->flags = newflags | DMA_CHANGED;
|
||||
|
||||
// set objects image
|
||||
pAniObj->hImg = hNewImg;
|
||||
|
||||
// adjust objects position - subtract new from old for difference
|
||||
pAniObj->xPos += intToFrac(oldAniX - newAniX);
|
||||
pAniObj->yPos += intToFrac(oldAniY - newAniY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give an object a new image.
|
||||
* @param pAniObj Object to animate
|
||||
* @param hNewImg Objects new image
|
||||
*/
|
||||
void AnimateObject(OBJECT *pAniObj, SCNHANDLE hNewImg) {
|
||||
// dont change the objects flags
|
||||
AnimateObjectFlags(pAniObj, pAniObj->flags, hNewImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rectangle object of the given dimensions and returns
|
||||
* a pointer to the object.
|
||||
* @param hPal Palette for the rectangle object
|
||||
* @param color Which color offset from the above palette
|
||||
* @param width Width of rectangle
|
||||
* @param height Height of rectangle
|
||||
*/
|
||||
OBJECT *RectangleObject(SCNHANDLE hPal, int color, int width, int height) {
|
||||
// template for initializing the rectangle object
|
||||
static const OBJ_INIT rectObj = {0, DMA_CONST, OID_EFFECTS, 0, 0, 0};
|
||||
PALQ *pPalQ; // palette queue pointer
|
||||
|
||||
// allocate and init a new object
|
||||
OBJECT *pRect = InitObject(&rectObj);
|
||||
|
||||
// allocate a palette for this object
|
||||
pPalQ = AllocPalette(hPal);
|
||||
|
||||
// make sure palette allocated
|
||||
assert(pPalQ != NULL);
|
||||
|
||||
// assign palette to object
|
||||
pRect->pPal = pPalQ;
|
||||
|
||||
// set color in the palette
|
||||
pRect->constant = color;
|
||||
|
||||
// set rectangle width
|
||||
pRect->width = width;
|
||||
|
||||
// set rectangle height
|
||||
pRect->height = height;
|
||||
|
||||
// return pointer to rectangle object
|
||||
return pRect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translucent rectangle object of the given dimensions
|
||||
* and returns a pointer to the object.
|
||||
* @param width Width of rectangle
|
||||
* @param height Height of rectangle
|
||||
*/
|
||||
OBJECT *TranslucentObject(int width, int height) {
|
||||
// template for initializing the rectangle object
|
||||
static const OBJ_INIT rectObj = {0, DMA_TRANS, OID_EFFECTS, 0, 0, 0};
|
||||
|
||||
// allocate and init a new object
|
||||
OBJECT *pRect = InitObject(&rectObj);
|
||||
|
||||
// set rectangle width
|
||||
pRect->width = width;
|
||||
|
||||
// set rectangle height
|
||||
pRect->height = height;
|
||||
|
||||
// return pointer to rectangle object
|
||||
return pRect;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
207
engines/tinsel/object.h
Normal file
207
engines/tinsel/object.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Object Manager data structures
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_OBJECT_H // prevent multiple includes
|
||||
#define TINSEL_OBJECT_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "common/frac.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct PALQ;
|
||||
|
||||
enum {
|
||||
/** the maximum number of objects */
|
||||
NUM_OBJECTS = 512,
|
||||
|
||||
// object flags
|
||||
DMA_WNZ = 0x0001, ///< write non-zero data
|
||||
DMA_CNZ = 0x0002, ///< TinselV1 write constant on non-zero data
|
||||
DMA_RLWA = 0x0002, ///< TenselV2+ run-length write all
|
||||
DMA_CONST = 0x0004, ///< write constant on both zero & non-zero data
|
||||
DMA_WA = 0x0008, ///< write all data
|
||||
DMA_FLIPH = 0x0010, ///< flip object horizontally
|
||||
DMA_FLIPV = 0x0020, ///< flip object vertically
|
||||
DMA_CLIP = 0x0040, ///< clip object
|
||||
DMA_TRANS = 0x0084, ///< translucent rectangle object
|
||||
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,
|
||||
DMA_3D = 0x0400, ///< 3D objects for TinselV3
|
||||
|
||||
/** 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 */
|
||||
struct IMAGE {
|
||||
short imgWidth; ///< image width
|
||||
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 (Tinsel V1/V2)
|
||||
short isRLE; ///< if image is using run-length encoding (Tinsel V3)
|
||||
short colorFlags; ///< type of blending (Tinsel V3)
|
||||
};
|
||||
|
||||
/** a multi-object animation frame is a list of multi-image handles */
|
||||
typedef uint32 FRAME;
|
||||
|
||||
// object structure
|
||||
struct OBJECT {
|
||||
OBJECT *pNext; ///< pointer to next object in list
|
||||
OBJECT *pSlave; ///< pointer to slave object (multi-part objects)
|
||||
// char *pOnDispList; ///< pointer to display list byte for background objects
|
||||
// frac_t xVel; ///< x velocity of object
|
||||
// frac_t yVel; ///< y velocity of object
|
||||
frac_t xPos; ///< x position of object
|
||||
frac_t yPos; ///< y position of object
|
||||
int zPos; ///< z position of object
|
||||
Common::Rect rcPrev; ///< previous screen coordinates of object bounding rectangle
|
||||
int flags; ///< object flags - see above for list
|
||||
PALQ *pPal; ///< objects palette Q position
|
||||
short isRLE; ///< TinselVersion == 3, if image is using run-length encoding
|
||||
short colorFlags; /// TinselV3, type of color blending
|
||||
int constant; ///< which color in palette for monochrome objects
|
||||
int width; ///< width of object
|
||||
int height; ///< height of object
|
||||
SCNHANDLE hBits; ///< image bitmap handle
|
||||
SCNHANDLE hImg; ///< handle to object image definition
|
||||
SCNHANDLE hShape; ///< objects current animation frame
|
||||
SCNHANDLE hMirror; ///< objects previous animation frame
|
||||
int oid; ///< object identifier
|
||||
|
||||
void reset() {
|
||||
pNext = nullptr;
|
||||
pSlave = nullptr;
|
||||
//pOnDispList = nullptr;
|
||||
//xVel = 0;
|
||||
//yVel = 0;
|
||||
xPos = 0;
|
||||
yPos = 0;
|
||||
zPos = 0;
|
||||
rcPrev.top = 0;
|
||||
rcPrev.left = 0;
|
||||
rcPrev.bottom = 0;
|
||||
rcPrev.right = 0;
|
||||
flags = 0;
|
||||
pPal = nullptr;
|
||||
constant = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
hBits = 0;
|
||||
hImg = 0;
|
||||
hShape = 0;
|
||||
hMirror = 0;
|
||||
oid = 0;
|
||||
}
|
||||
|
||||
OBJECT() { reset(); }
|
||||
};
|
||||
|
||||
// object initialisation structure
|
||||
struct OBJ_INIT {
|
||||
SCNHANDLE hObjImg; // objects shape - handle to IMAGE structure
|
||||
int32 objFlags; // objects flags
|
||||
int32 objID; // objects id
|
||||
int32 objX; // objects initial x position
|
||||
int32 objY; // objects initial y position
|
||||
int32 objZ; // objects initial z position
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Object Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
void KillAllObjects(); // kill all objects and place them on free list
|
||||
|
||||
void FreeObjectList(); // free the object list
|
||||
|
||||
#ifdef DEBUG
|
||||
void ObjectStats(); // Shows the maximum number of objects used at once
|
||||
#endif
|
||||
|
||||
OBJECT *AllocObject(); // allocate a object from the free list
|
||||
|
||||
void FreeObject( // place a object back on the free list
|
||||
OBJECT *pFreeObj); // object to free
|
||||
|
||||
bool isValidObject(OBJECT *obj);
|
||||
|
||||
void CopyObject( // copy one object to another
|
||||
OBJECT *pDest, // destination object
|
||||
OBJECT *pSrc); // source object
|
||||
|
||||
void InsertObject( // insert a object onto a sorted object list
|
||||
OBJECT **pObjList, // list to insert object onto
|
||||
OBJECT *pInsObj); // object to insert
|
||||
|
||||
void DelObject( // delete a object from a object list and add to free list
|
||||
OBJECT **pObjList, // list to delete object from
|
||||
OBJECT *pDelObj); // object to delete
|
||||
|
||||
void SortObjectList( // re-sort an object list
|
||||
OBJECT **pObjList); // list to sort
|
||||
|
||||
void GetAniOffset( // returns the anim offsets of a image, takes into account orientation
|
||||
SCNHANDLE hImg, // image to get animation offset of
|
||||
int flags, // images current flags
|
||||
int *pAniX, // gets set to new X animation offset
|
||||
int *pAniY); // gets set to new Y animation offset
|
||||
|
||||
void GetAniPosition( // Returns a objects x,y animation point
|
||||
OBJECT *pObj, // pointer to object
|
||||
int *pPosX, // gets set to objects X animation position
|
||||
int *pPosY); // gets set to objects Y animation position
|
||||
|
||||
OBJECT *InitObject( // Init a object using a OBJ_INIT struct
|
||||
const OBJ_INIT *pInitTbl); // pointer to object initialisation table
|
||||
|
||||
void AnimateObjectFlags( // Give a object a new image and new orientation flags
|
||||
OBJECT *pAniObj, // object to be updated
|
||||
int newflags, // objects new flags
|
||||
SCNHANDLE hNewImg); // objects new image
|
||||
|
||||
void AnimateObject( // give a object a new image
|
||||
OBJECT *pAniObj, // object to animate
|
||||
SCNHANDLE hNewImg); // objects new image
|
||||
|
||||
void HideObject( // Hides a object by giving it a "NullImage" image pointer
|
||||
OBJECT *pObj); // object to be hidden
|
||||
|
||||
OBJECT *RectangleObject( // create a rectangle object of the given dimensions
|
||||
SCNHANDLE hPal, // palette for the rectangle object
|
||||
int color, // which color offset from the above palette
|
||||
int width, // width of rectangle
|
||||
int height); // height of rectangle
|
||||
|
||||
OBJECT *TranslucentObject( // create a translucent rectangle object of the given dimensions
|
||||
int width, // width of rectangle
|
||||
int height); // height of rectangle
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_OBJECT_H
|
||||
685
engines/tinsel/palette.cpp
Normal file
685
engines/tinsel/palette.cpp
Normal file
@@ -0,0 +1,685 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Palette Allocator for IBM PC.
|
||||
*/
|
||||
|
||||
#include "tinsel/dw.h" // TBLUE1 definition
|
||||
#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"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
/** video DAC transfer Q structure */
|
||||
struct VIDEO_DAC_Q {
|
||||
union {
|
||||
SCNHANDLE hRGBarray; ///< handle of palette or
|
||||
COLORREF *pRGBarray; ///< list of palette colors
|
||||
COLORREF singleRGB;
|
||||
} pal;
|
||||
bool bHandle; ///< when set - use handle of palette
|
||||
int destDACindex; ///< start index of palette in video DAC
|
||||
int numColors; ///< number of colors in "hRGBarray"
|
||||
};
|
||||
|
||||
|
||||
/** video DAC transfer Q length */
|
||||
#define VDACQLENGTH (NUM_PALETTES + 2)
|
||||
|
||||
/** color index of the 4 colors used for the translucent palette */
|
||||
#define COL_HILIGHT TBLUE1
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
/** palette allocator data */
|
||||
static PALQ g_palAllocData[NUM_PALETTES];
|
||||
|
||||
/** video DAC transfer Q */
|
||||
static VIDEO_DAC_Q g_vidDACdata[VDACQLENGTH];
|
||||
|
||||
/** video DAC transfer Q head pointer */
|
||||
static VIDEO_DAC_Q *g_pDAChead;
|
||||
|
||||
/** the translucent palette lookup table */
|
||||
uint8 g_transPalette[MAX_COLORS]; // used in graphics.cpp
|
||||
|
||||
static int g_translucentIndex = 228;
|
||||
static int g_talkIndex = 233;
|
||||
|
||||
static COLORREF g_talkColRef = 0;
|
||||
static COLORREF g_tagColRef = 0;
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
// diagnostic palette counters
|
||||
static int numPals = 0;
|
||||
static int maxPals = 0;
|
||||
static int maxDACQ = 0;
|
||||
#endif
|
||||
|
||||
void ResetVarsPalette() {
|
||||
memset(g_palAllocData, 0, sizeof(g_palAllocData));
|
||||
|
||||
g_pDAChead = g_vidDACdata;
|
||||
|
||||
memset(g_transPalette, 0, sizeof(g_transPalette));
|
||||
|
||||
g_translucentIndex = 228;
|
||||
g_talkIndex = 233;
|
||||
|
||||
g_talkColRef = 0;
|
||||
g_tagColRef = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map PSX palettes to original palette from resource file
|
||||
*/
|
||||
void psxPaletteMapper(PALQ *originalPal, uint8 *psxClut, byte *mapperTable) {
|
||||
PALETTE *pal = _vm->_handle->GetPalette(originalPal->hPal);
|
||||
bool colorFound = false;
|
||||
uint16 clutEntry = 0;
|
||||
|
||||
// Empty the table with color correspondences
|
||||
memset(mapperTable, 0, 16);
|
||||
|
||||
for (int j = 1; j < 16; j++) {
|
||||
clutEntry = READ_16(psxClut + (sizeof(uint16) * j));
|
||||
if (clutEntry) {
|
||||
if (clutEntry == 0x7EC0) { // This is an already known value, used by the in-game text
|
||||
mapperTable[j] = 232;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for correspondent color
|
||||
for (int32 i = 0; (i < pal->numColors) && !colorFound; i++) {
|
||||
// get R G B values in the same way as psx format converters
|
||||
uint16 psxEquivalent = TINSEL_PSX_RGB(
|
||||
pal->palette[i * 3] >> 3,
|
||||
pal->palette[i * 3 + 1] >> 3,
|
||||
pal->palette[i * 3 + 2] >> 3
|
||||
);
|
||||
|
||||
if (psxEquivalent == clutEntry) {
|
||||
mapperTable[j] = i + 1; // Add entry in the table for the found color
|
||||
colorFound = true;
|
||||
}
|
||||
}
|
||||
colorFound = false;
|
||||
} else { // The rest of the entries are zeroes
|
||||
delete pal;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
delete pal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer palettes in the palette Q to Video DAC.
|
||||
*/
|
||||
void PalettesToVideoDAC() {
|
||||
PALQ *pPalQ; // palette Q iterator
|
||||
VIDEO_DAC_Q *pDACtail = g_vidDACdata; // set tail pointer
|
||||
byte pal[768];
|
||||
|
||||
memset(pal, 0, sizeof(pal));
|
||||
|
||||
// while Q is not empty
|
||||
while (g_pDAChead != pDACtail) {
|
||||
#ifdef DEBUG
|
||||
// make sure palette does not overlap
|
||||
assert(pDACtail->destDACindex + pDACtail->numColors <= MAX_COLORS);
|
||||
#else
|
||||
// make sure palette does not overlap
|
||||
if (pDACtail->destDACindex + pDACtail->numColors > MAX_COLORS)
|
||||
pDACtail->numColors = MAX_COLORS - pDACtail->destDACindex;
|
||||
#endif
|
||||
|
||||
if (pDACtail->bHandle) {
|
||||
// we are using a palette handle
|
||||
PALETTE *newPal = _vm->_handle->GetPalette(pDACtail->pal.hRGBarray);
|
||||
memcpy(pal, newPal->palette, pDACtail->numColors * 3);
|
||||
delete newPal;
|
||||
} else if (pDACtail->numColors == 1) {
|
||||
// we are using a single color palette
|
||||
pal[0] = (byte)(pDACtail->pal.singleRGB & 0xFF);
|
||||
pal[1] = (byte)((pDACtail->pal.singleRGB >> 8) & 0xFF);
|
||||
pal[2] = (byte)((pDACtail->pal.singleRGB >> 16) & 0xFF);
|
||||
} else {
|
||||
// we are using a palette pointer
|
||||
for (int i = 0; i < pDACtail->numColors; ++i) {
|
||||
pal[i * 3 + 0] = (byte)(pDACtail->pal.pRGBarray[i] & 0xFF);
|
||||
pal[i * 3 + 1] = (byte)((pDACtail->pal.pRGBarray[i] >> 8) & 0xFF);
|
||||
pal[i * 3 + 2] = (byte)((pDACtail->pal.pRGBarray[i] >> 16) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap black/white colors in the Mac version.
|
||||
// We need to swap the current black/white values so that screen fade
|
||||
// in/out is done correctly.
|
||||
if (TinselV1Mac) {
|
||||
byte macWhite = pal[ 0 * 3 + 0];
|
||||
byte macBlack = pal[254 * 3 + 0];
|
||||
pal[254 * 3 + 0] = pal[254 * 3 + 1] = pal[254 * 3 + 2] = macWhite;
|
||||
pal[ 0 * 3 + 0] = pal[ 0 * 3 + 1] = pal[ 0 * 3 + 2] = macBlack;
|
||||
}
|
||||
|
||||
// update the system palette
|
||||
g_system->getPaletteManager()->setPalette(pal, pDACtail->destDACindex, pDACtail->numColors);
|
||||
|
||||
// update tail pointer
|
||||
pDACtail++;
|
||||
|
||||
}
|
||||
|
||||
// reset video DAC transfer Q head pointer
|
||||
g_pDAChead = g_vidDACdata;
|
||||
|
||||
// clear all palette moved bits
|
||||
for (pPalQ = g_palAllocData; pPalQ < g_palAllocData + NUM_PALETTES; pPalQ++)
|
||||
pPalQ->posInDAC &= ~PALETTE_MOVED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commpletely reset the palette allocator.
|
||||
*/
|
||||
void ResetPalAllocator() {
|
||||
#ifdef DEBUG
|
||||
// clear number of palettes in use
|
||||
numPals = 0;
|
||||
#endif
|
||||
|
||||
// wipe out the palette allocator data
|
||||
memset(g_palAllocData, 0, sizeof(g_palAllocData));
|
||||
|
||||
// reset video DAC transfer Q head pointer
|
||||
g_pDAChead = g_vidDACdata;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Shows the maximum number of palettes used at once.
|
||||
*/
|
||||
void PaletteStats() {
|
||||
debug("%i palettes of %i used", maxPals, NUM_PALETTES);
|
||||
debug("%i DAC queue entries of %i used", maxDACQ, VDACQLENGTH);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Places a palette in the video DAC queue.
|
||||
* @param posInDAC Position in video DAC
|
||||
* @param numColors Number of colors in palette
|
||||
* @param hPalette Handle to palette
|
||||
*/
|
||||
void UpdateDACqueueHandle(int posInDAC, int numColors, SCNHANDLE hPalette) {
|
||||
// check Q overflow
|
||||
assert(g_pDAChead < g_vidDACdata + VDACQLENGTH);
|
||||
|
||||
g_pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
|
||||
g_pDAChead->numColors = numColors; // set number of colors
|
||||
g_pDAChead->pal.hRGBarray = hPalette; // set handle of palette
|
||||
g_pDAChead->bHandle = true; // we are using a palette handle
|
||||
|
||||
// update head pointer
|
||||
++g_pDAChead;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ((g_pDAChead-g_vidDACdata) > maxDACQ)
|
||||
maxDACQ = g_pDAChead-g_vidDACdata;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a palette in the video DAC queue.
|
||||
* @param posInDAC Position in video DAC
|
||||
* @param numColors Number of colors in palette
|
||||
* @param pColors List of RGB triples
|
||||
*/
|
||||
void UpdateDACqueue(int posInDAC, int numColors, COLORREF *pColors) {
|
||||
// check Q overflow
|
||||
assert(g_pDAChead < g_vidDACdata + NUM_PALETTES);
|
||||
|
||||
g_pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
|
||||
g_pDAChead->numColors = numColors; // set number of colors
|
||||
if (numColors == 1)
|
||||
g_pDAChead->pal.singleRGB = *pColors; // set single color of which the "palette" consists
|
||||
else
|
||||
g_pDAChead->pal.pRGBarray = pColors; // set addr of palette
|
||||
g_pDAChead->bHandle = false; // we are not using a palette handle
|
||||
|
||||
// update head pointer
|
||||
++g_pDAChead;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ((g_pDAChead-g_vidDACdata) > maxDACQ)
|
||||
maxDACQ = g_pDAChead-g_vidDACdata;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Places a "palette" consisting of a single color in the video DAC queue.
|
||||
* @param posInDAC Position in video DAC
|
||||
* @param color Single RGB triple
|
||||
*/
|
||||
void UpdateDACqueue(int posInDAC, COLORREF color) {
|
||||
// check Q overflow
|
||||
assert(g_pDAChead < g_vidDACdata + NUM_PALETTES);
|
||||
|
||||
g_pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
|
||||
g_pDAChead->numColors = 1; // set number of colors
|
||||
g_pDAChead->pal.singleRGB = color; // set single color of which the "palette" consists
|
||||
g_pDAChead->bHandle = false; // we are not using a palette handle
|
||||
|
||||
// update head pointer
|
||||
++g_pDAChead;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ((g_pDAChead-g_vidDACdata) > maxDACQ)
|
||||
maxDACQ = g_pDAChead-g_vidDACdata;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a palette.
|
||||
* @param hNewPal Palette to allocate
|
||||
*/
|
||||
PALQ *AllocPalette(SCNHANDLE hNewPal) {
|
||||
PALQ *pPrev, *p; // walks palAllocData
|
||||
int iDAC; // color index in video DAC
|
||||
PALQ *pNxtPal; // next PALQ struct in palette allocator
|
||||
|
||||
// search all structs in palette allocator - see if palette already allocated
|
||||
for (p = g_palAllocData; p < g_palAllocData + NUM_PALETTES; p++) {
|
||||
if (p->hPal == hNewPal) {
|
||||
// found the desired palette in palette allocator
|
||||
p->objCount++; // update number of objects using palette
|
||||
return p; // return palette queue position
|
||||
}
|
||||
}
|
||||
|
||||
PALETTE *pal = _vm->_handle->GetPalette(hNewPal);
|
||||
|
||||
// search all structs in palette allocator - find a free slot
|
||||
iDAC = FGND_DAC_INDEX; // init DAC index to first available foreground color
|
||||
|
||||
for (p = g_palAllocData; p < g_palAllocData + NUM_PALETTES; p++) {
|
||||
if (p->hPal == 0) {
|
||||
// found a free slot in palette allocator
|
||||
p->objCount = 1; // init number of objects using palette
|
||||
p->posInDAC = iDAC; // set palettes start pos in video DAC
|
||||
p->hPal = hNewPal; // set hardware palette data
|
||||
p->numColors = pal->numColors; // set number of colors in palette
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
// Copy all the colors
|
||||
memcpy(p->palRGB, pal->palRGB, p->numColors * sizeof(COLORREF));
|
||||
|
||||
#ifdef DEBUG
|
||||
// one more palette in use
|
||||
if (++numPals > maxPals)
|
||||
maxPals = numPals;
|
||||
#endif
|
||||
|
||||
// Q the change to the video DAC
|
||||
if (TinselVersion >= 2)
|
||||
UpdateDACqueue(p->posInDAC, p->numColors, p->palRGB);
|
||||
else
|
||||
UpdateDACqueueHandle(p->posInDAC, p->numColors, p->hPal);
|
||||
|
||||
// move all palettes after this one down (if necessary)
|
||||
for (pPrev = p, pNxtPal = pPrev + 1; pNxtPal < g_palAllocData + NUM_PALETTES; pNxtPal++) {
|
||||
if (pNxtPal->hPal != 0) {
|
||||
// palette slot is in use
|
||||
if (pNxtPal->posInDAC >= pPrev->posInDAC + pPrev->numColors)
|
||||
// no need to move palettes down
|
||||
break;
|
||||
|
||||
// move palette down - indicate change
|
||||
pNxtPal->posInDAC = (pPrev->posInDAC + pPrev->numColors) | PALETTE_MOVED;
|
||||
|
||||
// Q the palette change in position to the video DAC
|
||||
if (TinselVersion <= 1)
|
||||
UpdateDACqueueHandle(pNxtPal->posInDAC, pNxtPal->numColors, pNxtPal->hPal);
|
||||
else if (!pNxtPal->bFading)
|
||||
UpdateDACqueue(pNxtPal->posInDAC, pNxtPal->numColors, pNxtPal->palRGB);
|
||||
|
||||
// update previous palette to current palette
|
||||
pPrev = pNxtPal;
|
||||
}
|
||||
}
|
||||
|
||||
delete pal;
|
||||
|
||||
// return palette pointer
|
||||
return p;
|
||||
}
|
||||
|
||||
// set new DAC index
|
||||
iDAC = p->posInDAC + p->numColors;
|
||||
}
|
||||
|
||||
// no free palettes
|
||||
error("AllocPalette(): formally 'assert(0)!'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a palette allocated with "AllocPalette".
|
||||
* @param pFreePal Palette queue entry to free
|
||||
*/
|
||||
void FreePalette(PALQ *pFreePal) {
|
||||
// validate palette Q pointer
|
||||
assert(pFreePal >= g_palAllocData && pFreePal <= g_palAllocData + NUM_PALETTES - 1);
|
||||
|
||||
// reduce the palettes object reference count
|
||||
pFreePal->objCount--;
|
||||
|
||||
// make sure palette has not been deallocated too many times
|
||||
assert(pFreePal->objCount >= 0);
|
||||
|
||||
if (pFreePal->objCount == 0) {
|
||||
pFreePal->hPal = 0; // palette is no longer in use
|
||||
|
||||
#ifdef DEBUG
|
||||
// one less palette in use
|
||||
--numPals;
|
||||
assert(numPals >= 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the specified palette.
|
||||
* @param hSrchPal Hardware palette to search for
|
||||
*/
|
||||
PALQ *FindPalette(SCNHANDLE hSrchPal) {
|
||||
PALQ *pPal; // palette allocator iterator
|
||||
|
||||
// search all structs in palette allocator
|
||||
for (pPal = g_palAllocData; pPal < g_palAllocData + NUM_PALETTES; pPal++) {
|
||||
if (pPal->hPal == hSrchPal)
|
||||
// found palette in palette allocator
|
||||
return pPal;
|
||||
}
|
||||
|
||||
// palette not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the palettes at the specified palette queue position.
|
||||
* @param pPalQ Palette queue position
|
||||
* @param hNewPal New palette
|
||||
*/
|
||||
void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) {
|
||||
PALETTE *pal = _vm->_handle->GetPalette(hNewPal);
|
||||
|
||||
// validate palette Q pointer
|
||||
assert(pPalQ >= g_palAllocData && pPalQ <= g_palAllocData + NUM_PALETTES - 1);
|
||||
|
||||
if (pPalQ->numColors >= pal->numColors) {
|
||||
// new palette will fit the slot
|
||||
|
||||
// install new palette
|
||||
pPalQ->hPal = hNewPal;
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
pPalQ->numColors = pal->numColors;
|
||||
|
||||
// Copy all the colors
|
||||
memcpy(pPalQ->palRGB, pal->palRGB, pal->numColors * sizeof(COLORREF));
|
||||
|
||||
if (!pPalQ->bFading)
|
||||
// Q the change to the video DAC
|
||||
UpdateDACqueue(pPalQ->posInDAC, pal->numColors, pPalQ->palRGB);
|
||||
} else {
|
||||
// Q the change to the video DAC
|
||||
UpdateDACqueueHandle(pPalQ->posInDAC, pal->numColors, hNewPal);
|
||||
}
|
||||
} else {
|
||||
// # colors are different - will have to update all following palette entries
|
||||
assert(TinselVersion <= 1); // Fatal error for Tinsel 2
|
||||
|
||||
PALQ *pNxtPalQ; // next palette queue position
|
||||
|
||||
for (pNxtPalQ = pPalQ + 1; pNxtPalQ < g_palAllocData + NUM_PALETTES; pNxtPalQ++) {
|
||||
if (pNxtPalQ->posInDAC >= pPalQ->posInDAC + pPalQ->numColors)
|
||||
// no need to move palettes down
|
||||
break;
|
||||
|
||||
// move palette down
|
||||
pNxtPalQ->posInDAC = (pPalQ->posInDAC + pPalQ->numColors) | PALETTE_MOVED;
|
||||
|
||||
// Q the palette change in position to the video DAC
|
||||
UpdateDACqueueHandle(pNxtPalQ->posInDAC,
|
||||
pNxtPalQ->numColors,
|
||||
pNxtPalQ->hPal);
|
||||
|
||||
// update previous palette to current palette
|
||||
pPalQ = pNxtPalQ;
|
||||
}
|
||||
}
|
||||
|
||||
delete pal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statless palette iterator. Returns the next palette in the list
|
||||
* @param pStrtPal Palette to start from - when NULL will start from beginning of list
|
||||
*/
|
||||
PALQ *GetNextPalette(PALQ *pStrtPal) {
|
||||
if (pStrtPal == NULL) {
|
||||
// start of palette iteration - return 1st palette
|
||||
return (g_palAllocData[0].objCount) ? g_palAllocData : NULL;
|
||||
}
|
||||
|
||||
// validate palette Q pointer
|
||||
assert(pStrtPal >= g_palAllocData && pStrtPal <= g_palAllocData + NUM_PALETTES - 1);
|
||||
|
||||
// return next active palette in list
|
||||
while (++pStrtPal < g_palAllocData + NUM_PALETTES) {
|
||||
if (pStrtPal->objCount)
|
||||
// active palette found
|
||||
return pStrtPal;
|
||||
}
|
||||
|
||||
// non found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current background color.
|
||||
* @param color Color to set the background to
|
||||
*/
|
||||
void SetBgndColor(COLORREF color) {
|
||||
// update background color struct by queuing the change to the video DAC
|
||||
UpdateDACqueue(BGND_DAC_INDEX, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note whether a palette is being faded.
|
||||
* @param pPalQ Palette queue position
|
||||
* @param bFading Whether it is fading
|
||||
*/
|
||||
void FadingPalette(PALQ *pPalQ, bool bFading) {
|
||||
// validate palette Q pointer
|
||||
assert(pPalQ >= g_palAllocData && pPalQ <= g_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() {
|
||||
PALQ *pPalQ;
|
||||
|
||||
for (pPalQ = g_palAllocData; pPalQ <= g_palAllocData + NUM_PALETTES - 1; pPalQ++) {
|
||||
pPalQ->bFading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the translucent palette from the current backgrounds palette.
|
||||
* @param hPalette Handle to current background palette
|
||||
*/
|
||||
void CreateTranslucentPalette(SCNHANDLE hPalette) {
|
||||
PALETTE *pal = _vm->_handle->GetPalette(hPalette);
|
||||
|
||||
// leave background color alone
|
||||
g_transPalette[0] = 0;
|
||||
|
||||
for (int32 i = 0; i < pal->numColors; i++) {
|
||||
byte red = pal->palette[i * 3];
|
||||
byte green = pal->palette[i * 3 + 1];
|
||||
byte blue = pal->palette[i * 3 + 2];
|
||||
|
||||
// calculate the Value field of the HSV color model
|
||||
unsigned val = (red > green) ? red : green;
|
||||
val = (val > blue) ? val : blue;
|
||||
|
||||
// map the Value field to one of the 4 colors reserved for the translucent palette
|
||||
val /= 63;
|
||||
byte blackColorIndex = (!TinselV1Mac) ? 0 : 255;
|
||||
g_transPalette[i + 1] = (uint8)((val == 0) ? blackColorIndex : val +
|
||||
((TinselVersion >= 2) ? TranslucentColor() : COL_HILIGHT) - 1);
|
||||
}
|
||||
|
||||
delete pal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an adjusted color RGB
|
||||
* @param color Color to scale
|
||||
*/
|
||||
static COLORREF DimColor(byte r, byte g, byte b, int factor) {
|
||||
if (factor == 10) {
|
||||
// No change
|
||||
return TINSEL_RGB(r, g, b);
|
||||
} else if (factor == 0) {
|
||||
// No brightness
|
||||
return 0;
|
||||
} else {
|
||||
// apply multiplier to RGB components
|
||||
uint32 red = r * factor / 10;
|
||||
uint32 green = g * factor / 10;
|
||||
uint32 blue = b * factor / 10;
|
||||
|
||||
// return new color
|
||||
return TINSEL_RGB(red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DimPartPalette
|
||||
*/
|
||||
void DimPartPalette(SCNHANDLE hDimPal, int startColor, int length, int brightness) {
|
||||
PALQ *pPalQ = FindPalette(hDimPal);
|
||||
assert(pPalQ);
|
||||
|
||||
// Adjust for the fact that palettes don't contain color 0
|
||||
startColor -= 1;
|
||||
|
||||
// Check some other things
|
||||
if (startColor + length > pPalQ->numColors)
|
||||
error("DimPartPalette(): color overrun");
|
||||
|
||||
// Check if the palette actually contains entries
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
PALETTE *pal = _vm->_handle->GetPalette(hDimPal);
|
||||
|
||||
for (int iColor = startColor; iColor < startColor + length; iColor++) {
|
||||
byte r = pal->palette[iColor * 3];
|
||||
byte g = pal->palette[iColor * 3 + 1];
|
||||
byte b = pal->palette[iColor * 3 + 2];
|
||||
pPalQ->palRGB[iColor] = DimColor(r, g, b, brightness);
|
||||
}
|
||||
|
||||
delete pal;
|
||||
|
||||
if (!pPalQ->bFading) {
|
||||
// Q the change to the video DAC
|
||||
UpdateDACqueue(pPalQ->posInDAC + startColor, length, &pPalQ->palRGB[startColor]);
|
||||
}
|
||||
}
|
||||
|
||||
int DarkGreen() {
|
||||
return _vm->screen().format.RGBToColor(0x00, 0x40, 0x00);
|
||||
}
|
||||
|
||||
int TranslucentColor() {
|
||||
return g_translucentIndex;
|
||||
}
|
||||
|
||||
int HighlightColor() {
|
||||
if (TinselVersion == 3) {
|
||||
return _vm->screen().format.RGBToColor(0x00, 0x80, 0x00);
|
||||
}
|
||||
UpdateDACqueue(g_talkIndex, (COLORREF)SysVar(SYS_HighlightRGB));
|
||||
|
||||
return g_talkIndex;
|
||||
}
|
||||
|
||||
int TalkColor() {
|
||||
return (TinselVersion >= 2) ? g_talkIndex : TALKFONT_COL;
|
||||
}
|
||||
|
||||
void SetTalkColorRef(COLORREF colRef) {
|
||||
g_talkColRef = colRef;
|
||||
}
|
||||
|
||||
COLORREF GetTalkColorRef() {
|
||||
return g_talkColRef;
|
||||
}
|
||||
|
||||
void SetTagColorRef(COLORREF colRef) {
|
||||
g_tagColRef = colRef;
|
||||
}
|
||||
|
||||
COLORREF GetTagColorRef() {
|
||||
return g_tagColRef;
|
||||
}
|
||||
|
||||
void SetTranslucencyOffset(int offset) {
|
||||
g_translucentIndex = offset;
|
||||
}
|
||||
|
||||
void SetTalkTextOffset(int offset) {
|
||||
g_talkIndex = offset;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
169
engines/tinsel/palette.h
Normal file
169
engines/tinsel/palette.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Palette Allocator Definitions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_PALETTE_H // prevent multiple includes
|
||||
#define TINSEL_PALETTE_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
typedef uint32 COLORREF;
|
||||
|
||||
#define TINSEL_RGB(r,g,b) ((COLORREF)((uint8)(r)|((uint16)(g)<<8))|(((uint32)(uint8)(b))<<16))
|
||||
#define TINSEL_PSX_RGB(r,g,b) ((uint16)(((uint8)(r))|((uint16)(g)<<5)|(((uint16)(b))<<10)))
|
||||
|
||||
enum {
|
||||
MAX_COLORS = 256, ///< maximum number of colors - 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 = 32, ///< number of palettes
|
||||
|
||||
// Discworld has some fixed apportioned bits in the palette.
|
||||
BGND_DAC_INDEX = 0, ///< index of background color in Video DAC
|
||||
FGND_DAC_INDEX = 1, ///< index of first foreground color in Video DAC
|
||||
TBLUE1 = 228, ///< Blue used in translucent rectangles
|
||||
TBLUE2 = 229, ///< Blue used in translucent rectangles
|
||||
TBLUE3 = 230, ///< Blue used in translucent rectangles
|
||||
TBLUE4 = 231, ///< Blue used in translucent rectangles
|
||||
TALKFONT_COL = 233
|
||||
};
|
||||
|
||||
// some common colors
|
||||
|
||||
#define BLACK (TINSEL_RGB(0, 0, 0))
|
||||
#define WHITE (TINSEL_RGB(MAX_INTENSITY, MAX_INTENSITY, MAX_INTENSITY))
|
||||
#define RED (TINSEL_RGB(MAX_INTENSITY, 0, 0))
|
||||
#define GREEN (TINSEL_RGB(0, MAX_INTENSITY, 0))
|
||||
#define BLUE (TINSEL_RGB(0, 0, MAX_INTENSITY))
|
||||
#define YELLOW (TINSEL_RGB(MAX_INTENSITY, MAX_INTENSITY, 0))
|
||||
#define MAGENTA (TINSEL_RGB(MAX_INTENSITY, 0, MAX_INTENSITY))
|
||||
#define CYAN (TINSEL_RGB(0, MAX_INTENSITY, MAX_INTENSITY))
|
||||
|
||||
struct PALETTE {
|
||||
int32 numColors; ///< number of colors in the palette
|
||||
COLORREF palRGB[MAX_COLORS]; ///< actual palette colors
|
||||
byte palette[MAX_COLORS * 3]; ///< actual palette colors (RGB values)
|
||||
};
|
||||
|
||||
/** palette queue structure */
|
||||
struct PALQ {
|
||||
SCNHANDLE hPal; ///< handle to palette data struct
|
||||
int objCount; ///< number of objects using this palette
|
||||
int posInDAC; ///< palette position in the video DAC
|
||||
int32 numColors; ///< number of colors in the palette
|
||||
// Discworld 2 fields
|
||||
bool bFading; // Whether or not fading
|
||||
COLORREF palRGB[MAX_COLORS]; // actual palette colors
|
||||
};
|
||||
|
||||
#define PALETTE_MOVED 0x8000 // when this bit is set in the "posInDAC"
|
||||
// field - the palette entry has moved
|
||||
|
||||
// Translucent objects have NULL pPal
|
||||
#define HasPalMoved(pPal) (((pPal) != NULL) && ((pPal)->posInDAC & PALETTE_MOVED))
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Palette Manager Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
void ResetPalAllocator(); // wipe out all palettes
|
||||
|
||||
#ifdef DEBUG
|
||||
void PaletteStats(); // Shows the maximum number of palettes used at once
|
||||
#endif
|
||||
|
||||
void psxPaletteMapper(PALQ *originalPal, uint8 *psxClut, byte *mapperTable); // Maps PSX CLUTs to original palette in resource file
|
||||
|
||||
void PalettesToVideoDAC(); // Update the video DAC with palettes currently in the DAC queue
|
||||
|
||||
void UpdateDACqueueHandle(
|
||||
int posInDAC, // position in video DAC
|
||||
int numColors, // number of colors in palette
|
||||
SCNHANDLE hPalette); // handle to palette
|
||||
|
||||
void UpdateDACqueue( // places a palette in the video DAC queue
|
||||
int posInDAC, // position in video DAC
|
||||
int numColors, // number of colors in palette
|
||||
COLORREF *pColors); // list of RGB tripples
|
||||
|
||||
void UpdateDACqueue(int posInDAC, COLORREF color);
|
||||
|
||||
PALQ *AllocPalette( // allocate a new palette
|
||||
SCNHANDLE hNewPal); // palette to allocate
|
||||
|
||||
void FreePalette( // free a palette allocated with "AllocPalette"
|
||||
PALQ *pFreePal); // palette queue entry to free
|
||||
|
||||
PALQ *FindPalette( // find a palette in the palette queue
|
||||
SCNHANDLE hSrchPal); // palette to search for
|
||||
|
||||
void SwapPalette( // swaps palettes at the specified palette queue position
|
||||
PALQ *pPalQ, // palette queue position
|
||||
SCNHANDLE hNewPal); // new palette
|
||||
|
||||
PALQ *GetNextPalette( // returns the next palette in the queue
|
||||
PALQ *pStrtPal); // queue position to start from - when NULL will start from beginning of queue
|
||||
|
||||
COLORREF GetBgndColor(); // returns current background color
|
||||
|
||||
void SetBgndColor( // sets current background color
|
||||
COLORREF color); // color to set the background to
|
||||
|
||||
void FadingPalette(PALQ *pPalQ, bool bFading);
|
||||
|
||||
void CreateTranslucentPalette(SCNHANDLE BackPal);
|
||||
|
||||
void NoFadingPalettes(); // All fading processes have just been killed
|
||||
|
||||
void DimPartPalette(
|
||||
SCNHANDLE hPal,
|
||||
int startColor,
|
||||
int length,
|
||||
int brightness); // 0 = black, 10 == 100%
|
||||
|
||||
|
||||
int TranslucentColor();
|
||||
int DarkGreen();
|
||||
|
||||
#define BoxColor (TinselVersion == 3 ? DarkGreen : TranslucentColor)
|
||||
|
||||
int HighlightColor();
|
||||
|
||||
int TalkColor();
|
||||
|
||||
void SetTalkColorRef(COLORREF colRef);
|
||||
|
||||
COLORREF GetTalkColorRef();
|
||||
|
||||
void SetTagColorRef(COLORREF colRef);
|
||||
|
||||
COLORREF GetTagColorRef();
|
||||
|
||||
void SetTalkTextOffset(int offset);
|
||||
|
||||
void SetTranslucencyOffset(int offset);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_PALETTE_H
|
||||
1027
engines/tinsel/pcode.cpp
Normal file
1027
engines/tinsel/pcode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
165
engines/tinsel/pcode.h
Normal file
165
engines/tinsel/pcode.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Virtual processor definitions
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_PCODE_H // prevent multiple includes
|
||||
#define TINSEL_PCODE_H
|
||||
|
||||
#include "tinsel/events.h" // for TINSEL_EVENT
|
||||
#include "tinsel/sched.h" // for Common::PROCESS
|
||||
|
||||
namespace Common {
|
||||
class Serializer;
|
||||
}
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
class InventoryObject;
|
||||
|
||||
enum RESUME_STATE {
|
||||
RES_NOT, RES_1, RES_2, RES_SAVEGAME
|
||||
};
|
||||
|
||||
enum {
|
||||
PCODE_STACK_SIZE = 128 ///< interpeters stack size
|
||||
};
|
||||
|
||||
enum GSORT {
|
||||
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 WorkaroundEntry;
|
||||
|
||||
struct INT_CONTEXT {
|
||||
|
||||
// Elements for interpret context management
|
||||
Common::PROCESS *pProc; ///< processes owning this context
|
||||
GSORT GSort; ///< sort of this context
|
||||
|
||||
// Previously parameters to Interpret()
|
||||
SCNHANDLE hCode; ///< scene handle of the code to execute
|
||||
byte *code; ///< pointer to the code to execute
|
||||
TINSEL_EVENT event; ///< causal event
|
||||
HPOLYGON hPoly; ///< associated polygon (if any)
|
||||
int idActor; ///< associated actor (if any)
|
||||
const InventoryObject *pinvo; ///< associated inventory object
|
||||
|
||||
// Previously local variables in Interpret()
|
||||
int32 stack[PCODE_STACK_SIZE]; ///< interpeters run time stack
|
||||
int sp; ///< stack pointer
|
||||
int bp; ///< base pointer
|
||||
int ip; ///< instruction pointer
|
||||
bool bHalt; ///< set to exit interpeter
|
||||
bool escOn;
|
||||
int myEscape; ///< only initialized to prevent compiler warning!
|
||||
|
||||
uint32 waitNumber1; // The waiting numbert
|
||||
uint32 waitNumber2; // The wait for number
|
||||
RESCODE resumeCode;
|
||||
RESUME_STATE resumeState;
|
||||
|
||||
// Used to store execution state within a script workaround fragment
|
||||
const WorkaroundEntry *fragmentPtr;
|
||||
|
||||
void syncWithSerializer(Common::Serializer &s);
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Interpreter Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
// 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
|
||||
TINSEL_EVENT event, // causal event
|
||||
HPOLYGON hpoly, // associated polygon (if any)
|
||||
int actorid, // associated actor (if any)
|
||||
const InventoryObject *pinvo,
|
||||
int myEscape = -1); // associated inventory object
|
||||
|
||||
INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric);
|
||||
|
||||
void FreeMostInterpretContexts();
|
||||
void FreeMasterInterpretContext();
|
||||
|
||||
void SaveInterpretContexts(INT_CONTEXT *sICInfo);
|
||||
|
||||
void RegisterGlobals(int num);
|
||||
void FreeGlobals();
|
||||
|
||||
void AttachInterpret(INT_CONTEXT *pic, Common::PROCESS *pProc);
|
||||
|
||||
void WaitInterpret(CORO_PARAM, Common::PPROCESS pWaitProc, bool *result);
|
||||
|
||||
#define NUM_INTERPRET (CORO_NUM_PROCESS - 20)
|
||||
#define MAX_INTERPRET (CORO_MAX_PROCESSES - 20)
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Library Procedure and Function codes parameter enums *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
#define TAG_DEF 0 // For tagactor()
|
||||
#define TAG_Q1TO3 1 // tag types
|
||||
#define TAG_Q1TO4 2 // tag types
|
||||
|
||||
#define CONV_DEF 0 //
|
||||
#define CONV_BOTTOM 1 // conversation() parameter
|
||||
#define CONV_END 2 //
|
||||
|
||||
#define CONTROL_OFF 0 // control()
|
||||
#define CONTROL_ON 1 // parameter
|
||||
#define CONTROL_OFFV 2 //
|
||||
#define CONTROL_OFFV2 3 //
|
||||
#define CONTROL_STARTOFF 4 //
|
||||
|
||||
#define NULL_ACTOR (-1) // For actor parameters
|
||||
#define LEAD_ACTOR (-2) //
|
||||
|
||||
#define RAND_NORM 0 // For random() frills
|
||||
#define RAND_NORPT 1 //
|
||||
|
||||
#define D_UP 1
|
||||
#define D_DOWN 0
|
||||
|
||||
#define TW_START 1 // topwindow() parameter
|
||||
#define TW_END 2 //
|
||||
|
||||
#define MIDI_DEF 0
|
||||
#define MIDI_LOOP 1
|
||||
|
||||
#define FM_IN 0 //
|
||||
#define FM_OUT 1 // fademidi()
|
||||
|
||||
#define FG_ON 0 //
|
||||
#define FG_OFF 1 // FrameGrab()
|
||||
|
||||
#define ST_ON 0 //
|
||||
#define ST_OFF 1 // SubTitles()
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_PCODE_H
|
||||
846
engines/tinsel/pdisplay.cpp
Normal file
846
engines/tinsel/pdisplay.cpp
Normal file
@@ -0,0 +1,846 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* CursorPositionProcess()
|
||||
* TagProcess()
|
||||
* PointProcess()
|
||||
*/
|
||||
|
||||
#include "common/coroutines.h"
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/events.h"
|
||||
#include "tinsel/font.h"
|
||||
#include "tinsel/graphics.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/object.h"
|
||||
#include "tinsel/pcode.h"
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/movers.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/strres.h"
|
||||
#include "tinsel/text.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- EXTERNAL GLOBAL DATA --------------------
|
||||
|
||||
#ifdef DEBUG
|
||||
//extern int Overrun; // The overrun counter, in DOS_DW.C
|
||||
|
||||
extern int g_newestString; // The overrun counter, in STRRES.C
|
||||
#endif
|
||||
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
enum HotSpotTag {
|
||||
NO_HOTSPOT_TAG,
|
||||
POLY_HOTSPOT_TAG,
|
||||
ACTOR_HOTSPOT_TAG
|
||||
};
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
static bool g_DispPath = false;
|
||||
static bool g_bShowString = false;
|
||||
|
||||
static int g_TaggedActor = 0;
|
||||
static HPOLYGON g_hTaggedPolygon = NOPOLY;
|
||||
|
||||
static bool g_bTagsActive = true;
|
||||
|
||||
static bool g_bPointingActive = true;
|
||||
static int tagX = 0, tagY = 0; // Values when tag was displayed
|
||||
static int Loffset = 0, Toffset = 0; // Values when tag was displayed
|
||||
static int curX = 0, curY = 0;
|
||||
|
||||
void ResetVarsPDisplay() {
|
||||
g_DispPath = false;
|
||||
g_bShowString = false;
|
||||
|
||||
g_TaggedActor = 0;
|
||||
g_hTaggedPolygon = NOPOLY;
|
||||
|
||||
g_bTagsActive = true;
|
||||
|
||||
g_bPointingActive = true;
|
||||
|
||||
tagX = tagY = 0;
|
||||
Loffset = Toffset = 0;
|
||||
curX = curY = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Displays the cursor and lead actor's co-ordinates and the overrun
|
||||
* counter. Also which path polygon the cursor is in, if required.
|
||||
*
|
||||
* This process is only started up if a Glitter showpos() call is made.
|
||||
* Obviously, this is for testing purposes only...
|
||||
*/
|
||||
void CursorPositionProcess(CORO_PARAM, const void *) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
int prevsX, prevsY; // Last screen top left
|
||||
int prevcX, prevcY; // Last displayed cursor position
|
||||
int prevlX, prevlY; // Last displayed lead actor position
|
||||
// int prevOver; // Last displayed overrun
|
||||
int prevString; // Last displayed string number
|
||||
|
||||
OBJECT *cpText; // cursor position text object pointer
|
||||
OBJECT *cpathText; // cursor path text object pointer
|
||||
OBJECT *rpText; // text object pointer
|
||||
// OBJECT *opText; // text object pointer
|
||||
OBJECT *spText; // string number text object pointer
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
_ctx->prevsX = -1;
|
||||
_ctx->prevsY = -1;
|
||||
_ctx->prevcX = -1;
|
||||
_ctx->prevcY = -1;
|
||||
_ctx->prevlX = -1;
|
||||
_ctx->prevlY = -1;
|
||||
// _ctx->prevOver = -1;
|
||||
_ctx->prevString = -1;
|
||||
|
||||
_ctx->cpText = nullptr;
|
||||
_ctx->cpathText = nullptr;
|
||||
_ctx->rpText = nullptr;
|
||||
// _ctx->opText = nullptr;
|
||||
_ctx->spText = nullptr;
|
||||
|
||||
|
||||
int aniX, aniY; // cursor/lead actor position
|
||||
int Loffset, Toffset; // Screen top left
|
||||
|
||||
char PositionString[64]; // Common::sprintf_s() things into here
|
||||
|
||||
MOVER *pActor; // Lead actor
|
||||
|
||||
while (1) {
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
|
||||
/*-----------------------------------*\
|
||||
| Cursor's position and path display. |
|
||||
\*-----------------------------------*/
|
||||
_vm->_cursor->GetCursorXY(&aniX, &aniY, false);
|
||||
|
||||
// Change in cursor position?
|
||||
if (aniX != _ctx->prevcX || aniY != _ctx->prevcY ||
|
||||
Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
|
||||
// kill current text objects
|
||||
if (_ctx->cpText) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->cpText);
|
||||
}
|
||||
if (_ctx->cpathText) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->cpathText);
|
||||
_ctx->cpathText = nullptr;
|
||||
}
|
||||
|
||||
// New text objects
|
||||
Common::sprintf_s(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
|
||||
_ctx->cpText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
|
||||
0, CPOSX, POSY, _vm->_font->GetTagFontHandle(), TXT_CENTER);
|
||||
if (g_DispPath) {
|
||||
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
|
||||
if (hp == NOPOLY)
|
||||
Common::sprintf_s(PositionString, "No path");
|
||||
else
|
||||
Common::sprintf_s(PositionString, "%d,%d %d,%d %d,%d %d,%d",
|
||||
PolyCornerX(hp, 0), PolyCornerY(hp, 0),
|
||||
PolyCornerX(hp, 1), PolyCornerY(hp, 1),
|
||||
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
|
||||
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
|
||||
_ctx->cpathText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
|
||||
0, 4, POSY+ 10, _vm->_font->GetTagFontHandle(), 0);
|
||||
}
|
||||
|
||||
// update previous position
|
||||
_ctx->prevcX = aniX;
|
||||
_ctx->prevcY = aniY;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*------------------------*\
|
||||
| Overrun counter display. |
|
||||
\*------------------------*/
|
||||
if (Overrun != _ctx->prevOver) {
|
||||
// kill current text objects
|
||||
if (_ctx->opText) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->opText);
|
||||
}
|
||||
|
||||
Common::sprintf_s(PositionString, "%d", Overrun);
|
||||
_ctx->opText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
|
||||
0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
|
||||
|
||||
// update previous value
|
||||
_ctx->prevOver = Overrun;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------*\
|
||||
| Lead actor's position. |
|
||||
\*----------------------*/
|
||||
pActor = GetMover(LEAD_ACTOR);
|
||||
if (pActor && getMActorState(pActor)) {
|
||||
// get lead's animation position
|
||||
_vm->_actor->GetActorPos(LEAD_ACTOR, &aniX, &aniY);
|
||||
|
||||
// Change in position?
|
||||
if (aniX != _ctx->prevlX || aniY != _ctx->prevlY ||
|
||||
Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
|
||||
// Kill current text objects
|
||||
if (_ctx->rpText) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->rpText);
|
||||
}
|
||||
|
||||
// create new text object list
|
||||
Common::sprintf_s(PositionString, "%d %d", aniX, aniY);
|
||||
_ctx->rpText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
|
||||
0, LPOSX, POSY, _vm->_font->GetTagFontHandle(), TXT_CENTER);
|
||||
|
||||
// update previous position
|
||||
_ctx->prevlX = aniX;
|
||||
_ctx->prevlY = aniY;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------*\
|
||||
| String number |
|
||||
\*-------------*/
|
||||
if (g_bShowString && g_newestString != _ctx->prevString) {
|
||||
// kill current text objects
|
||||
if (_ctx->spText) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->spText);
|
||||
}
|
||||
|
||||
Common::sprintf_s(PositionString, "String: %d", g_newestString);
|
||||
_ctx->spText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
|
||||
0, SPOSX, POSY+10, _vm->_font->GetTalkFontHandle(), TXT_CENTER);
|
||||
|
||||
// update previous value
|
||||
_ctx->prevString = g_newestString;
|
||||
}
|
||||
|
||||
// update previous playfield position
|
||||
_ctx->prevsX = Loffset;
|
||||
_ctx->prevsY = Toffset;
|
||||
|
||||
CORO_SLEEP(1); // allow re-scheduling
|
||||
}
|
||||
CORO_END_CODE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* While inventory/menu is open.
|
||||
*/
|
||||
void DisablePointing() {
|
||||
int i;
|
||||
HPOLYGON hPoly; // Polygon handle
|
||||
|
||||
g_bPointingActive = false;
|
||||
|
||||
for (i = 0; i < MAX_POLY; i++) {
|
||||
hPoly = GetPolyHandle(i);
|
||||
|
||||
if (hPoly != NOPOLY && PolyType(hPoly) == TAG && PolyIsPointedTo(hPoly)) {
|
||||
SetPolyPointedTo(hPoly, false);
|
||||
SetPolyTagWanted(hPoly, false, false, 0);
|
||||
PolygonEvent(Common::nullContext, hPoly, UNPOINT, 0, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// For each tagged actor
|
||||
for (i = 0; (i = _vm->_actor->NextTaggedActor(i)) != 0;) {
|
||||
if (_vm->_actor->ActorIsPointedTo(i)) {
|
||||
_vm->_actor->SetActorPointedTo(i, false);
|
||||
_vm->_actor->SetActorTagWanted(i, false, false, 0);
|
||||
|
||||
ActorEvent(Common::nullContext, i, UNPOINT, false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EnablePointing()
|
||||
*/
|
||||
void EnablePointing() {
|
||||
g_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 ProcessUserEvent().
|
||||
*/
|
||||
static void SaveTaggedActor(int ano) {
|
||||
g_TaggedActor = 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 ProcessUserEvent().
|
||||
*/
|
||||
int GetTaggedActor() {
|
||||
return g_TaggedActor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag process keeps us updated as to which polygon is currently tagged
|
||||
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
|
||||
*/
|
||||
static void SaveTaggedPoly(HPOLYGON hp) {
|
||||
g_hTaggedPolygon = hp;
|
||||
}
|
||||
|
||||
HPOLYGON GetTaggedPoly() {
|
||||
return g_hTaggedPolygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If TRUE, the mid-top co-ordinates of the actor's tag area are also
|
||||
* returned.
|
||||
*/
|
||||
static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
|
||||
int Top, Bot; // Top and bottom limits of active area
|
||||
int left, right; // left and right of active area
|
||||
int qrt = 0; // 1/4 of height (sometimes 1/2)
|
||||
|
||||
// First check if within x-range
|
||||
if (aniX > (left = _vm->_actor->GetActorLeft(ano)) && aniX < (right = _vm->_actor->GetActorRight(ano))) {
|
||||
Top = _vm->_actor->GetActorTop(ano);
|
||||
Bot = _vm->_actor->GetActorBottom(ano);
|
||||
|
||||
// y-range varies according to tag-type
|
||||
switch (_vm->_actor->TagType(ano)) {
|
||||
case TAG_DEF:
|
||||
// Next to bottom 1/4 of the actor's area
|
||||
qrt = (Bot - Top) >> 1; // Half actor's height
|
||||
Top += qrt; // Top = mid-height
|
||||
|
||||
qrt = qrt >> 1; // Quarter height
|
||||
Bot -= qrt; // Bot = 1/4 way up
|
||||
break;
|
||||
|
||||
case TAG_Q1TO3:
|
||||
// Top 3/4 of the actor's area
|
||||
qrt = (Bot - Top) >> 2; // 1/4 actor's height
|
||||
Bot -= qrt; // Bot = 1/4 way up
|
||||
break;
|
||||
|
||||
case TAG_Q1TO4:
|
||||
// All the actor's area
|
||||
break;
|
||||
|
||||
default:
|
||||
error("illegal tag area type");
|
||||
}
|
||||
|
||||
// Now check if within y-range
|
||||
if (aniY >= Top && aniY <= Bot) {
|
||||
if (_vm->_actor->TagType(ano) == TAG_Q1TO3)
|
||||
*pytext = Top + qrt;
|
||||
else
|
||||
*pytext = Top;
|
||||
*pxtext = (left + right) / 2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the cursor is over a tagged actor's hot-spot. If so, display
|
||||
* the tag or, if tag already displayed, maintain the tag's position on
|
||||
* the screen.
|
||||
*/
|
||||
static bool ActorTag(int curX_, int curY_, HotSpotTag *pTag, OBJECT **ppText) {
|
||||
int newX, newY; // new values, to keep tag in place
|
||||
int ano;
|
||||
int xtext, ytext;
|
||||
bool newActor;
|
||||
char tagBuffer[64];
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// Tinsel 2 version
|
||||
// Get the foremost pointed to actor
|
||||
int actor = _vm->_actor->FrontTaggedActor();
|
||||
|
||||
if (actor == 0) {
|
||||
SaveTaggedActor(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If new actor
|
||||
// or actor has suddenly decided it wants tagging...
|
||||
if (actor != GetTaggedActor() || (_vm->_actor->ActorTagIsWanted(actor) && !*ppText)) {
|
||||
// Put up actor tag
|
||||
SaveTaggedActor(actor); // This actor tagged
|
||||
SaveTaggedPoly(NOPOLY); // No tagged polygon
|
||||
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, ppText);
|
||||
|
||||
if (_vm->_actor->ActorTagIsWanted(actor)) {
|
||||
_vm->_actor->GetActorTagPos(actor, &tagX, &tagY, false);
|
||||
LoadStringRes(_vm->_actor->GetActorTagHandle(actor), tagBuffer, sizeof(tagBuffer));
|
||||
|
||||
// May have buggered cursor
|
||||
_vm->_cursor->EndCursorFollowed();
|
||||
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), tagBuffer,
|
||||
0, tagX, tagY, _vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
|
||||
assert(*ppText);
|
||||
MultiSetZPosition(*ppText, Z_TAG_TEXT);
|
||||
} else
|
||||
*ppText = nullptr;
|
||||
} else if (*ppText) {
|
||||
// Same actor, maintain tag position
|
||||
_vm->_actor->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....
|
||||
_vm->_actor->FirstTaggedActor();
|
||||
while ((ano = _vm->_actor->NextTaggedActor()) != 0) {
|
||||
if (InHotSpot(ano, curX_, curY_, &xtext, &ytext)) {
|
||||
// Put up or maintain actor tag
|
||||
if (*pTag != ACTOR_HOTSPOT_TAG)
|
||||
newActor = true;
|
||||
else if (ano != GetTaggedActor())
|
||||
newActor = true; // Different actor
|
||||
else
|
||||
newActor = false; // Same actor
|
||||
|
||||
if (newActor) {
|
||||
// Display actor's tag
|
||||
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, ppText);
|
||||
|
||||
*pTag = ACTOR_HOTSPOT_TAG;
|
||||
SaveTaggedActor(ano); // This actor tagged
|
||||
SaveTaggedPoly(NOPOLY); // No tagged polygon
|
||||
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
|
||||
LoadStringRes(_vm->_actor->GetActorTag(ano), _vm->_font->TextBufferAddr(), TBUFSZ);
|
||||
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
|
||||
0, xtext - tagX, ytext - tagY, _vm->_font->GetTagFontHandle(), TXT_CENTER);
|
||||
assert(*ppText); // Actor tag string produced NULL text
|
||||
MultiSetZPosition(*ppText, Z_TAG_TEXT);
|
||||
} else {
|
||||
// Maintain actor tag's position
|
||||
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
|
||||
if (newX != tagX || newY != tagY) {
|
||||
MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
|
||||
tagX = newX;
|
||||
tagY = newY;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No tagged actor
|
||||
if (*pTag == ACTOR_HOTSPOT_TAG) {
|
||||
*pTag = NO_HOTSPOT_TAG;
|
||||
SaveTaggedActor(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perhaps some comment in due course.
|
||||
*
|
||||
* Under control of PointProcess(), when the cursor is over a TAG or
|
||||
* EXIT polygon, its pointState flag is set to POINTING. If its Glitter
|
||||
* code contains a printtag() call, its tagState flag gets set to TAG_ON.
|
||||
*/
|
||||
static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
|
||||
int nLoff, nToff; // new values, to keep tag in place
|
||||
HPOLYGON hp;
|
||||
bool newPoly;
|
||||
int shift;
|
||||
|
||||
int tagx, tagy; // Tag display co-ordinates
|
||||
SCNHANDLE hTagtext; // Tag text
|
||||
|
||||
// For each polgon with a tag....
|
||||
for (int i = 0; i < MAX_POLY; i++) {
|
||||
hp = GetPolyHandle(i);
|
||||
|
||||
if ((TinselVersion >= 2) && (hp == NOPOLY))
|
||||
continue;
|
||||
|
||||
// Added code for un-tagged tags
|
||||
if ((hp != NOPOLY) && (PolyPointState(hp) == PS_POINTING) && (PolyTagState(hp) != TAG_ON)) {
|
||||
// This poly is entitled to be tagged
|
||||
if (hp != GetTaggedPoly()) {
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, ppText);
|
||||
*pTag = POLY_HOTSPOT_TAG;
|
||||
SaveTaggedActor(0); // No tagged actor
|
||||
SaveTaggedPoly(hp); // This polygon tagged
|
||||
}
|
||||
return true;
|
||||
} else if (((TinselVersion >= 2) && PolyTagIsWanted(hp)) ||
|
||||
((TinselVersion <= 1) && hp != NOPOLY && PolyTagState(hp) == TAG_ON)) {
|
||||
// Put up or maintain polygon tag
|
||||
newPoly = false;
|
||||
if (TinselVersion >= 2) {
|
||||
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(_vm->_bg->GetPlayfieldList(FIELD_STATUS), *ppText);
|
||||
|
||||
if (TinselVersion <= 1)
|
||||
*pTag = POLY_HOTSPOT_TAG;
|
||||
SaveTaggedActor(0); // No tagged actor
|
||||
SaveTaggedPoly(hp); // This polygon tagged
|
||||
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
GetTagTag(hp, &hTagtext, &tagx, &tagy);
|
||||
|
||||
int strLen;
|
||||
if (GetPolyTagHandle(hp) != 0)
|
||||
strLen = LoadStringRes(GetPolyTagHandle(hp), _vm->_font->TextBufferAddr(), TBUFSZ);
|
||||
else
|
||||
strLen = LoadStringRes(hTagtext, _vm->_font->TextBufferAddr(), TBUFSZ);
|
||||
|
||||
if (strLen == 0)
|
||||
// No valid string returned, so leave ppText as NULL
|
||||
ppText = nullptr;
|
||||
else if ((TinselVersion >= 2) && !PolyTagFollowsCursor(hp)) {
|
||||
// May have buggered cursor
|
||||
_vm->_cursor->EndCursorFollowed();
|
||||
|
||||
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS),
|
||||
_vm->_font->TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
|
||||
_vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
|
||||
} else if (TinselVersion >= 2) {
|
||||
// Bugger cursor
|
||||
const char *tagPtr = _vm->_font->TextBufferAddr();
|
||||
if (tagPtr[0] < ' ' && tagPtr[1] == EOS_CHAR)
|
||||
_vm->_cursor->StartCursorFollowed();
|
||||
|
||||
_vm->_cursor->GetCursorXYNoWait(&curX, &curY, false);
|
||||
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
|
||||
0, curX, curY, _vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
|
||||
} else {
|
||||
// Handle displaying the tag text on-screen
|
||||
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
|
||||
0, tagx - Loffset, tagy - Toffset,
|
||||
_vm->_font->GetTagFontHandle(), TXT_CENTER);
|
||||
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 >= _vm->_bg->BgWidth()) // Not off right
|
||||
MultiMoveRelXY(*ppText, _vm->_bg->BgWidth() - shift, 0);
|
||||
shift = MultiLeftmost(*ppText) + Loffset - 1;
|
||||
if (shift <= 0) // Not off left
|
||||
MultiMoveRelXY(*ppText, -shift, 0);
|
||||
shift = MultiLowest(*ppText) + Toffset;
|
||||
if (shift > _vm->_bg->BgHeight()) // Not off bottom
|
||||
MultiMoveRelXY(*ppText, 0, _vm->_bg->BgHeight() - shift);
|
||||
}
|
||||
} else if ((TinselVersion >= 2) && (*ppText)) {
|
||||
if (!PolyTagFollowsCursor(hp)) {
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
|
||||
if (nLoff != Loffset || nToff != Toffset) {
|
||||
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
|
||||
Loffset = nLoff;
|
||||
Toffset = nToff;
|
||||
}
|
||||
} else {
|
||||
_vm->_cursor->GetCursorXY(&tagx, &tagy, false);
|
||||
if (tagx != curX || tagy != curY) {
|
||||
MultiMoveRelXY(*ppText, tagx - curX, tagy - curY);
|
||||
curX = tagx;
|
||||
curY = tagy;
|
||||
}
|
||||
}
|
||||
} else if (TinselVersion <= 1) {
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
|
||||
if (nLoff != Loffset || nToff != Toffset) {
|
||||
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
|
||||
Loffset = nLoff;
|
||||
Toffset = nToff;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No tagged polygon
|
||||
if (TinselVersion >= 2)
|
||||
SaveTaggedPoly(NOPOLY);
|
||||
else if (*pTag == POLY_HOTSPOT_TAG) {
|
||||
*pTag = NO_HOTSPOT_TAG;
|
||||
SaveTaggedPoly(NOPOLY);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle display of tagged actor and polygon tags.
|
||||
* Tagged actor's get priority over polygons.
|
||||
*/
|
||||
void TagProcess(CORO_PARAM, const void *) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
OBJECT *pText; // text object pointer
|
||||
HotSpotTag Tag;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
_ctx->pText = nullptr;
|
||||
_ctx->Tag = NO_HOTSPOT_TAG;
|
||||
|
||||
SaveTaggedActor(0); // No tagged actor yet
|
||||
SaveTaggedPoly(NOPOLY); // No tagged polygon yet
|
||||
|
||||
while (1) {
|
||||
if (g_bTagsActive) {
|
||||
int curX_, curY_; // cursor position
|
||||
while (!_vm->_cursor->GetCursorXYNoWait(&curX_, &curY_, true))
|
||||
CORO_SLEEP(1);
|
||||
|
||||
if (!ActorTag(curX_, curY_, &_ctx->Tag, &_ctx->pText)
|
||||
&& !PolyTag(&_ctx->Tag, &_ctx->pText)) {
|
||||
// Nothing tagged. Remove tag, if there is one
|
||||
if (_ctx->pText) {
|
||||
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->pText);
|
||||
_ctx->pText = nullptr;
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
// May have buggered cursor
|
||||
_vm->_cursor->EndCursorFollowed();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SaveTaggedActor(0);
|
||||
SaveTaggedPoly(NOPOLY);
|
||||
|
||||
// Remove tag, if there is one
|
||||
if (_ctx->pText) {
|
||||
// kill current text objects
|
||||
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
|
||||
_ctx->Tag = NO_HOTSPOT_TAG;
|
||||
}
|
||||
}
|
||||
|
||||
CORO_SLEEP(1); // allow re-scheduling
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from PointProcess() as appropriate.
|
||||
*/
|
||||
static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
SetPolyPointState(hp, PS_POINTING);
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
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(CORO_PARAM, HPOLYGON hp) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
SetPolyPointState(hp, PS_NOT_POINTING);
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, UNPOINT, 0, false, 0));
|
||||
SetPolyTagWanted(hp, false, false, 0);
|
||||
|
||||
} else if (PolyTagState(hp) == TAG_ON) {
|
||||
// Delete this tag entry
|
||||
SetPolyTagState(hp, TAG_OFF);
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* For TAG and EXIT polygons, monitor cursor entering and leaving.
|
||||
* Maintain the polygons' pointState and tagState flags accordingly.
|
||||
* Also run the polygon's Glitter code when the cursor enters.
|
||||
*/
|
||||
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 (TinselVersion >= 2)
|
||||
EnablePointing();
|
||||
|
||||
while (1) {
|
||||
while (!_vm->_cursor->GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
|
||||
CORO_SLEEP(1);
|
||||
|
||||
/*----------------------------------*\
|
||||
| For polygons of type TAG and EXIT. |
|
||||
\*----------------------------------*/
|
||||
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 (TinselVersion >= 2) {
|
||||
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 (!IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
|
||||
if (TinselVersion >= 2) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// For each tagged actor
|
||||
for (_ctx->i = 0; (_ctx->i = _vm->_actor->NextTaggedActor(_ctx->i)) != 0;) {
|
||||
if (!_vm->_actor->ActorIsPointedTo(_ctx->i)) {
|
||||
if (_vm->_actor->InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
|
||||
_vm->_actor->SetActorPointedTo(_ctx->i, true);
|
||||
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, POINTED, false, 0));
|
||||
}
|
||||
} else {
|
||||
if (!_vm->_actor->InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
|
||||
_vm->_actor->SetActorPointedTo(_ctx->i, false);
|
||||
_vm->_actor->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 (!g_bPointingActive);
|
||||
} else {
|
||||
// allow re-scheduling
|
||||
CORO_SLEEP(1);
|
||||
}
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
void DisableTags() {
|
||||
g_bTagsActive = false;
|
||||
}
|
||||
|
||||
void EnableTags() {
|
||||
g_bTagsActive = true;
|
||||
}
|
||||
|
||||
bool DisableTagsIfEnabled() {
|
||||
if (g_bTagsActive) {
|
||||
DisableTags();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing purposes only.
|
||||
* Causes CursorPositionProcess() to display, or not, the path that the
|
||||
* cursor is in.
|
||||
*/
|
||||
void TogglePathDisplay() {
|
||||
g_DispPath ^= 1; // Toggle path display (XOR with true)
|
||||
}
|
||||
|
||||
|
||||
void setshowstring() {
|
||||
g_bShowString = true;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
36
engines/tinsel/pdisplay.h
Normal file
36
engines/tinsel/pdisplay.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Tag related methods
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_PDISPLAY_H // prevent multiple includes
|
||||
#define TINSEL_PDISPLAY_H
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
void EnableTags();
|
||||
void DisableTags();
|
||||
|
||||
void DisablePointing();
|
||||
void EnablePointing();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_PDISPLAY_H */
|
||||
65
engines/tinsel/pid.h
Normal file
65
engines/tinsel/pid.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* List of all process identifiers
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_PID_H // prevent multiple includes
|
||||
#define TINSEL_PID_H
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
#define PID_DESTROY 0x8000 // process id of any process that is to be destroyed between scenes
|
||||
|
||||
#define PID_EFFECTS (0x0010 | PID_DESTROY) // generic special effects process id
|
||||
#define PID_FADER (PID_EFFECTS + 4) // fader process
|
||||
|
||||
#define PID_MOUSE 0x0030 // mouse button checking process id
|
||||
|
||||
#define PID_KEYBOARD 0x0050 // keyboard scanning process
|
||||
|
||||
#define PID_CURSOR 0x0060 // cursor process
|
||||
|
||||
#define PID_SCROLL (0x0070 | PID_DESTROY) // scroll process
|
||||
|
||||
#define PID_INVENTORY 0x0080 // inventory process
|
||||
|
||||
#define PID_POSITION (0x0090 | PID_DESTROY) // cursor position process
|
||||
|
||||
#define PID_TAG (0x00A0 | PID_DESTROY) // tag process
|
||||
|
||||
#define PID_TCODE (0x00B0 | PID_DESTROY) // tinsel code process
|
||||
|
||||
#define PID_MASTER_SCR 0x00C0 // tinsel master script process
|
||||
|
||||
#define PID_MOVER (0x00D0 | PID_DESTROY) // moving actor process
|
||||
|
||||
#define PID_REEL (0x00E0 | PID_DESTROY) // process for each film reel
|
||||
|
||||
#define PID_BTN_CLICK 0x110 // process to handle mouse button clicks
|
||||
|
||||
#define PID_PROCESS (((TinselVersion == 3) ? 0x0100 : 0x0110) | PID_DESTROY) // Scene process base
|
||||
|
||||
#define PID_GPROCESS ((TinselVersion == 3) ? ( 0x110 | PID_DESTROY) : 0x0120) // Global process base
|
||||
|
||||
// distinction introduced by noir
|
||||
#define PID_SCENE ((TinselVersion == 3) ? (0x0001 | PID_TCODE) : PID_TCODE) // Root scene process
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_PID_H
|
||||
1186
engines/tinsel/play.cpp
Normal file
1186
engines/tinsel/play.cpp
Normal file
File diff suppressed because it is too large
Load Diff
59
engines/tinsel/play.h
Normal file
59
engines/tinsel/play.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 "common/coroutines.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
#define MAX_SOUNDREELS 10
|
||||
|
||||
struct SOUNDREELS {
|
||||
SCNHANDLE hFilm; // The 'film'
|
||||
int column; // Column number
|
||||
int actorCol;
|
||||
};
|
||||
|
||||
void PlayFilm(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, bool sfact, bool escOn, int myescEvent, bool bTop, OBJECT** playfield);
|
||||
|
||||
void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop, OBJECT** playfield);
|
||||
|
||||
void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact, bool escOn, int myescEvent, bool bTop, OBJECT** playfield);
|
||||
|
||||
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 SaveSoundReels(SOUNDREELS *psr);
|
||||
void RestoreSoundReels(SOUNDREELS *psr);
|
||||
|
||||
int ExtractActor(SCNHANDLE hFilm);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
2587
engines/tinsel/polygons.cpp
Normal file
2587
engines/tinsel/polygons.cpp
Normal file
File diff suppressed because it is too large
Load Diff
193
engines/tinsel/polygons.h
Normal file
193
engines/tinsel/polygons.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Definition of POLYGON structure and functions in POLYGONS.C
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_POLYGONS_H // prevent multiple includes
|
||||
#define TINSEL_POLYGONS_H
|
||||
|
||||
#include "tinsel/dw.h" // for SCNHANDLE
|
||||
#include "tinsel/scene.h" // for PPOLY and REEL
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
|
||||
// Polygon Types
|
||||
enum PTYPE {
|
||||
// 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,
|
||||
// Extra polygon types from Tinsel v3
|
||||
SCALE, EX_SCALE, SHAPE
|
||||
};
|
||||
|
||||
// subtype
|
||||
enum {
|
||||
NORMAL = 0,
|
||||
NODE = 1 // For paths
|
||||
};
|
||||
|
||||
// tagFlags
|
||||
enum {
|
||||
POINTING = 0x01,
|
||||
TAGWANTED = 0x02,
|
||||
FOLLOWCURSOR = 0x04
|
||||
};
|
||||
|
||||
// tagState
|
||||
enum TSTATE {
|
||||
TAG_OFF, TAG_ON
|
||||
};
|
||||
|
||||
// pointState
|
||||
enum PSTATE {
|
||||
PS_NO_POINT, PS_NOT_POINTING, PS_POINTING
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
NOPOLY = -1
|
||||
};
|
||||
|
||||
struct POLY_VOLATILE {
|
||||
bool bDead;
|
||||
short xoff, yoff; // Polygon offset
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
bool IsInPolygon(int xt, int yt, HPOLYGON p);
|
||||
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);
|
||||
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);
|
||||
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 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);
|
||||
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();
|
||||
HPOLYGON GetPolyHandle(int i);
|
||||
void InitPolygons(SCNHANDLE ph, int numPoly, bool bRestart);
|
||||
void DropPolygons();
|
||||
|
||||
|
||||
void SaveDeadPolys(bool *sdp);
|
||||
void RestoreDeadPolys(bool *sdp);
|
||||
void SavePolygonStuff(POLY_VOLATILE *sps);
|
||||
void RestorePolygonStuff(POLY_VOLATILE *sps);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
PTYPE PolyType(HPOLYGON hp); // ->type
|
||||
int PolySubtype(HPOLYGON hp); // ->subtype
|
||||
int PolyCenterX(HPOLYGON hp); // ->pcenterx
|
||||
int PolyCenterY(HPOLYGON hp); // ->pcentery
|
||||
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
|
||||
|
||||
void SetPolyPointState(HPOLYGON hp, PSTATE ps); // ->pointState
|
||||
void SetPolyTagState(HPOLYGON hp, TSTATE ts); // ->tagState
|
||||
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);
|
||||
void GetPolyMidBottom(HPOLYGON hp, int *pX, int *pY);
|
||||
int PathCount();
|
||||
void MovePolygon(PTYPE ptype, int id, int x, int y);
|
||||
void MovePolygonTo(PTYPE ptype, int id, int x, int y);
|
||||
|
||||
void NotebookPolyEntry(Common::Point c0, Common::Point c1, Common::Point c2, Common::Point c3);
|
||||
void NotebookPolyNextPage(Common::Point c0, Common::Point c1, Common::Point c2, Common::Point c3);
|
||||
void NotebookPolyPrevPage(Common::Point c0, Common::Point c1, Common::Point c2, Common::Point c3);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
void UpdateGroundPlane();
|
||||
|
||||
enum class NoteBookPoly {
|
||||
CLUE_0 = 0,
|
||||
CLUE_1 = 1,
|
||||
CLUE_2 = 2,
|
||||
CLUE_3 = 3,
|
||||
CLUE_4 = 4,
|
||||
CLUE_5 = 5,
|
||||
CLUE_6 = 6,
|
||||
CLUE_7 = 7,
|
||||
MAIN,
|
||||
NEXT,
|
||||
PREV,
|
||||
NONE
|
||||
};
|
||||
|
||||
class NoteBookPolygons {
|
||||
public:
|
||||
virtual ~NoteBookPolygons() {}
|
||||
virtual void setPolygon(NoteBookPoly polyKind, const Common::Point &c0, const Common::Point &c1, const Common::Point &c2, const Common::Point &c3) = 0;
|
||||
virtual void pushPolygon(const Common::Point &c0, const Common::Point &c1, const Common::Point &c2, const Common::Point &c3) = 0;
|
||||
|
||||
virtual NoteBookPoly mostSpecificHit(const Common::Point &point) const = 0;
|
||||
virtual int lineHit(const Common::Point &point) const = 0;
|
||||
virtual bool isInsideNotebook(const Common::Point &point) const = 0;
|
||||
};
|
||||
|
||||
NoteBookPolygons *instantiateNoteBookPolygons();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_POLYGONS_H */
|
||||
749
engines/tinsel/saveload.cpp
Normal file
749
engines/tinsel/saveload.cpp
Normal file
@@ -0,0 +1,749 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Save and restore scene and game.
|
||||
*/
|
||||
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/config.h"
|
||||
#include "tinsel/dialogs.h"
|
||||
#include "tinsel/drives.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/movers.h"
|
||||
#include "tinsel/savescn.h"
|
||||
#include "tinsel/timers.h"
|
||||
#include "tinsel/tinlib.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
#include "common/serializer.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
|
||||
/**
|
||||
* The current savegame format version.
|
||||
* Our save/load system uses an elaborate scheme to allow us to modify the
|
||||
* savegame while keeping full backward compatibility, in the sense that newer
|
||||
* ScummVM versions always are able to load old savegames.
|
||||
* In order to achieve that, we store a version in the savegame files, and whenever
|
||||
* the savegame layout is modified, the version is incremented.
|
||||
*
|
||||
* This roughly works by marking each savegame entry with a range of versions
|
||||
* for which it is valid; the save/load code iterates over all entries, but
|
||||
* only saves/loads those which are valid for the version of the savegame
|
||||
* which is being loaded/saved currently.
|
||||
*/
|
||||
#define CURRENT_VER 4
|
||||
|
||||
//----------------- EXTERN FUNCTIONS --------------------
|
||||
|
||||
// in DOS_DW.C
|
||||
extern void syncSCdata(Common::Serializer &s);
|
||||
|
||||
// in PCODE.C
|
||||
extern void syncGlobInfo(Common::Serializer &s);
|
||||
|
||||
// in POLYGONS.C
|
||||
extern void syncPolyInfo(Common::Serializer &s);
|
||||
|
||||
extern int g_sceneCtr;
|
||||
|
||||
extern bool g_ASceneIsSaved;
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
struct SaveGameHeader {
|
||||
uint32 id;
|
||||
uint32 size;
|
||||
uint32 ver;
|
||||
char desc[SG_DESC_LEN];
|
||||
TimeDate dateTime;
|
||||
uint32 playTime;
|
||||
bool scnFlag;
|
||||
byte language;
|
||||
uint16 numInterpreters; // Savegame version 2 or later only
|
||||
};
|
||||
|
||||
enum {
|
||||
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 + 4 + 1 + 1 + 2
|
||||
};
|
||||
|
||||
#define SAVEGAME_ID ((TinselVersion >= 2) ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
|
||||
|
||||
enum {
|
||||
// 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.
|
||||
FNAMELEN = 256 // 8.3
|
||||
};
|
||||
|
||||
struct SFILES {
|
||||
char name[FNAMELEN];
|
||||
char desc[SG_DESC_LEN + 2];
|
||||
TimeDate dateTime;
|
||||
};
|
||||
|
||||
//----------------- GLOBAL GLOBAL DATA --------------------
|
||||
|
||||
int g_thingHeld = 0;
|
||||
int g_restoreCD = 0;
|
||||
SRSTATE g_SRstate = SR_IDLE;
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
static int g_numSfiles = 0;
|
||||
static SFILES g_savedFiles[MAX_SAVED_FILES];
|
||||
|
||||
static bool g_NeedLoad = true;
|
||||
|
||||
static SAVED_DATA *g_srsd = nullptr;
|
||||
static int g_RestoreGameNumber = 0;
|
||||
static char *g_SaveSceneName = nullptr;
|
||||
static const char *g_SaveSceneDesc = nullptr;
|
||||
static int *g_SaveSceneSsCount = 0;
|
||||
static SAVED_DATA *g_SaveSceneSsData = nullptr; // points to 'SAVED_DATA ssdata[MAX_NEST]'
|
||||
|
||||
//------------- SAVE/LOAD SUPPORT METHODS ----------------
|
||||
|
||||
void ResetVarsSaveLoad() {
|
||||
g_thingHeld = 0;
|
||||
g_restoreCD = 0;
|
||||
g_SRstate = SR_IDLE;
|
||||
|
||||
g_numSfiles = 0;
|
||||
memset(g_savedFiles, 0, sizeof(g_savedFiles));
|
||||
|
||||
g_NeedLoad = true;
|
||||
|
||||
g_srsd = nullptr;
|
||||
g_RestoreGameNumber = 0;
|
||||
g_SaveSceneName = nullptr;
|
||||
g_SaveSceneDesc = nullptr;
|
||||
g_SaveSceneSsCount = 0;
|
||||
g_SaveSceneSsData = nullptr;
|
||||
}
|
||||
|
||||
void setNeedLoad() {
|
||||
g_NeedLoad = true;
|
||||
}
|
||||
|
||||
static void syncTime(Common::Serializer &s, TimeDate &t) {
|
||||
s.syncAsUint16LE(t.tm_year);
|
||||
s.syncAsByte(t.tm_mon);
|
||||
s.syncAsByte(t.tm_mday);
|
||||
s.syncAsByte(t.tm_hour);
|
||||
s.syncAsByte(t.tm_min);
|
||||
s.syncAsByte(t.tm_sec);
|
||||
}
|
||||
|
||||
static bool syncSaveGameHeader(Common::Serializer &s, SaveGameHeader &hdr) {
|
||||
s.syncAsUint32LE(hdr.id);
|
||||
s.syncAsUint32LE(hdr.size);
|
||||
s.syncAsUint32LE(hdr.ver);
|
||||
|
||||
s.syncBytes((byte *)hdr.desc, SG_DESC_LEN);
|
||||
hdr.desc[SG_DESC_LEN - 1] = 0;
|
||||
|
||||
syncTime(s, hdr.dateTime);
|
||||
|
||||
if (hdr.ver >= 3)
|
||||
s.syncAsUint32LE(hdr.playTime);
|
||||
else
|
||||
hdr.playTime = 0;
|
||||
|
||||
int tmp = hdr.size - s.bytesSynced();
|
||||
|
||||
// NOTE: We can't use SAVEGAME_ID here when attempting to remove a saved game from the launcher,
|
||||
// as there is no TinselEngine initialized then. This means that we can't check if this is a DW1
|
||||
// or DW2 savegame in this case, but it doesn't really matter, as the saved game is about to be
|
||||
// deleted anyway. Refer to bug #5819.
|
||||
bool correctID = _vm ? (hdr.id == SAVEGAME_ID) : (hdr.id == DW1_SAVEGAME_ID || hdr.id == DW2_SAVEGAME_ID);
|
||||
|
||||
// Perform sanity check
|
||||
if (tmp < 0 || !correctID || hdr.ver > CURRENT_VER || hdr.size > 1024)
|
||||
return false;
|
||||
|
||||
if (tmp > 0) {
|
||||
// If there's header space left, handling syncing the Scn flag and game language
|
||||
s.syncAsByte(hdr.scnFlag);
|
||||
s.syncAsByte(hdr.language);
|
||||
tmp -= 2;
|
||||
|
||||
if (_vm && s.isLoading()) {
|
||||
// If the engine is loaded, ensure the Scn/Gra usage is correct, and it's the correct language
|
||||
if ((hdr.scnFlag != ((_vm->getFeatures() & GF_SCNFILES) != 0)) ||
|
||||
(hdr.language != _vm->_config->_language))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the number of interpreter contexts that will be saved in the savegame
|
||||
if (tmp >= 2) {
|
||||
tmp -= 2;
|
||||
hdr.numInterpreters = NUM_INTERPRET;
|
||||
s.syncAsUint16LE(hdr.numInterpreters);
|
||||
} else {
|
||||
if(_vm) // See comment above about bug #5819
|
||||
hdr.numInterpreters = ((TinselVersion >= 2) ? 70 : 64) - 20;
|
||||
else
|
||||
hdr.numInterpreters = 50; // This value doesn't matter since the saved game is being deleted.
|
||||
}
|
||||
|
||||
// Skip over any extra bytes
|
||||
s.skip(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void syncSavedMover(Common::Serializer &s, SAVED_MOVER &sm) {
|
||||
int i, j;
|
||||
|
||||
s.syncAsUint32LE(sm.bActive);
|
||||
s.syncAsSint32LE(sm.actorID);
|
||||
s.syncAsSint32LE(sm.objX);
|
||||
s.syncAsSint32LE(sm.objY);
|
||||
s.syncAsUint32LE(sm.hLastfilm);
|
||||
|
||||
// Sync walk reels
|
||||
for (i = 0; i < TOTAL_SCALES; ++i)
|
||||
for (j = 0; j < 4; ++j)
|
||||
s.syncAsUint32LE(sm.walkReels[i][j]);
|
||||
|
||||
// Sync stand reels
|
||||
for (i = 0; i < TOTAL_SCALES; ++i)
|
||||
for (j = 0; j < 4; ++j)
|
||||
s.syncAsUint32LE(sm.standReels[i][j]);
|
||||
|
||||
// Sync talk reels
|
||||
for (i = 0; i < TOTAL_SCALES; ++i)
|
||||
for (j = 0; j < 4; ++j)
|
||||
s.syncAsUint32LE(sm.talkReels[i][j]);
|
||||
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
s.syncAsByte(sm.bHidden);
|
||||
|
||||
s.syncAsSint32LE(sm.brightness);
|
||||
s.syncAsSint32LE(sm.startColor);
|
||||
s.syncAsSint32LE(sm.paletteLength);
|
||||
}
|
||||
}
|
||||
|
||||
static void syncSavedActor(Common::Serializer &s, SAVED_ACTOR &sa) {
|
||||
s.syncAsUint16LE(sa.actorID);
|
||||
s.syncAsUint16LE(sa.zFactor);
|
||||
s.syncAsUint16LE(sa.bAlive);
|
||||
s.syncAsUint16LE(sa.bHidden);
|
||||
s.syncAsUint32LE(sa.presFilm);
|
||||
s.syncAsUint16LE(sa.presRnum);
|
||||
s.syncAsUint16LE(sa.presPlayX);
|
||||
s.syncAsUint16LE(sa.presPlayY);
|
||||
}
|
||||
|
||||
static void syncNoScrollB(Common::Serializer &s, NOSCROLLB &ns) {
|
||||
s.syncAsSint32LE(ns.ln);
|
||||
s.syncAsSint32LE(ns.c1);
|
||||
s.syncAsSint32LE(ns.c2);
|
||||
}
|
||||
|
||||
static void syncZPosition(Common::Serializer &s, Z_POSITIONS &zp) {
|
||||
s.syncAsSint16LE(zp.actor);
|
||||
s.syncAsSint16LE(zp.column);
|
||||
s.syncAsSint32LE(zp.z);
|
||||
}
|
||||
|
||||
static void syncPolyVolatile(Common::Serializer &s, POLY_VOLATILE &p) {
|
||||
s.syncAsByte(p.bDead);
|
||||
s.syncAsSint16LE(p.xoff);
|
||||
s.syncAsSint16LE(p.yoff);
|
||||
}
|
||||
|
||||
static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) {
|
||||
s.syncAsUint32LE(sr.hFilm);
|
||||
s.syncAsSint32LE(sr.column);
|
||||
s.syncAsSint32LE(sr.actorCol);
|
||||
}
|
||||
|
||||
static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp, int numSystemVars) {
|
||||
s.syncAsUint32LE(sd.SavedSceneHandle);
|
||||
s.syncAsUint32LE(sd.SavedBgroundHandle);
|
||||
for (int i = 0; i < MAX_MOVERS; ++i)
|
||||
syncSavedMover(s, sd.SavedMoverInfo[i]);
|
||||
for (int i = 0; i < MAX_SAVED_ACTORS; ++i)
|
||||
syncSavedActor(s, sd.SavedActorInfo[i]);
|
||||
|
||||
s.syncAsSint32LE(sd.NumSavedActors);
|
||||
s.syncAsSint32LE(sd.SavedLoffset);
|
||||
s.syncAsSint32LE(sd.SavedToffset);
|
||||
for (int i = 0; i < numInterp; ++i)
|
||||
sd.SavedICInfo[i].syncWithSerializer(s);
|
||||
for (int i = 0; i < MAX_POLY; ++i)
|
||||
s.syncAsUint32LE(sd.SavedDeadPolys[i]);
|
||||
s.syncAsUint32LE(sd.SavedControl);
|
||||
s.syncAsUint32LE(sd.SavedMidi);
|
||||
s.syncAsUint32LE(sd.SavedLoop);
|
||||
s.syncAsUint32LE(sd.SavedNoBlocking);
|
||||
|
||||
// SavedNoScrollData
|
||||
for (int i = 0; i < MAX_VNOSCROLL; ++i)
|
||||
syncNoScrollB(s, sd.SavedNoScrollData.NoVScroll[i]);
|
||||
for (int i = 0; i < MAX_HNOSCROLL; ++i)
|
||||
syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]);
|
||||
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV);
|
||||
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoH);
|
||||
|
||||
// Tinsel 2 fields
|
||||
if (TinselVersion >= 2) {
|
||||
// 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 < numSystemVars; ++i)
|
||||
s.syncAsSint32LE(sd.SavedSystemVars[i]);
|
||||
|
||||
int numReels = s.getVersion() >= 4 ? MAX_SOUNDREELS : 5;
|
||||
for (int i = 0; i < numReels; ++i)
|
||||
syncSoundReel(s, sd.SavedSoundReels[i]);
|
||||
// For old saves we zero the remaining sound reels
|
||||
if (s.isLoading() && numReels < MAX_SOUNDREELS) {
|
||||
const auto reelSize = sizeof sd.SavedSoundReels[0];
|
||||
memset(&sd.SavedSoundReels[numReels], 0, reelSize * (MAX_SOUNDREELS - numReels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two TimeDate structs to see which one was earlier.
|
||||
* Returns 0 if they are equal, a negative value if a is lower / first, and
|
||||
* a positive value if b is lower / first.
|
||||
*/
|
||||
static int cmpTimeDate(const TimeDate &a, const TimeDate &b) {
|
||||
int tmp;
|
||||
|
||||
#define CMP_ENTRY(x) tmp = a.x - b.x; if (tmp != 0) return tmp
|
||||
|
||||
CMP_ENTRY(tm_year);
|
||||
CMP_ENTRY(tm_mon);
|
||||
CMP_ENTRY(tm_mday);
|
||||
CMP_ENTRY(tm_hour);
|
||||
CMP_ENTRY(tm_min);
|
||||
CMP_ENTRY(tm_sec);
|
||||
|
||||
#undef CMP_ENTRY
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a list of all available saved game files.
|
||||
* Store the file details, ordered by time, in savedFiles[] and return
|
||||
* 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 (!g_NeedLoad)
|
||||
return g_numSfiles;
|
||||
|
||||
int i;
|
||||
|
||||
const Common::String pattern = target + ".???";
|
||||
Common::StringArray files = saveFileMan->listSavefiles(pattern);
|
||||
|
||||
g_numSfiles = 0;
|
||||
|
||||
for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) {
|
||||
if (g_numSfiles >= MAX_SAVED_FILES)
|
||||
break;
|
||||
|
||||
const Common::String &fname = *file;
|
||||
Common::InSaveFile *f = saveFileMan->openForLoading(fname);
|
||||
if (f == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to load save game header
|
||||
Common::Serializer s(f, 0);
|
||||
SaveGameHeader hdr;
|
||||
bool validHeader = syncSaveGameHeader(s, hdr);
|
||||
delete f;
|
||||
if (!validHeader) {
|
||||
continue; // Invalid header, or savegame too new -> skip it
|
||||
// TODO: In SCUMM, we still show an entry for the save, but with description
|
||||
// "incompatible version".
|
||||
}
|
||||
|
||||
i = g_numSfiles;
|
||||
#ifndef DISABLE_SAVEGAME_SORTING
|
||||
for (i = 0; i < g_numSfiles; i++) {
|
||||
if (cmpTimeDate(hdr.dateTime, g_savedFiles[i].dateTime) > 0) {
|
||||
Common::copy_backward(&g_savedFiles[i], &g_savedFiles[g_numSfiles], &g_savedFiles[g_numSfiles + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Common::strlcpy(g_savedFiles[i].name, fname.c_str(), FNAMELEN);
|
||||
Common::strlcpy(g_savedFiles[i].desc, hdr.desc, SG_DESC_LEN);
|
||||
g_savedFiles[i].dateTime = hdr.dateTime;
|
||||
|
||||
++g_numSfiles;
|
||||
}
|
||||
|
||||
// Next getList() needn't do its stuff again
|
||||
g_NeedLoad = false;
|
||||
|
||||
return g_numSfiles;
|
||||
}
|
||||
|
||||
int getList() {
|
||||
// No change since last call?
|
||||
// TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
|
||||
if (!g_NeedLoad)
|
||||
return g_numSfiles;
|
||||
|
||||
return getList(_vm->getSaveFileMan(), _vm->getTargetName());
|
||||
}
|
||||
|
||||
char *ListEntry(int i, letype which) {
|
||||
if (i == -1)
|
||||
i = g_numSfiles;
|
||||
|
||||
assert(i >= 0);
|
||||
|
||||
if (i < g_numSfiles)
|
||||
return which == LE_NAME ? g_savedFiles[i].name : g_savedFiles[i].desc;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool DoSync(Common::Serializer &s, int numInterp, int numSystemVars) {
|
||||
int sg = 0;
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
if (s.isSaving())
|
||||
g_restoreCD = GetCurrentCD();
|
||||
s.syncAsSint16LE(g_restoreCD);
|
||||
|
||||
if (s.isLoading())
|
||||
_vm->_dialogs->holdItem(INV_NOICON);
|
||||
}
|
||||
|
||||
syncSavedData(s, *g_srsd, numInterp, numSystemVars);
|
||||
syncGlobInfo(s); // Glitter globals
|
||||
_vm->_dialogs->syncInvInfo(s); // Inventory data
|
||||
|
||||
// Held object
|
||||
if (s.isSaving())
|
||||
sg = _vm->_dialogs->whichItemHeld();
|
||||
s.syncAsSint32LE(sg);
|
||||
if (s.isLoading()) {
|
||||
if (sg != -1 && !_vm->_dialogs->getIsInvObject(sg))
|
||||
// Not a valid inventory object, so return false
|
||||
return false;
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
g_thingHeld = sg;
|
||||
else
|
||||
_vm->_dialogs->holdItem(sg);
|
||||
}
|
||||
|
||||
syncTimerInfo(s); // Timer data
|
||||
if (TinselVersion <= 1)
|
||||
syncPolyInfo(s); // Dead polygon data
|
||||
syncSCdata(s); // Hook Scene and delayed scene
|
||||
|
||||
s.syncAsSint32LE(*g_SaveSceneSsCount);
|
||||
|
||||
if (*g_SaveSceneSsCount != 0) {
|
||||
SAVED_DATA *sdPtr = g_SaveSceneSsData;
|
||||
for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr)
|
||||
syncSavedData(s, *sdPtr, numInterp, numSystemVars);
|
||||
|
||||
// Flag that there is a saved scene to return to. Note that in this context 'saved scene'
|
||||
// is a stored scene to return to from another scene, such as from the Summoning Book close-up
|
||||
// in Discworld 1 to whatever scene Rincewind was in prior to that
|
||||
g_ASceneIsSaved = true;
|
||||
}
|
||||
|
||||
if (TinselVersion <= 1)
|
||||
_vm->_actor->syncAllActorsAlive(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* DoRestore
|
||||
*/
|
||||
static bool DoRestore() {
|
||||
Common::InSaveFile *f = _vm->getSaveFileMan()->openForLoading(g_savedFiles[g_RestoreGameNumber].name);
|
||||
|
||||
if (f == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Serializer s(f, 0);
|
||||
SaveGameHeader hdr;
|
||||
if (!syncSaveGameHeader(s, hdr)) {
|
||||
delete f; // Invalid header, or savegame too new -> skip it
|
||||
return false;
|
||||
}
|
||||
|
||||
s.setVersion(hdr.ver);
|
||||
if (hdr.ver >= 3)
|
||||
_vm->setTotalPlayTime(hdr.playTime);
|
||||
else
|
||||
_vm->setTotalPlayTime(0);
|
||||
|
||||
// Load in the data. For older savegame versions, we potentially need to load the data twice, once
|
||||
// for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames
|
||||
int numInterpreters = hdr.numInterpreters;
|
||||
int32 currentPos = f->pos();
|
||||
int numberOfTries = ((hdr.ver >= 2) ? 1 : 2);
|
||||
int numSystemVars = SV_TOPVALID; // Appropriate for both Noir and DW2
|
||||
for (int tryNumber = 0; tryNumber < numberOfTries; ++tryNumber) {
|
||||
if (tryNumber == 1) {
|
||||
f->seek(currentPos);
|
||||
// If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts
|
||||
if (hdr.ver < 2) {
|
||||
numInterpreters = 80;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the savegame data
|
||||
bool successfullSync = DoSync(s, numInterpreters, numSystemVars);
|
||||
|
||||
uint32 id = f->readSint32LE();
|
||||
|
||||
int remaining = f->size() - f->pos();
|
||||
// BUG #13897: Older savegames won't run on ScummVM 2.6.x
|
||||
// The reason being that the system vars for Noir were added increasing the SV_TOPVALID value,
|
||||
// which also affected DW2 unintentionally, creating some v3 savegames that had additional data
|
||||
// stored there. To properly load these savegames, we'll have to try again, with the knowledge
|
||||
// that we have to skip this data.
|
||||
if (hdr.id == DW2_SAVEGAME_ID && hdr.ver == 3 && remaining != 0) {
|
||||
if (tryNumber == 0) {
|
||||
numberOfTries++;
|
||||
// Make the second attempt read the Noir amount of sys-vars, so that the problematic
|
||||
// savegames can be loaded.
|
||||
numSystemVars = SV_TOPVALID_T3;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (successfullSync) {
|
||||
if (id != (uint32)0xFEEDFACE) {
|
||||
error("Incompatible saved game");
|
||||
}
|
||||
|
||||
// Data load was successful (or likely), so break out of loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int remainingBytes = f->size() - f->pos();
|
||||
if (remainingBytes != 0) {
|
||||
error("%d bytes of savegame not read", remainingBytes);
|
||||
}
|
||||
bool failed = (f->eos() || f->err());
|
||||
|
||||
delete f;
|
||||
|
||||
if (failed) {
|
||||
GUI::MessageDialog dialog(_("Failed to load saved game from file."));
|
||||
dialog.runModal();
|
||||
}
|
||||
|
||||
return !failed;
|
||||
}
|
||||
|
||||
static void SaveFailure(Common::OutSaveFile *f) {
|
||||
if (f) {
|
||||
delete f;
|
||||
_vm->getSaveFileMan()->removeSavefile(g_SaveSceneName);
|
||||
}
|
||||
g_SaveSceneName= nullptr; // Invalidate save name
|
||||
GUI::MessageDialog dialog(_("Failed to save game to file."));
|
||||
dialog.runModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* DoSave
|
||||
*/
|
||||
static void DoSave() {
|
||||
Common::OutSaveFile *f;
|
||||
char tmpName[FNAMELEN];
|
||||
|
||||
// Next getList() must do its stuff again
|
||||
g_NeedLoad = true;
|
||||
|
||||
if (g_SaveSceneName == NULL) {
|
||||
// Generate a new unique save name
|
||||
int i;
|
||||
int ano = 1; // Allocated number
|
||||
|
||||
while (1) {
|
||||
Common::String fname = _vm->getSavegameFilename(ano);
|
||||
Common::strlcpy(tmpName, fname.c_str(), FNAMELEN);
|
||||
|
||||
for (i = 0; i < g_numSfiles; i++)
|
||||
if (!strcmp(g_savedFiles[i].name, tmpName))
|
||||
break;
|
||||
|
||||
if (i == g_numSfiles)
|
||||
break;
|
||||
ano++;
|
||||
}
|
||||
|
||||
g_SaveSceneName = tmpName;
|
||||
}
|
||||
|
||||
|
||||
if (g_SaveSceneDesc[0] == 0)
|
||||
g_SaveSceneDesc = "unnamed";
|
||||
|
||||
f = _vm->getSaveFileMan()->openForSaving(g_SaveSceneName);
|
||||
Common::Serializer s(0, f);
|
||||
|
||||
if (f == NULL) {
|
||||
SaveFailure(f);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write out a savegame header
|
||||
SaveGameHeader hdr;
|
||||
hdr.id = SAVEGAME_ID;
|
||||
hdr.size = SAVEGAME_HEADER_SIZE;
|
||||
hdr.ver = CURRENT_VER;
|
||||
memset(hdr.desc, 0, SG_DESC_LEN);
|
||||
Common::strlcpy(hdr.desc, g_SaveSceneDesc, SG_DESC_LEN);
|
||||
g_system->getTimeAndDate(hdr.dateTime);
|
||||
hdr.playTime = _vm->getTotalPlayTime();
|
||||
hdr.scnFlag = _vm->getFeatures() & GF_SCNFILES;
|
||||
hdr.language = _vm->_config->_language;
|
||||
|
||||
if (!syncSaveGameHeader(s, hdr) || f->err()) {
|
||||
SaveFailure(f);
|
||||
return;
|
||||
}
|
||||
|
||||
s.setVersion(hdr.ver);
|
||||
DoSync(s, hdr.numInterpreters, SV_TOPVALID);
|
||||
|
||||
// Write out the special Id for Discworld savegames
|
||||
f->writeUint32LE(0xFEEDFACE);
|
||||
if (f->err()) {
|
||||
SaveFailure(f);
|
||||
return;
|
||||
}
|
||||
|
||||
f->finalize();
|
||||
delete f;
|
||||
g_SaveSceneName= nullptr; // Invalidate save name
|
||||
}
|
||||
|
||||
/**
|
||||
* ProcessSRQueue
|
||||
*/
|
||||
void ProcessSRQueue() {
|
||||
switch (g_SRstate) {
|
||||
case SR_DORESTORE:
|
||||
// If a load has been done directly from title screens, set a larger value for scene ctr so the
|
||||
// code used to skip the title screens in Discworld 1 gets properly disabled
|
||||
if (g_sceneCtr < 10)
|
||||
g_sceneCtr = 10;
|
||||
|
||||
if (DoRestore()) {
|
||||
DoRestoreScene(g_srsd, false);
|
||||
}
|
||||
g_SRstate = SR_IDLE;
|
||||
break;
|
||||
|
||||
case SR_DOSAVE:
|
||||
DoSave();
|
||||
g_SRstate = SR_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
|
||||
assert(g_SRstate == SR_IDLE);
|
||||
|
||||
g_SaveSceneName = name;
|
||||
g_SaveSceneDesc = desc;
|
||||
g_SaveSceneSsCount = pSsCount;
|
||||
g_SaveSceneSsData = pSsData;
|
||||
g_srsd = sd;
|
||||
g_SRstate = SR_DOSAVE;
|
||||
}
|
||||
|
||||
void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
|
||||
if (TinselVersion >= 2) {
|
||||
if (num == -1)
|
||||
return;
|
||||
else if (num == -2) {
|
||||
// From CD change for restore
|
||||
num = g_RestoreGameNumber;
|
||||
}
|
||||
}
|
||||
|
||||
assert(num >= 0);
|
||||
|
||||
g_RestoreGameNumber = num;
|
||||
g_SaveSceneSsCount = pSsCount;
|
||||
g_SaveSceneSsData = pSsData;
|
||||
g_srsd = sd;
|
||||
g_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() {
|
||||
int numFiles = getList();
|
||||
|
||||
return (numFiles == 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
485
engines/tinsel/savescn.cpp
Normal file
485
engines/tinsel/savescn.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Save and restore scene and game.
|
||||
*/
|
||||
|
||||
|
||||
#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/dialogs.h"
|
||||
#include "tinsel/music.h"
|
||||
#include "tinsel/pid.h"
|
||||
#include "tinsel/play.h"
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/movers.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"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- EXTERN FUNCTIONS --------------------
|
||||
|
||||
// In DOS_DW.C
|
||||
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 SCENE.C
|
||||
extern SCNHANDLE GetSceneHandle();
|
||||
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
enum {
|
||||
RS_COUNT = 5, // Restore scene count
|
||||
|
||||
MAX_NEST = 4
|
||||
};
|
||||
|
||||
//----------------- EXTERNAL GLOBAL DATA --------------------
|
||||
|
||||
extern int g_thingHeld;
|
||||
extern int g_restoreCD;
|
||||
extern SRSTATE g_SRstate;
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
bool g_ASceneIsSaved = false;
|
||||
|
||||
static int g_savedSceneCount = 0;
|
||||
|
||||
static bool g_bNotDoneYet = false;
|
||||
|
||||
static SAVED_DATA *g_ssData = nullptr;
|
||||
static SAVED_DATA g_sgData;
|
||||
static SAVED_DATA *g_rsd = nullptr;
|
||||
|
||||
static int g_RestoreSceneCount = 0;
|
||||
|
||||
static bool g_bNoFade = false;
|
||||
|
||||
void ResetVarsSaveScn() {
|
||||
g_ASceneIsSaved = false;
|
||||
|
||||
g_savedSceneCount = 0;
|
||||
|
||||
g_bNotDoneYet = false;
|
||||
|
||||
free(g_ssData);
|
||||
g_ssData = nullptr;
|
||||
|
||||
memset(&g_sgData, 0, sizeof(g_sgData));
|
||||
g_rsd = nullptr;
|
||||
|
||||
g_RestoreSceneCount = 0;
|
||||
g_bNoFade = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current scene.
|
||||
* @param sd Pointer to the scene data
|
||||
*/
|
||||
void DoSaveScene(SAVED_DATA *sd) {
|
||||
sd->SavedSceneHandle = GetSceneHandle();
|
||||
sd->SavedBgroundHandle = _vm->_bg->GetBgroundHandle();
|
||||
SaveMovers(sd->SavedMoverInfo);
|
||||
sd->NumSavedActors = _vm->_actor->SaveActors(sd->SavedActorInfo);
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
|
||||
SaveInterpretContexts(sd->SavedICInfo);
|
||||
sd->SavedControl = ControlIsOn();
|
||||
sd->SavedNoBlocking = GetNoBlocking();
|
||||
_vm->_scroll->GetNoScrollData(&sd->SavedNoScrollData);
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// Tinsel 2 specific data save
|
||||
_vm->_actor->SaveActorZ(sd->savedActorZ);
|
||||
_vm->_actor->SaveZpositions(sd->zPositions);
|
||||
SavePolygonStuff(sd->SavedPolygonStuff);
|
||||
_vm->_pcmMusic->getTunePlaying(sd->SavedTune, sizeof(sd->SavedTune));
|
||||
sd->bTinselDim = _vm->_pcmMusic->getMusicTinselDimmed();
|
||||
sd->SavedScrollFocus = _vm->_scroll->GetScrollFocus();
|
||||
SaveSysVars(sd->SavedSystemVars);
|
||||
SaveSoundReels(sd->SavedSoundReels);
|
||||
|
||||
} else {
|
||||
// Tinsel 1 specific data save
|
||||
SaveDeadPolys(sd->SavedDeadPolys);
|
||||
_vm->_music->CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
|
||||
}
|
||||
|
||||
g_ASceneIsSaved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate restoration of the saved scene.
|
||||
* @param sd Pointer to the scene data
|
||||
* @param bFadeOut Flag to perform a fade out
|
||||
*/
|
||||
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
|
||||
g_rsd = sd;
|
||||
|
||||
if (bFadeOut)
|
||||
g_RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
|
||||
else
|
||||
g_RestoreSceneCount = RS_COUNT; // Set restore scene count
|
||||
}
|
||||
|
||||
void InitializeSaveScenes() {
|
||||
if (g_ssData == NULL) {
|
||||
g_ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
|
||||
if (g_ssData == NULL) {
|
||||
error("Cannot allocate memory for scene changes");
|
||||
}
|
||||
} else {
|
||||
// Re-initialize - no scenes saved
|
||||
g_savedSceneCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeSaveScenes() {
|
||||
free(g_ssData);
|
||||
g_ssData = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all non-moving actors are playing the same reel as when
|
||||
* the scene was saved.
|
||||
* Also 'stand' all the moving actors at their saved positions.
|
||||
*/
|
||||
void sortActors(SAVED_DATA *sd) {
|
||||
assert(TinselVersion <= 1);
|
||||
for (int i = 0; i < sd->NumSavedActors; i++) {
|
||||
_vm->_actor->ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
|
||||
|
||||
// Should be playing the same reel.
|
||||
if (sd->SavedActorInfo[i].presFilm != 0) {
|
||||
if (!_vm->_actor->actorAlive(sd->SavedActorInfo[i].actorID))
|
||||
continue;
|
||||
|
||||
RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
|
||||
sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
|
||||
}
|
||||
}
|
||||
|
||||
RestoreAuxScales(sd->SavedMoverInfo);
|
||||
for (int i = 0; i < MAX_MOVERS; i++) {
|
||||
if (sd->SavedMoverInfo[i].bActive)
|
||||
Stand(Common::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(g_rsd->SavedMoverInfo);
|
||||
|
||||
for (_ctx->i = 0; _ctx->i < MAX_MOVERS; _ctx->i++) {
|
||||
if (g_rsd->SavedMoverInfo[_ctx->i].bActive) {
|
||||
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, g_rsd->SavedMoverInfo[_ctx->i].actorID,
|
||||
g_rsd->SavedMoverInfo[_ctx->i].objX, g_rsd->SavedMoverInfo[_ctx->i].objY,
|
||||
g_rsd->SavedMoverInfo[_ctx->i].hLastfilm));
|
||||
|
||||
if (g_rsd->SavedMoverInfo[_ctx->i].bHidden)
|
||||
HideMover(GetMover(g_rsd->SavedMoverInfo[_ctx->i].actorID));
|
||||
}
|
||||
|
||||
ActorPalette(g_rsd->SavedMoverInfo[_ctx->i].actorID,
|
||||
g_rsd->SavedMoverInfo[_ctx->i].startColor, g_rsd->SavedMoverInfo[_ctx->i].paletteLength);
|
||||
|
||||
if (g_rsd->SavedMoverInfo[_ctx->i].brightness != BOGUS_BRIGHTNESS)
|
||||
ActorBrightness(g_rsd->SavedMoverInfo[_ctx->i].actorID, g_rsd->SavedMoverInfo[_ctx->i].brightness);
|
||||
}
|
||||
|
||||
// Restore via actor
|
||||
SetSysVar(ISV_DIVERT_ACTOR, _ctx->viaActor);
|
||||
|
||||
g_bNotDoneYet = false;
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void ResumeInterprets() {
|
||||
// Master script only affected on restore game, not restore scene
|
||||
if (TinselVersion <= 1) {
|
||||
if (g_rsd == &g_sgData) {
|
||||
CoroScheduler.killMatchingProcess(PID_MASTER_SCR, -1);
|
||||
FreeMasterInterpretContext();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_INTERPRET; i++) {
|
||||
switch (g_rsd->SavedICInfo[i].GSort) {
|
||||
case GS_NONE:
|
||||
break;
|
||||
|
||||
case GS_INVENTORY:
|
||||
if (g_rsd->SavedICInfo[i].event != POINTED) {
|
||||
RestoreProcess(&g_rsd->SavedICInfo[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_MASTER:
|
||||
// Master script only affected on restore game, not restore scene
|
||||
if (g_rsd == &g_sgData)
|
||||
RestoreMasterProcess(&g_rsd->SavedICInfo[i]);
|
||||
break;
|
||||
|
||||
case GS_PROCESS:
|
||||
// Tinsel 2 process
|
||||
RestoreSceneProcess(&g_rsd->SavedICInfo[i]);
|
||||
break;
|
||||
|
||||
case GS_GPROCESS:
|
||||
// Tinsel 2 Global processes only affected on restore game, not restore scene
|
||||
if (g_rsd == &g_sgData)
|
||||
RestoreGlobalProcess(&g_rsd->SavedICInfo[i]);
|
||||
break;
|
||||
|
||||
case GS_ACTOR:
|
||||
if (TinselVersion >= 2)
|
||||
RestoreProcess(&g_rsd->SavedICInfo[i]);
|
||||
else
|
||||
RestoreActorProcess(g_rsd->SavedICInfo[i].idActor, &g_rsd->SavedICInfo[i], g_rsd == &g_sgData);
|
||||
break;
|
||||
|
||||
case GS_POLYGON:
|
||||
case GS_SCENE:
|
||||
RestoreProcess(&g_rsd->SavedICInfo[i]);
|
||||
break;
|
||||
|
||||
default:
|
||||
warning("Unhandled GSort in ResumeInterprets");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do restore scene
|
||||
* @param n Id
|
||||
*/
|
||||
static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
|
||||
switch (n) {
|
||||
case RS_COUNT + COUNTOUT_COUNT:
|
||||
// Trigger pre-load and fade and start countdown
|
||||
FadeOutFast();
|
||||
break;
|
||||
|
||||
case RS_COUNT:
|
||||
_vm->_sound->stopAllSamples();
|
||||
ClearScreen();
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
|
||||
// Master script only affected on restore game, not restore scene
|
||||
if (sd == &g_sgData) {
|
||||
CoroScheduler.killMatchingProcess(PID_MASTER_SCR);
|
||||
KillGlobalProcesses();
|
||||
FreeMasterInterpretContext();
|
||||
}
|
||||
|
||||
RestorePolygonStuff(sd->SavedPolygonStuff);
|
||||
|
||||
// Abandon temporarily if different CD
|
||||
if (sd == &g_sgData && g_restoreCD != GetCurrentCD()) {
|
||||
g_SRstate = SR_IDLE;
|
||||
|
||||
EndScene();
|
||||
SetNextCD(g_restoreCD);
|
||||
CDChangeForRestore(g_restoreCD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
RestoreDeadPolys(sd->SavedDeadPolys);
|
||||
}
|
||||
|
||||
// Start up the scene
|
||||
StartNewScene(sd->SavedSceneHandle, NO_ENTRY_NUM);
|
||||
|
||||
_vm->_bg->SetDoFadeIn(!g_bNoFade);
|
||||
g_bNoFade = false;
|
||||
_vm->_bg->StartupBackground(Common::nullContext, sd->SavedBgroundHandle);
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
|
||||
} else {
|
||||
_vm->_scroll->KillScroll();
|
||||
_vm->_bg->PlayfieldSetPos(FIELD_WORLD, sd->SavedLoffset, sd->SavedToffset);
|
||||
SetNoBlocking(sd->SavedNoBlocking);
|
||||
}
|
||||
|
||||
_vm->_scroll->RestoreNoScrollData(&sd->SavedNoScrollData);
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// create process to sort out the moving actors
|
||||
CoroScheduler.createProcess(PID_MOVER, SortMAProcess, NULL, 0);
|
||||
g_bNotDoneYet = true;
|
||||
|
||||
_vm->_actor->RestoreActorZ(sd->savedActorZ);
|
||||
_vm->_actor->RestoreZpositions(sd->zPositions);
|
||||
RestoreSysVars(sd->SavedSystemVars);
|
||||
_vm->_actor->RestoreActors(sd->NumSavedActors, sd->SavedActorInfo);
|
||||
RestoreSoundReels(sd->SavedSoundReels);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sortActors(sd);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (TinselVersion >= 2) {
|
||||
if (g_bNotDoneYet)
|
||||
return n;
|
||||
|
||||
if (sd == &g_sgData)
|
||||
_vm->_dialogs->holdItem(g_thingHeld, true);
|
||||
if (sd->bTinselDim)
|
||||
_vm->_pcmMusic->dim(true);
|
||||
_vm->_pcmMusic->restoreThatTune(sd->SavedTune);
|
||||
_vm->_scroll->ScrollFocus(sd->SavedScrollFocus);
|
||||
} else {
|
||||
_vm->_music->RestoreMidiFacts(sd->SavedMidi, sd->SavedLoop);
|
||||
}
|
||||
|
||||
if (sd->SavedControl)
|
||||
ControlOn(); // Control was on
|
||||
ResumeInterprets();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return n - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore game
|
||||
* @param num num
|
||||
*/
|
||||
void RestoreGame(int num) {
|
||||
_vm->_dialogs->killInventory();
|
||||
|
||||
RequestRestoreGame(num, &g_sgData, &g_savedSceneCount, g_ssData);
|
||||
|
||||
// Actual restoring is performed by ProcessSRQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* Save game
|
||||
* @param name Name of savegame
|
||||
* @param desc Description of savegame
|
||||
*/
|
||||
void SaveGame(char *name, char *desc) {
|
||||
// Get current scene data
|
||||
DoSaveScene(&g_sgData);
|
||||
|
||||
RequestSaveGame(name, desc, &g_sgData, &g_savedSceneCount, g_ssData);
|
||||
|
||||
// Actual saving is performed by ProcessSRQueue
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
bool IsRestoringScene() {
|
||||
if (g_RestoreSceneCount) {
|
||||
g_RestoreSceneCount = DoRestoreSceneFrame(g_rsd, g_RestoreSceneCount);
|
||||
}
|
||||
|
||||
return g_RestoreSceneCount ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores Scene
|
||||
*/
|
||||
void TinselRestoreScene(bool bFade) {
|
||||
// only called by restore_scene PCODE
|
||||
if (g_RestoreSceneCount == 0) {
|
||||
assert(g_savedSceneCount >= 1); // No saved scene to restore
|
||||
|
||||
if (g_ASceneIsSaved)
|
||||
DoRestoreScene(&g_ssData[--g_savedSceneCount], bFade);
|
||||
if (!bFade)
|
||||
g_bNoFade = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Please Save Scene
|
||||
*/
|
||||
void TinselSaveScene(CORO_PARAM) {
|
||||
// only called by save_scene PCODE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
assert(g_savedSceneCount < MAX_NEST); // nesting limit reached
|
||||
|
||||
// Don't save the same thing multiple times!
|
||||
// FIXME/TODO: Maybe this can be changed to an assert?
|
||||
if (g_savedSceneCount && g_ssData[g_savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
|
||||
CORO_KILL_SELF();
|
||||
|
||||
DoSaveScene(&g_ssData[g_savedSceneCount++]);
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
101
engines/tinsel/savescn.h
Normal file
101
engines/tinsel/savescn.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Should really be called "moving actors.h"
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SAVESCN_H
|
||||
#define TINSEL_SAVESCN_H
|
||||
|
||||
#include "tinsel/actors.h" // SAVED_ACTOR
|
||||
#include "tinsel/dw.h" // SCNHANDLE
|
||||
#include "tinsel/movers.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 {
|
||||
|
||||
#define SG_DESC_LEN 40 // Max. saved game description length
|
||||
#define MAX_SAVED_FILES 100
|
||||
|
||||
struct SAVED_DATA {
|
||||
SCNHANDLE SavedSceneHandle; // Scene handle
|
||||
SCNHANDLE SavedBgroundHandle; // Background handle
|
||||
SAVED_MOVER SavedMoverInfo[MAX_MOVERS]; // Moving actors
|
||||
SAVED_ACTOR SavedActorInfo[MAX_SAVED_ACTORS]; // } Actors
|
||||
int NumSavedActors; // }
|
||||
int SavedLoffset, SavedToffset; // Screen offsets
|
||||
INT_CONTEXT SavedICInfo[MAX_INTERPRET]; // Interpret contexts
|
||||
bool SavedDeadPolys[MAX_POLY];
|
||||
bool SavedControl;
|
||||
SCNHANDLE SavedMidi; // }
|
||||
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_T3];
|
||||
SOUNDREELS SavedSoundReels[MAX_SOUNDREELS];
|
||||
};
|
||||
|
||||
|
||||
enum SRSTATE {
|
||||
SR_IDLE, SR_DORESTORE, SR_DONERESTORE,
|
||||
SR_DOSAVE, SR_DONESAVE, SR_ABORTED
|
||||
};
|
||||
|
||||
void TinselRestoreScene(bool bFade);
|
||||
void TinselSaveScene(CORO_PARAM);
|
||||
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut);
|
||||
void DoSaveScene(SAVED_DATA *sd);
|
||||
|
||||
bool IsRestoringScene();
|
||||
|
||||
|
||||
enum letype{
|
||||
LE_NAME, LE_DESC
|
||||
};
|
||||
|
||||
char *ListEntry(int i, letype which);
|
||||
int getList();
|
||||
void setNeedLoad();
|
||||
|
||||
void RestoreGame(int num);
|
||||
void SaveGame(char *name, char *desc);
|
||||
|
||||
void ProcessSRQueue();
|
||||
|
||||
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 InitializeSaveScenes();
|
||||
void FreeSaveScenes();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_SAVESCN_H */
|
||||
581
engines/tinsel/scene.cpp
Normal file
581
engines/tinsel/scene.cpp
Normal file
@@ -0,0 +1,581 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Starts up new scenes.
|
||||
*/
|
||||
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/anim.h"
|
||||
#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/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/movers.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/scn.h"
|
||||
#include "tinsel/scroll.h"
|
||||
#include "tinsel/sound.h" // stopAllSamples()
|
||||
#include "tinsel/sysvar.h"
|
||||
#include "tinsel/token.h"
|
||||
#include "tinsel/noir/spriter.h"
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- EXTERNAL FUNCTIONS ---------------------
|
||||
|
||||
// in EFFECT.C
|
||||
extern void EffectPolyProcess(CORO_PARAM, const void *);
|
||||
|
||||
// in PDISPLAY.C
|
||||
#ifdef DEBUG
|
||||
extern void CursorPositionProcess(CORO_PARAM, const void *);
|
||||
#endif
|
||||
extern void TagProcess(CORO_PARAM, const void *);
|
||||
extern void PointProcess(CORO_PARAM, const void *);
|
||||
extern void EnableTags();
|
||||
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
/** scene structure - one per scene */
|
||||
struct SCENE_STRUC {
|
||||
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 - Tinsel 2 only
|
||||
SCNHANDLE hMusicSegment; // handle to music segments - Tinsel 2 only
|
||||
int32 numCameras;
|
||||
SCNHANDLE hCamera;
|
||||
int32 numLights;
|
||||
SCNHANDLE hLight;
|
||||
} 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;
|
||||
|
||||
/** camera strucutre, Noir only */
|
||||
struct CAMERA_STRUC {
|
||||
int32 sceneId;
|
||||
int32 rotX;
|
||||
int32 rotY;
|
||||
int32 rotZ;
|
||||
int32 posX;
|
||||
int32 posY;
|
||||
int32 posZ;
|
||||
int32 aperture;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct LIGHT_STRUC {
|
||||
int32 sceneId;
|
||||
int32 posX;
|
||||
int32 posY;
|
||||
int32 posZ;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool g_ShowPosition = false; // Set when showpos() has been called
|
||||
#endif
|
||||
|
||||
int g_sceneCtr = 0;
|
||||
static int g_initialMyEscape = 0;
|
||||
|
||||
static SCNHANDLE g_SceneHandle = 0; // Current scene handle - stored in case of Save_Scene()
|
||||
|
||||
SCENE_STRUC g_tempStruc;
|
||||
|
||||
static bool g_isViewSet = false;
|
||||
|
||||
struct TP_INIT {
|
||||
SCNHANDLE hTinselCode; // Code
|
||||
TINSEL_EVENT event; // Triggering event
|
||||
};
|
||||
|
||||
void ResetVarsScene() {
|
||||
g_sceneCtr = 0;
|
||||
g_initialMyEscape = 0;
|
||||
|
||||
g_SceneHandle = 0;
|
||||
g_isViewSet = false;
|
||||
|
||||
memset(&g_tempStruc, 0, sizeof(SCENE_STRUC));
|
||||
}
|
||||
|
||||
SCENE_STRUC* parseSceneV1(const byte *p) {
|
||||
g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32);
|
||||
|
||||
return &g_tempStruc;
|
||||
}
|
||||
|
||||
SCENE_STRUC* parseSceneV2(const byte *p) {
|
||||
g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hSceneDesc = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numProcess = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hProcess = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hMusicScript = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hMusicSegment = READ_UINT32(p); p += sizeof(uint32);
|
||||
|
||||
return &g_tempStruc;
|
||||
}
|
||||
|
||||
SCENE_STRUC* parseSceneV3(const byte *p) {
|
||||
g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hSceneDesc = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numCameras = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hCamera = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numLights = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hLight = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.numProcess = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hProcess = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hMusicScript = READ_UINT32(p); p += sizeof(uint32);
|
||||
g_tempStruc.hMusicSegment = READ_UINT32(p); p += sizeof(uint32);
|
||||
// scollfields scaling/speed - 4 ints
|
||||
warning("TODO: Complete scene loading logic for Noir");
|
||||
|
||||
return &g_tempStruc;
|
||||
}
|
||||
|
||||
const SCENE_STRUC *GetSceneStruc(const byte *pStruc) {
|
||||
memset(&g_tempStruc, 0, sizeof(SCENE_STRUC));
|
||||
|
||||
if (TinselVersion == 3) {
|
||||
return parseSceneV3(pStruc);
|
||||
} else if (TinselVersion == 2) {
|
||||
return parseSceneV2(pStruc);
|
||||
} else {
|
||||
return parseSceneV1(pStruc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Started up for scene script and entrance script.
|
||||
*/
|
||||
static void SceneTinselProcess(CORO_PARAM, const void *param) {
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
INT_CONTEXT *pic;
|
||||
const TP_INIT *pInit;
|
||||
int myEscape;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
// The following myEscape value setting is used for enabling title screen skipping in DW1
|
||||
if ((TinselVersion == 1) && (g_sceneCtr == 1)) g_initialMyEscape = GetEscEvents();
|
||||
// DW1 PSX, Saturn and Mac has its own scene skipping script code for scenes 2 and 3 (bug #6094).
|
||||
_ctx->myEscape = ((TinselVersion == 1) && (g_sceneCtr < ((TinselV1PSX || TinselV1Saturn || TinselV1Mac) ? 2 : 4))) ? g_initialMyEscape : 0;
|
||||
|
||||
// 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,
|
||||
FROM_32(_ctx->pInit->hTinselCode),
|
||||
(TinselVersion >= 2) ? _ctx->pInit->event : NOEVENT,
|
||||
NOPOLY, // No polygon
|
||||
0, // No actor
|
||||
NULL, // No object
|
||||
_ctx->myEscape);
|
||||
CORO_INVOKE_1(Interpret, _ctx->pic);
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up a SceneTinselProcess() running the scene
|
||||
* script for various events.
|
||||
*/
|
||||
void SendSceneTinselProcess(TINSEL_EVENT event) {
|
||||
SCENE_STRUC *ss;
|
||||
|
||||
if (g_SceneHandle != 0) {
|
||||
ss = (SCENE_STRUC *) FindChunk(g_SceneHandle, CHUNK_SCENE);
|
||||
|
||||
if (ss->hSceneScript) {
|
||||
TP_INIT init;
|
||||
|
||||
init.event = event;
|
||||
init.hTinselCode = ss->hSceneScript;
|
||||
|
||||
CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the SCENE_STRUC
|
||||
* Initialize polygons for the scene
|
||||
* Initialize the actors for this scene
|
||||
* Run the appropriate entrance code (if any)
|
||||
* Get the default refer type
|
||||
*/
|
||||
|
||||
static void LoadScene(SCNHANDLE scene, int entry) {
|
||||
uint32 i;
|
||||
TP_INIT init;
|
||||
const SCENE_STRUC *ss;
|
||||
const ENTRANCE_STRUC *es;
|
||||
|
||||
// Scene handle
|
||||
g_SceneHandle = scene; // Save scene handle in case of Save_Scene()
|
||||
_vm->_handle->LockMem(g_SceneHandle); // Make sure scene is loaded
|
||||
_vm->_handle->LockScene(g_SceneHandle); // Prevent current scene from being discarded
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// CdPlay() stuff
|
||||
byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM);
|
||||
assert(cptr);
|
||||
i = READ_32(cptr);
|
||||
assert(i < 512);
|
||||
cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME);
|
||||
assert(cptr);
|
||||
_vm->_handle->SetCdPlaySceneDetails((const char *)cptr);
|
||||
}
|
||||
|
||||
// Find scene structure
|
||||
ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
|
||||
assert(ss != NULL);
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
// Music stuff
|
||||
char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME);
|
||||
assert(cptr);
|
||||
_vm->_pcmMusic->setMusicSceneDetails(FROM_32(ss->hMusicScript), FROM_32(ss->hMusicSegment), cptr);
|
||||
}
|
||||
|
||||
if (entry == NO_ENTRY_NUM) {
|
||||
// Restoring scene
|
||||
|
||||
// Initialize all the polygons for this scene
|
||||
InitPolygons(FROM_32(ss->hPoly), FROM_32(ss->numPoly), true);
|
||||
|
||||
// Initialize the actors for this scene
|
||||
_vm->_actor->StartTaggedActors(FROM_32(ss->hTaggedActor), FROM_32(ss->numTaggedActor), false);
|
||||
|
||||
if (TinselVersion >= 2)
|
||||
// Returning from cutscene
|
||||
SendSceneTinselProcess(RESTORE);
|
||||
|
||||
} else {
|
||||
// Genuine new scene
|
||||
|
||||
// Initialize all the polygons for this scene
|
||||
InitPolygons(FROM_32(ss->hPoly), FROM_32(ss->numPoly), false);
|
||||
|
||||
// Initialize the actors for this scene
|
||||
_vm->_actor->StartTaggedActors(FROM_32(ss->hTaggedActor), FROM_32(ss->numTaggedActor), true);
|
||||
|
||||
// Run the appropriate entrance code (if any)
|
||||
es = (const ENTRANCE_STRUC *)_vm->_handle->LockMem(FROM_32(ss->hEntrance));
|
||||
for (i = 0; i < FROM_32(ss->numEntrance); i++) {
|
||||
if (FROM_32(es->eNumber) == (uint)entry) {
|
||||
if (es->hScript) {
|
||||
init.event = STARTUP;
|
||||
init.hTinselCode = es->hScript;
|
||||
|
||||
CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Move to next entrance
|
||||
if (TinselVersion >= 2)
|
||||
++es;
|
||||
else
|
||||
es = (const ENTRANCE_STRUC *)((const byte *)es + 8);
|
||||
|
||||
}
|
||||
|
||||
if (i == FROM_32(ss->numEntrance))
|
||||
error("Non-existent scene entry number");
|
||||
|
||||
if (ss->hSceneScript) {
|
||||
init.event = STARTUP;
|
||||
init.hTinselCode = ss->hSceneScript;
|
||||
|
||||
CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
|
||||
}
|
||||
}
|
||||
|
||||
// Default refer type
|
||||
SetDefaultRefer(FROM_32(ss->defRefer));
|
||||
|
||||
// Scene's processes
|
||||
SceneProcesses(FROM_32(ss->numProcess), FROM_32(ss->hProcess));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrap up the last scene.
|
||||
*/
|
||||
void EndScene() {
|
||||
if (g_SceneHandle != 0) {
|
||||
_vm->_handle->UnlockScene(g_SceneHandle);
|
||||
g_SceneHandle = 0;
|
||||
}
|
||||
|
||||
_vm->_dialogs->killInventory(); // Close down any open inventory
|
||||
|
||||
DropPolygons(); // No polygons
|
||||
_vm->_scroll->DropScroll(); // No no-scrolls
|
||||
_vm->_bg->DropBackground(); // No background
|
||||
DropMovers(); // No moving actors
|
||||
_vm->_cursor->DropCursor(); // No cursor
|
||||
_vm->_actor->DropActors(); // No actor reels running
|
||||
FreeAllTokens(); // No-one has tokens
|
||||
FreeMostInterpretContexts(); // Only master script still interpreting
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
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);
|
||||
_vm->_font->ResetFontHandles();
|
||||
NoSoundReels();
|
||||
}
|
||||
|
||||
_vm->_sound->stopAllSamples(); // Kill off any still-running sample
|
||||
//_vm->_mixer->stopAll();
|
||||
|
||||
// init the palette manager
|
||||
ResetPalAllocator();
|
||||
|
||||
// init the object manager
|
||||
KillAllObjects();
|
||||
|
||||
// kill all destructable process
|
||||
CoroScheduler.killMatchingProcess(PID_DESTROY, PID_DESTROY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the standard stuff for the next scene.
|
||||
*/
|
||||
|
||||
void PrimeScene() {
|
||||
SetNoBlocking(false);
|
||||
SetSysVar(SYS_SceneFxDimFactor, SysVar(SYS_DefaultFxDimFactor));
|
||||
|
||||
_vm->_cursor->RestartCursor(); // Restart the cursor
|
||||
if (TinselVersion <= 1)
|
||||
EnableTags(); // Next scene with tags enabled
|
||||
|
||||
CoroScheduler.createProcess(PID_SCROLL, ScrollProcess, NULL, 0);
|
||||
CoroScheduler.createProcess(PID_SCROLL, EffectPolyProcess, NULL, 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (g_ShowPosition)
|
||||
CoroScheduler.createProcess(PID_POSITION, CursorPositionProcess, NULL, 0);
|
||||
#endif
|
||||
|
||||
CoroScheduler.createProcess(PID_TAG, TagProcess, NULL, 0);
|
||||
CoroScheduler.createProcess(PID_TAG, PointProcess, NULL, 0);
|
||||
|
||||
// init the current background
|
||||
_vm->_bg->InitBackground();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap up the last scene and start up the next scene.
|
||||
*/
|
||||
|
||||
void StartNewScene(SCNHANDLE scene, int entry) {
|
||||
EndScene(); // Wrap up the last scene.
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
TouchMoverReels();
|
||||
|
||||
_vm->_handle->LockMem(scene); // Do CD change before PrimeScene
|
||||
}
|
||||
|
||||
PrimeScene(); // Start up the standard stuff for the next scene.
|
||||
|
||||
LoadScene(scene, entry);
|
||||
|
||||
g_isViewSet = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Sets the ShowPosition flag, causing the cursor position process to be
|
||||
* created in each scene.
|
||||
*/
|
||||
|
||||
void setshowpos() {
|
||||
g_ShowPosition = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return the current scene handle.
|
||||
*/
|
||||
|
||||
SCNHANDLE GetSceneHandle() {
|
||||
return g_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;
|
||||
|
||||
CoroScheduler.createProcess(PID_SCENE, SceneTinselProcess, &init, sizeof(init));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WrapScene
|
||||
*/
|
||||
void WrapScene() {
|
||||
SendSceneTinselProcess(CLOSEDOWN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set parameters for 3D rendering for Noir.
|
||||
*/
|
||||
void SetView(int sceneId, int scale) {
|
||||
if (sceneId == SysVar(SV_SPRITER_SCENE_ID)) {
|
||||
if (scale == SysVar(SV_SPRITER_SCALE)) {
|
||||
debug("Ignoring SetView()");
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug("SetView(%d, %d)", sceneId, scale);
|
||||
|
||||
SetSysVar(SV_SPRITER_SCALE, scale);
|
||||
SetSysVar(SV_USER3 ,0x28);
|
||||
|
||||
if (g_isViewSet) {
|
||||
//EndActors();
|
||||
} else {
|
||||
g_isViewSet = true;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
CAMERA_STRUC *pCamera = (CAMERA_STRUC *)_vm->_handle->LockMem(g_tempStruc.hCamera);
|
||||
for (i = 0; i < g_tempStruc.numCameras; ++i, ++pCamera) {
|
||||
if (sceneId == (int)FROM_32(pCamera->sceneId)) {
|
||||
_vm->_spriter->SetCamera(
|
||||
FROM_32(pCamera->rotX),
|
||||
FROM_32(pCamera->rotY),
|
||||
FROM_32(pCamera->rotZ),
|
||||
FROM_32(pCamera->posX * SysVar(SV_SPRITER_SCALE)),
|
||||
FROM_32(-pCamera->posY * SysVar(SV_SPRITER_SCALE)),
|
||||
FROM_32(-pCamera->posZ * SysVar(SV_SPRITER_SCALE)),
|
||||
FROM_32(pCamera->aperture)
|
||||
);
|
||||
SetSysVar(SV_SPRITER_SCENE_ID, sceneId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == g_tempStruc.numCameras) {
|
||||
// no suitable camera found
|
||||
return;
|
||||
}
|
||||
|
||||
LIGHT_STRUC *pLight = (LIGHT_STRUC *)_vm->_handle->LockMem(g_tempStruc.hLight);
|
||||
for (i = 0; i < g_tempStruc.numLights; ++i, ++pLight) {
|
||||
if (sceneId == (int)FROM_32(pLight->sceneId)) {
|
||||
// TODO: Load lights
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == g_tempStruc.numLights) {
|
||||
// if no lights are present use the default light
|
||||
}
|
||||
|
||||
// TODO: Update the ground plane
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
95
engines/tinsel/scene.h
Normal file
95
engines/tinsel/scene.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Scene parsing defines
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SCENE_H
|
||||
#define TINSEL_SCENE_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/events.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
enum {
|
||||
MAX_NODES = 32, ///< maximum nodes in a Node Path
|
||||
MAX_NOSCROLL = 16, ///< maximum number of NoScroll commands in a scene
|
||||
MAX_ENTRANCE = 25, ///< maximum number of entrances in a scene
|
||||
MAX_POLY = 256, ///< maximum number of polygons in a scene
|
||||
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_FILM
|
||||
};
|
||||
|
||||
/** different actor masks */
|
||||
enum MASK_TYPE{
|
||||
ACT_DEFAULT,
|
||||
ACT_MASK = -1,
|
||||
ACT_ALWAYS = -2
|
||||
};
|
||||
|
||||
/** different scales */
|
||||
enum SCALE {
|
||||
SCALE_DEFAULT, SCALE_LARGE, SCALE_MEDIUM, SCALE_SMALL,
|
||||
SCALE_COMPACT, SCALE_TINY,
|
||||
SCALE_AUX1, SCALE_AUX2, SCALE_AUX3,
|
||||
SCALE_AUX4, SCALE_AUX5
|
||||
};
|
||||
|
||||
/** different reels */
|
||||
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 (((TinselVersion >= 2) && !TinselV2Demo) ? 25 : 23)
|
||||
#define OFFSETMASK (((TinselVersion >= 2) && !TinselV2Demo) ? 0x01ffffffL : 0x007fffffL)
|
||||
#define HANDLEMASK (((TinselVersion >= 2) && !TinselV2Demo) ? 0xFE000000L : 0xFF800000L)
|
||||
|
||||
void DoHailScene(SCNHANDLE scene);
|
||||
|
||||
void WrapScene();
|
||||
|
||||
void StartNewScene(SCNHANDLE scene, int entry);
|
||||
|
||||
void EndScene();
|
||||
|
||||
void SendSceneTinselProcess(TINSEL_EVENT event);
|
||||
|
||||
void SetView(int id, int scale);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_SCENE_H
|
||||
325
engines/tinsel/sched.cpp
Normal file
325
engines/tinsel/sched.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Process scheduler.
|
||||
*/
|
||||
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/pcode.h"
|
||||
#include "tinsel/pid.h"
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/sched.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
static uint32 g_numSceneProcess = 0;
|
||||
static SCNHANDLE g_hSceneProcess = 0;
|
||||
|
||||
static uint32 g_numGlobalProcess = 0;
|
||||
static PROCESS_STRUC *g_pGlobalProcess = nullptr;
|
||||
|
||||
|
||||
/**************************************************************************\
|
||||
|*********** Stuff to do with scene and global processes ************|
|
||||
\**************************************************************************/
|
||||
|
||||
void ResetVarsSched() {
|
||||
g_numSceneProcess = 0;
|
||||
g_hSceneProcess = 0;
|
||||
|
||||
g_numGlobalProcess = 0;
|
||||
g_pGlobalProcess = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* The code for for restored scene processes.
|
||||
*/
|
||||
static void RestoredProcessProcess(CORO_PARAM, const void *param) {
|
||||
CORO_BEGIN_CONTEXT;
|
||||
INT_CONTEXT *pic;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
// get the stuff copied to process when it was created
|
||||
// FIXME: Code without typedef emits -Wcast-qual GCC warning.
|
||||
// However, adding const casts break compilation with -fpermissive.
|
||||
// Reverted to local typedef for now until this can be avoided.
|
||||
#if 0
|
||||
_ctx->pic = *(INT_CONTEXT **)param;
|
||||
//_ctx->pic = *(const INT_CONTEXT **)param;
|
||||
#else
|
||||
typedef INT_CONTEXT *PINT_CONTEXT;
|
||||
_ctx->pic = *(const PINT_CONTEXT *)param;
|
||||
#endif
|
||||
|
||||
_ctx->pic = RestoreInterpretContext(_ctx->pic);
|
||||
AttachInterpret(_ctx->pic, CoroScheduler.getCurrentProcess());
|
||||
|
||||
CORO_INVOKE_1(Interpret, _ctx->pic);
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Tinsel Process
|
||||
*/
|
||||
static void ProcessTinselProcess(CORO_PARAM, const void *param) {
|
||||
// FIXME: Code without typedef emits -Wcast-qual GCC warning.
|
||||
// However, adding const casts break compilation with -fpermissive.
|
||||
// Reverted to local typedef for now until this can be avoided.
|
||||
#if 0
|
||||
//INT_CONTEXT **pPic = (INT_CONTEXT **)param;
|
||||
const INT_CONTEXT **pPic = (const INT_CONTEXT **)param;
|
||||
#else
|
||||
typedef INT_CONTEXT *PINT_CONTEXT;
|
||||
const PINT_CONTEXT *pPic = (const PINT_CONTEXT *)param;
|
||||
#endif
|
||||
|
||||
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) {
|
||||
const PROCESS_STRUC *processes = _vm->_handle->GetProcessData(g_hSceneProcess, g_numSceneProcess);
|
||||
|
||||
for (uint32 i = 0; i < g_numSceneProcess; i++) {
|
||||
if (processes[i].hProcessCode == pic->hCode) {
|
||||
CoroScheduler.createProcess(PID_PROCESS + i, RestoredProcessProcess,
|
||||
&pic, sizeof(pic));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] processes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a scene process with the given event.
|
||||
*/
|
||||
void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape,
|
||||
bool *result) {
|
||||
if (result) *result = false;
|
||||
|
||||
CORO_BEGIN_CONTEXT;
|
||||
const PROCESS_STRUC *processes;
|
||||
Common::PPROCESS pProc;
|
||||
INT_CONTEXT *pic;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
_ctx->processes = _vm->_handle->GetProcessData(g_hSceneProcess, g_numSceneProcess);
|
||||
for (uint32 i = 0; i < g_numSceneProcess; i++) {
|
||||
if (_ctx->processes[i].processId == procID) {
|
||||
assert(_ctx->processes[i].hProcessCode); // Must have some code to run
|
||||
|
||||
_ctx->pic = InitInterpretContext(GS_PROCESS,
|
||||
_ctx->processes[i].hProcessCode,
|
||||
event,
|
||||
NOPOLY, // No polygon
|
||||
0, // No actor
|
||||
nullptr, // No object
|
||||
myEscape
|
||||
);
|
||||
|
||||
if (_ctx->pic) {
|
||||
_ctx->pProc = CoroScheduler.createProcess(
|
||||
PID_PROCESS + i,
|
||||
ProcessTinselProcess,
|
||||
&_ctx->pic,
|
||||
sizeof(_ctx->pic));
|
||||
AttachInterpret(_ctx->pic, _ctx->pProc);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bWait && _ctx->pProc)
|
||||
CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
|
||||
|
||||
delete[] _ctx->processes;
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill all instances of a scene process.
|
||||
*/
|
||||
void KillSceneProcess(uint32 procID) {
|
||||
const PROCESS_STRUC *processes = _vm->_handle->GetProcessData(g_hSceneProcess, g_numSceneProcess);
|
||||
|
||||
for (uint32 i = 0; i < g_numSceneProcess; i++) {
|
||||
if (processes[i].processId == procID) {
|
||||
CoroScheduler.killMatchingProcess(PID_PROCESS + i, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] processes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the scene processes in a scene.
|
||||
*/
|
||||
void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess) {
|
||||
g_numSceneProcess = numProcess;
|
||||
g_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 < g_numGlobalProcess; i++) {
|
||||
if (g_pGlobalProcess[i].hProcessCode == pic->hCode) {
|
||||
CoroScheduler.createProcess(PID_GPROCESS + i, RestoredProcessProcess,
|
||||
&pic, sizeof(pic));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < g_numGlobalProcess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill them all (restore game).
|
||||
*/
|
||||
void KillGlobalProcesses() {
|
||||
|
||||
for (uint32 i = 0; i < g_numGlobalProcess; ++i) {
|
||||
CoroScheduler.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;
|
||||
INT_CONTEXT *pic;
|
||||
Common::PPROCESS pProc;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
bool result = false;
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
uint32 i; // Loop counter
|
||||
_ctx->pProc= nullptr;
|
||||
|
||||
for (i = 0; i < g_numGlobalProcess; ++i) {
|
||||
if (g_pGlobalProcess[i].processId == procID) {
|
||||
assert(g_pGlobalProcess[i].hProcessCode); // Must have some code to run
|
||||
|
||||
_ctx->pic = InitInterpretContext(GS_GPROCESS,
|
||||
g_pGlobalProcess[i].hProcessCode,
|
||||
event,
|
||||
NOPOLY, // No polygon
|
||||
0, // No actor
|
||||
NULL, // No object
|
||||
myEscape);
|
||||
|
||||
if (_ctx->pic != NULL) {
|
||||
|
||||
_ctx->pProc = CoroScheduler.createProcess(PID_GPROCESS + i, ProcessTinselProcess,
|
||||
&_ctx->pic, sizeof(_ctx->pic));
|
||||
AttachInterpret(_ctx->pic, _ctx->pProc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((i == g_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 < g_numGlobalProcess; ++i) {
|
||||
if (g_pGlobalProcess[i].processId == procID) {
|
||||
CoroScheduler.killMatchingProcess(PID_GPROCESS + i, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the global processes list
|
||||
*/
|
||||
void GlobalProcesses(uint32 numProcess, byte *pProcess) {
|
||||
g_pGlobalProcess = new PROCESS_STRUC[numProcess];
|
||||
g_numGlobalProcess = numProcess;
|
||||
byte *p = pProcess;
|
||||
|
||||
for (uint i = 0; i < numProcess; ++i, p += 8) {
|
||||
g_pGlobalProcess[i].processId = READ_32(p);
|
||||
g_pGlobalProcess[i].hProcessCode = READ_32(p + 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the global processes list
|
||||
*/
|
||||
void FreeGlobalProcesses() {
|
||||
delete[] g_pGlobalProcess;
|
||||
g_pGlobalProcess = 0;
|
||||
g_numGlobalProcess = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
57
engines/tinsel/sched.h
Normal file
57
engines/tinsel/sched.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Data structures used by the process scheduler
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SCHED_H // prevent multiple includes
|
||||
#define TINSEL_SCHED_H
|
||||
|
||||
#include "common/coroutines.h"
|
||||
#include "tinsel/dw.h" // new data types
|
||||
#include "tinsel/events.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
struct INT_CONTEXT;
|
||||
|
||||
struct PROCESS_STRUC {
|
||||
uint32 processId; // ID of process
|
||||
SCNHANDLE hProcessCode; // handle to actor script
|
||||
};
|
||||
|
||||
//----------------- FUNCTION PROTOTYPES --------------------
|
||||
|
||||
void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess);
|
||||
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 xKillGlobalProcess(uint32 procID);
|
||||
bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape);
|
||||
void RestoreGlobalProcess(INT_CONTEXT *pic);
|
||||
void KillGlobalProcesses();
|
||||
void FreeGlobalProcesses();
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_SCHED_H
|
||||
71
engines/tinsel/scn.cpp
Normal file
71
engines/tinsel/scn.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* A (some would say very) small collection of utility functions.
|
||||
*/
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/film.h"
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/multiobj.h"
|
||||
#include "tinsel/scn.h"
|
||||
#include "tinsel/tinsel.h" // for _vm
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
/**
|
||||
* Given a scene handle and a chunk id, gets the scene in RAM and
|
||||
* locates the requested chunk.
|
||||
* @param handle Scene handle
|
||||
* @param chunk Chunk Id
|
||||
*/
|
||||
byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
|
||||
byte *bptr = _vm->_handle->LockMem(handle);
|
||||
uint32 *lptr = (uint32 *)bptr;
|
||||
uint32 add;
|
||||
|
||||
// Initial adjustment for Tinsel 1 chunk types
|
||||
if ((TinselVersion <= 1) && (chunk >= CHUNK_SCENE) &&
|
||||
(chunk != CHUNK_MBSTRING))
|
||||
--chunk;
|
||||
|
||||
// V0 chunk types can be found by subtracting 2 from the
|
||||
// chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are
|
||||
// the same in V0 and V1
|
||||
if (TinselVersion == 0 &&
|
||||
chunk != CHUNK_STRING && chunk != CHUNK_BITMAP)
|
||||
chunk -= 0x2L;
|
||||
|
||||
while (1) {
|
||||
if (READ_32(lptr) == chunk)
|
||||
return (byte *)(lptr + 2);
|
||||
|
||||
++lptr;
|
||||
add = READ_32(lptr);
|
||||
|
||||
if (!add)
|
||||
// End of file reached
|
||||
return NULL;
|
||||
|
||||
// Move to next chunk
|
||||
lptr = (uint32 *)(bptr + add);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
85
engines/tinsel/scn.h
Normal file
85
engines/tinsel/scn.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SCN_H // prevent multiple includes
|
||||
#define TINSEL_SCN_H
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// chunk identifier numbers
|
||||
|
||||
// V2 chunks
|
||||
|
||||
#define CHUNK_STRING 0x33340001L // same in V1 and V2
|
||||
#define CHUNK_BITMAP 0x33340002L // same in V1 and V2
|
||||
#define CHUNK_CHARPTR 0x33340003L // not used!
|
||||
#define CHUNK_CHARMATRIX 0x33340004L // not used!
|
||||
#define CHUNK_PALETTE 0x33340005L // not used!
|
||||
#define CHUNK_IMAGE 0x33340006L // not used!
|
||||
#define CHUNK_ANI_FRAME 0x33340007L // not used!
|
||||
#define CHUNK_FILM 0x33340008L // not used!
|
||||
#define CHUNK_FONT 0x33340009L // not used!
|
||||
#define CHUNK_PCODE 0x3334000AL
|
||||
#define CHUNK_ENTRANCE 0x3334000BL // not used!
|
||||
#define CHUNK_POLYGONS 0x3334000CL // not used!
|
||||
#define CHUNK_ACTORS 0x3334000DL // not used!
|
||||
|
||||
#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
|
||||
|
||||
// Used in Discworld Noir
|
||||
#define CHUNK_GAME 0x33340031L
|
||||
|
||||
// This is a base, subsequent numbers may also get used
|
||||
#define CHUNK_GRAB_NAME 0x33340100L
|
||||
|
||||
byte *FindChunk(SCNHANDLE handle, uint32 chunk);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_SCN_H */
|
||||
542
engines/tinsel/scroll.cpp
Normal file
542
engines/tinsel/scroll.cpp
Normal file
@@ -0,0 +1,542 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Handles scrolling
|
||||
*/
|
||||
|
||||
#include "tinsel/actors.h"
|
||||
#include "tinsel/background.h"
|
||||
#include "tinsel/cursor.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/graphics.h"
|
||||
#include "tinsel/polygons.h"
|
||||
#include "tinsel/movers.h"
|
||||
#include "tinsel/scroll.h"
|
||||
#include "tinsel/sched.h"
|
||||
#include "tinsel/sysvar.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
//----------------- LOCAL DEFINES --------------------
|
||||
|
||||
#define LEFT 'L'
|
||||
#define RIGHT 'R'
|
||||
#define UP 'U'
|
||||
#define DOWN 'D'
|
||||
|
||||
#define SCROLLPIXELS 8 // Number of pixels to scroll per iteration
|
||||
|
||||
// Distance from edge that triggers a scroll
|
||||
#define RLDISTANCE ((TinselVersion >= 2) ? _scrollData.xTrigger : 50)
|
||||
#define UDISTANCE ((TinselVersion >= 2) ? _scrollData.yTriggerTop : 20)
|
||||
#define DDISTANCE ((TinselVersion >= 2) ? _scrollData.yTriggerBottom : 20)
|
||||
|
||||
// Number of iterations to make
|
||||
#define RLSCROLL 160 // 20*8 = 160 = half a screen
|
||||
#define UDSCROLL 100 // 12.5*8 = 100 = half a screen
|
||||
|
||||
Scroll::Scroll() {
|
||||
_leftScroll = 0;
|
||||
_downScroll = 0;
|
||||
_scrollActor = 0;
|
||||
_pScrollMover = nullptr;
|
||||
_oldx = 0;
|
||||
_oldy = 0;
|
||||
|
||||
_scrollData = {
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {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};
|
||||
|
||||
_imageH = 0;
|
||||
_imageW = 0;
|
||||
|
||||
_scrollCursor = 0;
|
||||
|
||||
_scrollPixelsX = SCROLLPIXELS;
|
||||
_scrollPixelsY = SCROLLPIXELS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the ScrollCursor flag
|
||||
*/
|
||||
void Scroll::DontScrollCursor() {
|
||||
_scrollCursor = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ScrollCursor flag
|
||||
*/
|
||||
void Scroll::DoScrollCursor() {
|
||||
_scrollCursor = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a no-scroll boundary for a scene.
|
||||
*/
|
||||
void Scroll::SetNoScroll(int x1, int y1, int x2, int y2) {
|
||||
if (x1 == x2) {
|
||||
/* Vertical line */
|
||||
assert(_scrollData.NumNoH < MAX_HNOSCROLL);
|
||||
|
||||
_scrollData.NoHScroll[_scrollData.NumNoH].ln = x1; // X pos of vertical line
|
||||
_scrollData.NoHScroll[_scrollData.NumNoH].c1 = y1;
|
||||
_scrollData.NoHScroll[_scrollData.NumNoH].c2 = y2;
|
||||
_scrollData.NumNoH++;
|
||||
} else if (y1 == y2) {
|
||||
/* Horizontal line */
|
||||
assert(_scrollData.NumNoV < MAX_VNOSCROLL);
|
||||
|
||||
_scrollData.NoVScroll[_scrollData.NumNoV].ln = y1; // Y pos of horizontal line
|
||||
_scrollData.NoVScroll[_scrollData.NumNoV].c1 = x1;
|
||||
_scrollData.NoVScroll[_scrollData.NumNoV].c2 = x2;
|
||||
_scrollData.NumNoV++;
|
||||
} else {
|
||||
/* No-scroll lines must be horizontal or vertical */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void Scroll::NeedScroll(int direction) {
|
||||
uint i;
|
||||
int BottomLine, RightCol;
|
||||
int Loffset, Toffset;
|
||||
|
||||
// get background offsets
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
|
||||
switch (direction) {
|
||||
case LEFT: /* Picture will go left, 'camera' right */
|
||||
|
||||
BottomLine = Toffset + (SCREEN_HEIGHT - 1);
|
||||
RightCol = Loffset + (SCREEN_WIDTH - 1);
|
||||
|
||||
for (i = 0; i < _scrollData.NumNoH; i++) {
|
||||
if (RightCol >= _scrollData.NoHScroll[i].ln - 1 && RightCol <= _scrollData.NoHScroll[i].ln + 1 &&
|
||||
((_scrollData.NoHScroll[i].c1 >= Toffset && _scrollData.NoHScroll[i].c1 <= BottomLine) ||
|
||||
(_scrollData.NoHScroll[i].c2 >= Toffset && _scrollData.NoHScroll[i].c2 <= BottomLine) ||
|
||||
(_scrollData.NoHScroll[i].c1 < Toffset && _scrollData.NoHScroll[i].c2 > BottomLine)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (_leftScroll <= 0) {
|
||||
if (TinselVersion >= 2) {
|
||||
_scrollPixelsX = _scrollData.xSpeed;
|
||||
_leftScroll += _scrollData.xDistance;
|
||||
} else {
|
||||
_scrollPixelsX = SCROLLPIXELS;
|
||||
_leftScroll = RLSCROLL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RIGHT: /* Picture will go right, 'camera' left */
|
||||
|
||||
BottomLine = Toffset + (SCREEN_HEIGHT - 1);
|
||||
|
||||
for (i = 0; i < _scrollData.NumNoH; i++) {
|
||||
if (Loffset >= _scrollData.NoHScroll[i].ln - 1 && Loffset <= _scrollData.NoHScroll[i].ln + 1 &&
|
||||
((_scrollData.NoHScroll[i].c1 >= Toffset && _scrollData.NoHScroll[i].c1 <= BottomLine) ||
|
||||
(_scrollData.NoHScroll[i].c2 >= Toffset && _scrollData.NoHScroll[i].c2 <= BottomLine) ||
|
||||
(_scrollData.NoHScroll[i].c1 < Toffset && _scrollData.NoHScroll[i].c2 > BottomLine)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (_leftScroll >= 0) {
|
||||
if (TinselVersion >= 2) {
|
||||
_scrollPixelsX = _scrollData.xSpeed;
|
||||
_leftScroll -= _scrollData.xDistance;
|
||||
} else {
|
||||
_scrollPixelsX = SCROLLPIXELS;
|
||||
_leftScroll = -RLSCROLL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UP: /* Picture will go upwards, 'camera' downwards */
|
||||
|
||||
BottomLine = Toffset + (SCREEN_HEIGHT - 1);
|
||||
RightCol = Loffset + (SCREEN_WIDTH - 1);
|
||||
|
||||
for (i = 0; i < _scrollData.NumNoV; i++) {
|
||||
if ((BottomLine >= _scrollData.NoVScroll[i].ln - 1 && BottomLine <= _scrollData.NoVScroll[i].ln + 1) &&
|
||||
((_scrollData.NoVScroll[i].c1 >= Loffset && _scrollData.NoVScroll[i].c1 <= RightCol) ||
|
||||
(_scrollData.NoVScroll[i].c2 >= Loffset && _scrollData.NoVScroll[i].c2 <= RightCol) ||
|
||||
(_scrollData.NoVScroll[i].c1 < Loffset && _scrollData.NoVScroll[i].c2 > RightCol)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (_downScroll <= 0) {
|
||||
if (TinselVersion >= 2) {
|
||||
_scrollPixelsY = _scrollData.ySpeed;
|
||||
_downScroll += _scrollData.yDistance;
|
||||
} else {
|
||||
_scrollPixelsY = SCROLLPIXELS;
|
||||
_downScroll = UDSCROLL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DOWN: /* Picture will go downwards, 'camera' upwards */
|
||||
|
||||
RightCol = Loffset + (SCREEN_WIDTH - 1);
|
||||
|
||||
for (i = 0; i < _scrollData.NumNoV; i++) {
|
||||
if (Toffset >= _scrollData.NoVScroll[i].ln - 1 && Toffset <= _scrollData.NoVScroll[i].ln + 1 &&
|
||||
((_scrollData.NoVScroll[i].c1 >= Loffset && _scrollData.NoVScroll[i].c1 <= RightCol) ||
|
||||
(_scrollData.NoVScroll[i].c2 >= Loffset && _scrollData.NoVScroll[i].c2 <= RightCol) ||
|
||||
(_scrollData.NoVScroll[i].c1 < Loffset && _scrollData.NoVScroll[i].c2 > RightCol)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (_downScroll >= 0) {
|
||||
if (TinselVersion >= 2) {
|
||||
_scrollPixelsY = _scrollData.ySpeed;
|
||||
_downScroll -= _scrollData.yDistance;
|
||||
} else {
|
||||
_scrollPixelsY = SCROLLPIXELS;
|
||||
_downScroll = -UDSCROLL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from scroll process - Scrolls the image as appropriate.
|
||||
*/
|
||||
void Scroll::ScrollImage() {
|
||||
int OldLoffset = 0, OldToffset = 0; // Used when keeping cursor on a tag
|
||||
int Loffset, Toffset;
|
||||
int curX, curY;
|
||||
|
||||
if (!_leftScroll && !_downScroll) // Only scroll if required
|
||||
return;
|
||||
|
||||
// get background offsets
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
|
||||
/*
|
||||
* Keeping cursor on a tag?
|
||||
*/
|
||||
if (_scrollCursor) {
|
||||
_vm->_cursor->GetCursorXYNoWait(&curX, &curY, true);
|
||||
if (InPolygon(curX, curY, TAG) != NOPOLY || InPolygon(curX, curY, EXIT) != NOPOLY) {
|
||||
OldLoffset = Loffset;
|
||||
OldToffset = Toffset;
|
||||
} else
|
||||
_scrollCursor = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Horizontal scrolling
|
||||
*/
|
||||
if (_leftScroll > 0) {
|
||||
_leftScroll -= _scrollPixelsX;
|
||||
if (_leftScroll < 0) {
|
||||
Loffset += _leftScroll;
|
||||
_leftScroll = 0;
|
||||
}
|
||||
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 ((TinselVersion >= 2) && SysVar(SV_MaximumXoffset) && (Loffset > SysVar(SV_MaximumXoffset)))
|
||||
Loffset = SysVar(SV_MaximumXoffset);
|
||||
|
||||
} else if (_leftScroll < 0) {
|
||||
_leftScroll += _scrollPixelsX;
|
||||
if (_leftScroll > 0) {
|
||||
Loffset += _leftScroll;
|
||||
_leftScroll = 0;
|
||||
}
|
||||
Loffset -= _scrollPixelsX; // Move left
|
||||
if (Loffset < 0)
|
||||
Loffset = 0; // Now at extreme left
|
||||
|
||||
/*** New feature to prop up rickety scroll boundaries ***/
|
||||
if ((TinselVersion >= 2) && SysVar(SV_MinimumXoffset) && (Loffset < SysVar(SV_MinimumXoffset)))
|
||||
Loffset = SysVar(SV_MinimumXoffset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vertical scrolling
|
||||
*/
|
||||
if (_downScroll > 0) {
|
||||
_downScroll -= _scrollPixelsY;
|
||||
if (_downScroll < 0) {
|
||||
Toffset += _downScroll;
|
||||
_downScroll = 0;
|
||||
}
|
||||
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 ((TinselVersion >= 2) && SysVar(SV_MaximumYoffset) && Toffset > SysVar(SV_MaximumYoffset))
|
||||
Toffset = SysVar(SV_MaximumYoffset);
|
||||
|
||||
} else if (_downScroll < 0) {
|
||||
_downScroll += _scrollPixelsY;
|
||||
if (_downScroll > 0) {
|
||||
Toffset += _downScroll;
|
||||
_downScroll = 0;
|
||||
}
|
||||
Toffset -= _scrollPixelsY; // Move up
|
||||
|
||||
if (Toffset < 0)
|
||||
Toffset = 0; // Now at extreme top
|
||||
|
||||
/*** New feature to prop up rickety scroll boundaries ***/
|
||||
if ((TinselVersion >= 2) && SysVar(SV_MinimumYoffset) && Toffset < SysVar(SV_MinimumYoffset))
|
||||
Toffset = SysVar(SV_MinimumYoffset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move cursor if keeping cursor on a tag.
|
||||
*/
|
||||
if (_scrollCursor)
|
||||
_vm->_cursor->AdjustCursorXY(OldLoffset - Loffset, OldToffset - Toffset);
|
||||
|
||||
_vm->_bg->PlayfieldSetPos(FIELD_WORLD, Loffset, Toffset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See if the actor on whom the camera is is approaching an edge.
|
||||
* Request a scroll if he is.
|
||||
*/
|
||||
void Scroll::MonitorScroll() {
|
||||
int newx, newy;
|
||||
int Loffset, Toffset;
|
||||
|
||||
/*
|
||||
* Only do it if the actor is there and is visible
|
||||
*/
|
||||
if (!_pScrollMover || MoverHidden(_pScrollMover) || !MoverIs(_pScrollMover))
|
||||
return;
|
||||
|
||||
_vm->_actor->GetActorPos(_scrollActor, &newx, &newy);
|
||||
|
||||
if (_oldx == newx && _oldy == newy)
|
||||
return;
|
||||
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
||||
|
||||
/*
|
||||
* Approaching right side or left side of the screen?
|
||||
*/
|
||||
if (newx > Loffset+SCREEN_WIDTH - RLDISTANCE && Loffset < _imageW - SCREEN_WIDTH) {
|
||||
if (newx > _oldx)
|
||||
NeedScroll(LEFT);
|
||||
} else if (newx < Loffset + RLDISTANCE && Loffset) {
|
||||
if (newx < _oldx)
|
||||
NeedScroll(RIGHT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Approaching bottom or top of the screen?
|
||||
*/
|
||||
if (newy > Toffset+SCREEN_HEIGHT-DDISTANCE && Toffset < _imageH-SCREEN_HEIGHT) {
|
||||
if (newy > _oldy)
|
||||
NeedScroll(UP);
|
||||
} else if (Toffset && newy < Toffset + UDISTANCE + _vm->_actor->GetActorBottom(_scrollActor) - _vm->_actor->GetActorTop(_scrollActor)) {
|
||||
if (newy < _oldy)
|
||||
NeedScroll(DOWN);
|
||||
}
|
||||
|
||||
_oldx = newx;
|
||||
_oldy = newy;
|
||||
}
|
||||
|
||||
void Scroll::RestoreScrollDefaults() {
|
||||
_scrollData.xTrigger = SysVar(SV_SCROLL_XTRIGGER);
|
||||
_scrollData.xDistance = SysVar(SV_SCROLL_XDISTANCE);
|
||||
_scrollData.xSpeed = SysVar(SV_SCROLL_XSPEED);
|
||||
_scrollData.yTriggerTop = SysVar(SV_SCROLL_YTRIGGERTOP);
|
||||
_scrollData.yTriggerBottom= SysVar(SV_SCROLL_YTRIGGERBOT);
|
||||
_scrollData.yDistance = SysVar(SV_SCROLL_YDISTANCE);
|
||||
_scrollData.ySpeed = SysVar(SV_SCROLL_YSPEED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the obvious - called at the end of a scene.
|
||||
*/
|
||||
void Scroll::DropScroll() {
|
||||
_scrollData.NumNoH = _scrollData.NumNoV = 0;
|
||||
if (TinselVersion >= 2) {
|
||||
_leftScroll = _downScroll = 0; // No iterations outstanding
|
||||
_oldx = _oldy = 0;
|
||||
_scrollPixelsX = _scrollData.xSpeed;
|
||||
_scrollPixelsY = _scrollData.ySpeed;
|
||||
RestoreScrollDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change which actor the camera is following.
|
||||
*/
|
||||
void Scroll::ScrollFocus(int ano) {
|
||||
if (_scrollActor != ano) {
|
||||
_oldx = _oldy = 0;
|
||||
_scrollActor = ano;
|
||||
|
||||
_pScrollMover = ano ? GetMover(_scrollActor) : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actor which the camera is following
|
||||
*/
|
||||
int Scroll::GetScrollFocus() {
|
||||
return _scrollActor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to abslote position.
|
||||
*/
|
||||
void Scroll::ScrollTo(int x, int y, int xIter, int yIter) {
|
||||
int Loffset, Toffset; // for background offsets
|
||||
|
||||
_scrollPixelsX = xIter != 0 ? xIter : ((TinselVersion >= 2) ? _scrollData.xSpeed : SCROLLPIXELS);
|
||||
_scrollPixelsY = yIter != 0 ? yIter : ((TinselVersion >= 2) ? _scrollData.ySpeed : SCROLLPIXELS);
|
||||
|
||||
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); // get background offsets
|
||||
|
||||
_leftScroll = x - Loffset;
|
||||
_downScroll = y - Toffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill of any current scroll.
|
||||
*/
|
||||
void Scroll::KillScroll() {
|
||||
_leftScroll = _downScroll = 0;
|
||||
}
|
||||
|
||||
void Scroll::GetNoScrollData(SCROLLDATA *ssd) {
|
||||
memcpy(ssd, &_scrollData, sizeof(SCROLLDATA));
|
||||
}
|
||||
|
||||
void Scroll::RestoreNoScrollData(SCROLLDATA *ssd) {
|
||||
memcpy(&_scrollData, ssd, sizeof(SCROLLDATA));
|
||||
}
|
||||
|
||||
/**
|
||||
* SetScrollParameters
|
||||
*/
|
||||
void Scroll::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)
|
||||
_scrollData.xTrigger = xTrigger;
|
||||
if (xDistance)
|
||||
_scrollData.xDistance = xDistance;
|
||||
if (xSpeed)
|
||||
_scrollData.xSpeed = xSpeed;
|
||||
if (yTriggerTop)
|
||||
_scrollData.yTriggerTop = yTriggerTop;
|
||||
if (yTriggerBottom)
|
||||
_scrollData.yTriggerBottom = yTriggerBottom;
|
||||
if (yDistance)
|
||||
_scrollData.yDistance = yDistance;
|
||||
if (ySpeed)
|
||||
_scrollData.ySpeed = ySpeed;
|
||||
}
|
||||
}
|
||||
|
||||
bool Scroll::IsScrolling() {
|
||||
return (_leftScroll || _downScroll);
|
||||
}
|
||||
|
||||
void Scroll::InitScroll(int width, int height) {
|
||||
_imageH = height; // Dimensions
|
||||
_imageW = width; // of this scene.
|
||||
|
||||
if (TinselVersion <= 1) {
|
||||
_leftScroll = _downScroll = 0; // No iterations outstanding
|
||||
_oldx = _oldy = 0;
|
||||
_scrollPixelsX = _scrollPixelsY = SCROLLPIXELS;
|
||||
}
|
||||
|
||||
if (!_scrollActor)
|
||||
_scrollActor = _vm->_actor->GetLeadId();
|
||||
|
||||
_pScrollMover = GetMover(_scrollActor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide when to scroll and scroll when decided to.
|
||||
*/
|
||||
void ScrollProcess(CORO_PARAM, const void *) {
|
||||
int width, height;
|
||||
|
||||
// COROUTINE
|
||||
CORO_BEGIN_CONTEXT;
|
||||
CORO_END_CONTEXT(_ctx);
|
||||
|
||||
CORO_BEGIN_CODE(_ctx);
|
||||
|
||||
// In Tinsel v2, scenes may play movies, so the background may not always
|
||||
// already be initialized like it is in v1
|
||||
while (!_vm->_bg->GetBgObject())
|
||||
CORO_SLEEP(1);
|
||||
|
||||
width = _vm->_bg->BgWidth(); // Dimensions
|
||||
height = _vm->_bg->BgHeight(); // of this scene.
|
||||
|
||||
// Give up if there'll be no purpose in this process
|
||||
if (width == SCREEN_WIDTH && height == SCREEN_HEIGHT)
|
||||
CORO_KILL_SELF();
|
||||
|
||||
_vm->_scroll->InitScroll(width, height);
|
||||
|
||||
while (1) {
|
||||
_vm->_scroll->MonitorScroll(); // Set scroll requirement
|
||||
_vm->_scroll->ScrollImage();
|
||||
|
||||
CORO_SLEEP(1); // allow re-scheduling
|
||||
}
|
||||
|
||||
CORO_END_CODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
106
engines/tinsel/scroll.h
Normal file
106
engines/tinsel/scroll.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SCROLL_H // prevent multiple includes
|
||||
#define TINSEL_SCROLL_H
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
#define MAX_HNOSCROLL 10
|
||||
#define MAX_VNOSCROLL 10
|
||||
|
||||
// These structures defined here so boundaries can be saved
|
||||
struct NOSCROLLB {
|
||||
int ln;
|
||||
int c1;
|
||||
int c2;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class Scroll {
|
||||
public:
|
||||
Scroll();
|
||||
|
||||
void DontScrollCursor();
|
||||
void DoScrollCursor();
|
||||
|
||||
void SetNoScroll(int x1, int y1, int x2, int y2);
|
||||
void DropScroll();
|
||||
|
||||
void ScrollFocus(int actor);
|
||||
int GetScrollFocus();
|
||||
void ScrollTo(int x, int y, int xIter, int yIter);
|
||||
|
||||
void KillScroll();
|
||||
|
||||
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 ScrollImage();
|
||||
void MonitorScroll();
|
||||
|
||||
void InitScroll(int width, int height);
|
||||
|
||||
private:
|
||||
void NeedScroll(int direction);
|
||||
void RestoreScrollDefaults();
|
||||
|
||||
int _leftScroll, _downScroll; // Number of iterations outstanding
|
||||
|
||||
int _scrollActor;
|
||||
MOVER *_pScrollMover;
|
||||
int _oldx, _oldy;
|
||||
|
||||
/** Boundaries and numbers of boundaries */
|
||||
SCROLLDATA _scrollData;
|
||||
|
||||
int _imageW, _imageH;
|
||||
|
||||
bool _scrollCursor; // If a TAG or EXIT polygon is clicked on,
|
||||
// the cursor is kept over that polygon
|
||||
// whilst scrolling
|
||||
|
||||
int _scrollPixelsX, _scrollPixelsY;
|
||||
};
|
||||
|
||||
void ScrollProcess(CORO_PARAM, const void *);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif /* TINSEL_SCROLL_H */
|
||||
574
engines/tinsel/sound.cpp
Normal file
574
engines/tinsel/sound.cpp
Normal file
@@ -0,0 +1,574 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* sound functionality
|
||||
*/
|
||||
|
||||
#include "tinsel/sound.h"
|
||||
|
||||
#include "tinsel/adpcm.h"
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/config.h"
|
||||
#include "tinsel/music.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/sysvar.h"
|
||||
#include "tinsel/background.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/flac.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
#include "audio/decoders/xa.h"
|
||||
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
extern LANGUAGE g_sampleLanguage;
|
||||
|
||||
//--------------------------- General data ----------------------------------
|
||||
|
||||
SoundManager::SoundManager(TinselEngine *vm) :
|
||||
//_vm(vm), // TODO: Enable this once global _vm var is gone
|
||||
_sampleIndex(0), _sampleIndexLen(0),
|
||||
_soundMode(kVOCMode) {
|
||||
|
||||
for (int i = 0; i < kNumChannels; i++)
|
||||
_channels[i].sampleNum = _channels[i].subSample = -1;
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager() {
|
||||
free(_sampleIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the specified sample through the sound driver.
|
||||
* @param id Identifier of sample to be played
|
||||
* @param type type of sound (voice or sfx)
|
||||
* @param handle sound handle
|
||||
*/
|
||||
// playSample for DiscWorld 1
|
||||
bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
|
||||
// Floppy version has no sample file.
|
||||
if (!_vm->isV1CD())
|
||||
return false;
|
||||
|
||||
// no sample driver?
|
||||
if (!_vm->_mixer->isReady())
|
||||
return false;
|
||||
|
||||
Channel &curChan = _channels[kChannelTinsel1];
|
||||
|
||||
// stop any currently playing sample
|
||||
_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
|
||||
uint32 dwSampleIndex = _sampleIndex[id];
|
||||
|
||||
// move to correct position in the sample file
|
||||
_sampleStream.seek(dwSampleIndex);
|
||||
if (_sampleStream.eos() || _sampleStream.err() || (uint32)_sampleStream.pos() != dwSampleIndex)
|
||||
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
|
||||
|
||||
// read the length of the sample
|
||||
uint32 sampleLen = _sampleStream.readUint32();
|
||||
if (_sampleStream.eos() || _sampleStream.err())
|
||||
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
|
||||
|
||||
if (TinselV1PSX) {
|
||||
// Read the stream and create a XA ADPCM audio stream
|
||||
Audio::AudioStream *xaStream = Audio::makeXAStream(_sampleStream.readStream(sampleLen), 44100);
|
||||
|
||||
// FIXME: Should set this in a different place ;)
|
||||
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
|
||||
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
|
||||
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
|
||||
|
||||
// Play the audio stream
|
||||
_vm->_mixer->playStream(type, &curChan.handle, xaStream);
|
||||
} else if (TinselV1Saturn) {
|
||||
// TODO: Sound format for the Saturn version - looks to be raw, but isn't
|
||||
} else {
|
||||
// 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(g_sampleLanguage));
|
||||
|
||||
// FIXME: Should set this in a different place ;)
|
||||
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
|
||||
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
|
||||
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
|
||||
|
||||
Audio::AudioStream *sampleStream = 0;
|
||||
|
||||
// play it
|
||||
switch (_soundMode) {
|
||||
case kMP3Mode:
|
||||
#ifdef USE_MAD
|
||||
{
|
||||
Common::MemoryReadStream *compressedStream =
|
||||
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
|
||||
sampleStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case kVorbisMode:
|
||||
#ifdef USE_VORBIS
|
||||
{
|
||||
Common::MemoryReadStream *compressedStream =
|
||||
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
|
||||
sampleStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case kFLACMode:
|
||||
#ifdef USE_FLAC
|
||||
{
|
||||
Common::MemoryReadStream *compressedStream =
|
||||
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
|
||||
sampleStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
sampleStream = Audio::makeRawStream(sampleBuf, sampleLen, 22050, Audio::FLAG_UNSIGNED);
|
||||
break;
|
||||
}
|
||||
if (sampleStream) {
|
||||
_vm->_mixer->playStream(type, &curChan.handle, sampleStream);
|
||||
}
|
||||
}
|
||||
|
||||
if (handle)
|
||||
*handle = curChan.handle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundManager::playDW1MacMusic(Common::File &s, uint32 length) {
|
||||
// TODO: It's a bad idea to load the music track in a buffer.
|
||||
// We should use a SubReadStream instead, and keep midi.dat open.
|
||||
// However, the track lengths aren't that big (about 1-4MB),
|
||||
// so this shouldn't be a major issue.
|
||||
byte *soundData = (byte *)malloc(length);
|
||||
assert(soundData);
|
||||
|
||||
// read all of the sample
|
||||
if (s.read(soundData, length) != length)
|
||||
error(FILE_IS_CORRUPT, MIDI_FILE);
|
||||
|
||||
Common::SeekableReadStream *memStream = new Common::MemoryReadStream(soundData, length);
|
||||
|
||||
Audio::SoundHandle *handle = &_channels[kChannelDW1MacMusic].handle;
|
||||
|
||||
// Stop any previously playing music track
|
||||
_vm->_mixer->stopHandle(*handle);
|
||||
|
||||
// TODO: Compression support (MP3/OGG/FLAC) for midi.dat in DW1 Mac
|
||||
Audio::RewindableAudioStream *musicStream = Audio::makeRawStream(memStream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
|
||||
|
||||
if (musicStream)
|
||||
_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, handle, Audio::makeLoopingAudioStream(musicStream, 0));
|
||||
}
|
||||
|
||||
// playSample for DiscWorld 2
|
||||
bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int priority,
|
||||
Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
|
||||
|
||||
// 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-existent sample %d", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// move to correct position in the sample file
|
||||
_sampleStream.seek(dwSampleIndex);
|
||||
if (_sampleStream.eos() || _sampleStream.err() || (uint32)_sampleStream.pos() != dwSampleIndex)
|
||||
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
|
||||
|
||||
// read the length of the sample
|
||||
uint32 sampleLen = _sampleStream.readUint32LE();
|
||||
if (_sampleStream.eos() || _sampleStream.err())
|
||||
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_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.eos() || _sampleStream.err())
|
||||
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
|
||||
}
|
||||
sampleLen = _sampleStream.readUint32LE();
|
||||
if (_sampleStream.eos() || _sampleStream.err())
|
||||
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
|
||||
}
|
||||
|
||||
debugC(DEBUG_DETAILED, kTinselDebugSound, "Playing sound %d.%d, %d bytes at %d (pan %d)", id, sub, sampleLen,
|
||||
(int)_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(g_sampleLanguage));
|
||||
|
||||
Common::MemoryReadStream *compressedStream =
|
||||
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
|
||||
Audio::AudioStream *sampleStream = 0;
|
||||
|
||||
switch (_soundMode) {
|
||||
case kMP3Mode:
|
||||
#ifdef USE_MAD
|
||||
sampleStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES);
|
||||
#endif
|
||||
break;
|
||||
case kVorbisMode:
|
||||
#ifdef USE_VORBIS
|
||||
sampleStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES);
|
||||
#endif
|
||||
break;
|
||||
case kFLACMode:
|
||||
#ifdef USE_FLAC
|
||||
sampleStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
sampleStream = new Tinsel6_ADPCMStream(compressedStream, DisposeAfterUse::YES, sampleLen, 22050, 1, 24);
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: Should set this in a different place ;)
|
||||
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
|
||||
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
|
||||
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
|
||||
|
||||
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
|
||||
// not needed and won't work when using MP3/OGG/FLAC anyway
|
||||
//curChan->timeDuration = (((sampleLen * 64) / 25) * 1000) / (22050 * 2);
|
||||
|
||||
// Play it
|
||||
_vm->_mixer->playStream(type, &curChan->handle, sampleStream);
|
||||
|
||||
_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 center
|
||||
x -= _vm->_bg->PlayfieldGetCenterX(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 -= _vm->_bg->PlayfieldGetCenterX(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
|
||||
*/
|
||||
bool SoundManager::sampleExists(int id) {
|
||||
if (_vm->_mixer->isReady()) {
|
||||
// make sure id is in range
|
||||
if (id > 0 && id < _sampleIndexLen) {
|
||||
// check for a sample index
|
||||
if (_sampleIndex[id])
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no sample driver or no sample
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a sample is currently playing.
|
||||
*/
|
||||
bool SoundManager::sampleIsPlaying() {
|
||||
if (TinselVersion <= 1)
|
||||
return _vm->_mixer->isSoundHandleActive(_channels[kChannelTinsel1].handle);
|
||||
|
||||
for (int i = 0; i < kNumChannels; i++)
|
||||
if (_vm->_mixer->isSoundHandleActive(_channels[i].handle))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any currently playing sample.
|
||||
*/
|
||||
void SoundManager::stopAllSamples() {
|
||||
if (TinselVersion <= 1) {
|
||||
_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 (TinselVersion <= 1) {
|
||||
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 (TinselVersion <= 1)
|
||||
return;
|
||||
|
||||
for (int i = kChannelSFX; i < kNumChannels; i++)
|
||||
_vm->_mixer->setChannelVolume(_channels[i].handle, volume);
|
||||
}
|
||||
|
||||
void SoundManager::showSoundError(const char *errorMsg, const char *soundFile) {
|
||||
Common::String msg;
|
||||
msg = Common::String::format(errorMsg, soundFile);
|
||||
GUI::MessageDialog dialog(msg.c_str());
|
||||
dialog.runModal();
|
||||
|
||||
error("%s", msg.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and inits all sound sample files.
|
||||
*/
|
||||
void SoundManager::openSampleFiles() {
|
||||
// V1 Floppy and V0 demo versions have no sample files
|
||||
if ((TinselVersion == 0) || ((TinselVersion == 1) && !_vm->isV1CD()))
|
||||
return;
|
||||
|
||||
TinselFile f(TinselV1Saturn);
|
||||
|
||||
if (_sampleIndex)
|
||||
// already allocated
|
||||
return;
|
||||
|
||||
// Open sample index (*.idx) in binary mode
|
||||
if (f.open(_vm->getSampleIndex(g_sampleLanguage))) {
|
||||
uint32 fileSize = f.size();
|
||||
_sampleIndex = (uint32 *)malloc(fileSize);
|
||||
if (_sampleIndex == NULL) {
|
||||
showSoundError(NO_MEM, _vm->getSampleIndex(g_sampleLanguage));
|
||||
return;
|
||||
}
|
||||
|
||||
_sampleIndexLen = fileSize / 4; // total sample of indices (DWORDs)
|
||||
|
||||
// Load data
|
||||
for (int i = 0; i < _sampleIndexLen; ++i) {
|
||||
_sampleIndex[i] = f.readUint32();
|
||||
if (f.err()) {
|
||||
showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage));
|
||||
}
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
// Detect format of soundfile by looking at 1st sample-index
|
||||
switch (TO_BE_32(_sampleIndex[0])) {
|
||||
case MKTAG('M','P','3',' '):
|
||||
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected MP3 sound-data");
|
||||
_soundMode = kMP3Mode;
|
||||
break;
|
||||
case MKTAG('O','G','G',' '):
|
||||
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected OGG sound-data");
|
||||
_soundMode = kVorbisMode;
|
||||
break;
|
||||
case MKTAG('F','L','A','C'):
|
||||
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected FLAC sound-data");
|
||||
_soundMode = kFLACMode;
|
||||
break;
|
||||
default:
|
||||
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected original sound-data");
|
||||
if (TinselVersion == 3) {
|
||||
// And in Noir, the data is MP3
|
||||
_soundMode = kMP3Mode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Normally the 1st sample index points to nothing at all. We use it to
|
||||
// determine if the game's sample files have been compressed, thus restore
|
||||
// it here
|
||||
_sampleIndex[0] = 0;
|
||||
} else {
|
||||
showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage));
|
||||
}
|
||||
|
||||
// Open sample file (*.smp) in binary mode
|
||||
if (!_sampleStream.open(_vm->getSampleFile(g_sampleLanguage))) {
|
||||
showSoundError(FILE_READ_ERROR, _vm->getSampleFile(g_sampleLanguage));
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::closeSampleStream() {
|
||||
_sampleStream.close();
|
||||
free(_sampleIndex);
|
||||
_sampleIndex = 0;
|
||||
_sampleIndexLen = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
130
engines/tinsel/sound.h
Normal file
130
engines/tinsel/sound.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file contains the Sound Driver data structures etc.
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_SOUND_H
|
||||
#define TINSEL_SOUND_H
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/tinsel.h"
|
||||
#include "tinsel/drives.h"
|
||||
|
||||
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 *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
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,
|
||||
kChannelDW1MacMusic = 2
|
||||
};
|
||||
static const int kNumChannels = kChannelSFX + kNumSFX;
|
||||
|
||||
enum SoundMode {
|
||||
kVOCMode,
|
||||
kMP3Mode,
|
||||
kVorbisMode,
|
||||
kFLACMode
|
||||
};
|
||||
|
||||
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 index buffer and number of entries */
|
||||
uint32 *_sampleIndex;
|
||||
|
||||
/** Number of entries in the sample index */
|
||||
int _sampleIndexLen;
|
||||
|
||||
/** Specifies if the sample-data is compressed and if yes, how */
|
||||
SoundMode _soundMode;
|
||||
|
||||
/** file stream for sample file */
|
||||
TinselFile _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);
|
||||
bool playSample(int id, int sub, bool bLooped, int x, int y, int priority,
|
||||
Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
|
||||
void playDW1MacMusic(Common::File &s, uint32 length);
|
||||
|
||||
void stopAllSamples(); // 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 openSampleFiles();
|
||||
void closeSampleStream();
|
||||
|
||||
private:
|
||||
void showSoundError(const char *errorMsg, const char *soundFile);
|
||||
};
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif // TINSEL_SOUND_H
|
||||
403
engines/tinsel/strres.cpp
Normal file
403
engines/tinsel/strres.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* String resource management routines
|
||||
*/
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/drives.h"
|
||||
#include "tinsel/sound.h"
|
||||
#include "tinsel/strres.h"
|
||||
#include "tinsel/scn.h"
|
||||
#include "common/file.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
#ifdef DEBUG
|
||||
// Diagnostic number
|
||||
int g_newestString;
|
||||
#endif
|
||||
|
||||
// buffer for resource strings
|
||||
static uint8 *g_textBuffer = nullptr;
|
||||
|
||||
static struct {
|
||||
bool bPresent;
|
||||
const char *szStem;
|
||||
SCNHANDLE hDescription;
|
||||
SCNHANDLE hFlagFilm;
|
||||
|
||||
} g_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 g_bMultiByte = false;
|
||||
|
||||
LANGUAGE g_textLanguage, g_sampleLanguage = TXT_ENGLISH;
|
||||
|
||||
//----------------- FUNCTIONS --------------------------------
|
||||
|
||||
void ResetVarsStrRes() {
|
||||
g_textBuffer = nullptr;
|
||||
g_bMultiByte = false;
|
||||
g_textLanguage = g_sampleLanguage = TXT_ENGLISH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to load a resource file for a different language
|
||||
* @param newLang The new language
|
||||
*/
|
||||
void ChangeLanguage(LANGUAGE newLang) {
|
||||
TinselFile f(TinselV1Mac || TinselV1Saturn);
|
||||
uint32 textLen = 0; // length of buffer
|
||||
|
||||
g_textLanguage = newLang;
|
||||
g_sampleLanguage = newLang;
|
||||
|
||||
// free the previous buffer
|
||||
free(g_textBuffer);
|
||||
g_textBuffer = nullptr;
|
||||
|
||||
// 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(_vm->getTextFile(newLang))) {
|
||||
if ((newLang == TXT_ENGLISH) || !f.open(_vm->getTextFile(TXT_ENGLISH))) {
|
||||
char buf[50];
|
||||
Common::sprintf_s(buf, CANNOT_FIND_FILE, _vm->getTextFile(newLang));
|
||||
GUI::MessageDialog dialog(buf);
|
||||
dialog.runModal();
|
||||
|
||||
error(CANNOT_FIND_FILE, _vm->getTextFile(newLang));
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the file is compressed or not - for compressed files the
|
||||
// first long is the filelength and for uncompressed files it is the chunk
|
||||
// identifier
|
||||
textLen = f.readUint32();
|
||||
if (f.eos() || f.err())
|
||||
error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
|
||||
|
||||
if (textLen == CHUNK_STRING || textLen == CHUNK_MBSTRING) {
|
||||
// the file is uncompressed
|
||||
|
||||
g_bMultiByte = (textLen == CHUNK_MBSTRING);
|
||||
|
||||
// get length of uncompressed file
|
||||
textLen = f.size();
|
||||
f.seek(0, SEEK_SET); // Set to beginning of file
|
||||
|
||||
if (g_textBuffer == NULL) {
|
||||
// allocate a text buffer for the strings
|
||||
g_textBuffer = (uint8 *)malloc(textLen);
|
||||
|
||||
// make sure memory allocated
|
||||
assert(g_textBuffer);
|
||||
}
|
||||
|
||||
// load data
|
||||
if (f.read(g_textBuffer, textLen) != textLen)
|
||||
// file must be corrupt if we get to here
|
||||
error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
|
||||
|
||||
// close the file
|
||||
f.close();
|
||||
} else {
|
||||
// the file must be compressed
|
||||
error("Compression handling for text file has been removed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FindStringBase
|
||||
*/
|
||||
static byte *FindStringBase(int id) {
|
||||
// base of string resource table
|
||||
byte *pText = g_textBuffer;
|
||||
|
||||
// For Tinsel 0, Ids are decremented by 1
|
||||
if (TinselVersion == 0)
|
||||
--id;
|
||||
|
||||
// index into text resource file
|
||||
uint32 index = 0;
|
||||
|
||||
// number of chunks to skip
|
||||
int chunkSkip = id / STRINGS_PER_CHUNK;
|
||||
|
||||
// number of strings to skip when in the correct chunk
|
||||
int strSkip = id % STRINGS_PER_CHUNK;
|
||||
|
||||
// skip to the correct chunk
|
||||
while (chunkSkip-- != 0) {
|
||||
// make sure chunk id is correct
|
||||
assert(READ_32(pText + index) == CHUNK_STRING || READ_32(pText + index) == CHUNK_MBSTRING);
|
||||
|
||||
if (READ_32(pText + index + sizeof(uint32)) == 0) {
|
||||
// string does not exist
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get index to next chunk
|
||||
index = READ_32(pText + index + sizeof(uint32));
|
||||
}
|
||||
|
||||
// skip over chunk id and offset
|
||||
index += (2 * sizeof(uint32));
|
||||
|
||||
// pointer to strings
|
||||
pText = pText + index;
|
||||
|
||||
// skip to the correct string
|
||||
while (strSkip-- != 0) {
|
||||
// skip to next string
|
||||
|
||||
if ((TinselVersion <= 1) || ((*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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pText;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Common::strcpy_s(pBuffer, bufferMax, "!! HIGH STRING !!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((TinselVersion <= 1) || ((*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) {
|
||||
memcpy(pBuffer, pText + 1, len);
|
||||
|
||||
// null terminate
|
||||
pBuffer[len] = 0;
|
||||
|
||||
// number of chars copied
|
||||
return len + 1;
|
||||
} else {
|
||||
memcpy(pBuffer, pText + 1, bufferMax - 1);
|
||||
|
||||
// null terminate
|
||||
pBuffer[bufferMax - 1] = 0;
|
||||
|
||||
// number of chars copied
|
||||
return bufferMax;
|
||||
}
|
||||
}
|
||||
|
||||
// TEMPORARY DIRTY BODGE
|
||||
Common::strcpy_s(pBuffer, bufferMax, "!! NULL STRING !!");
|
||||
|
||||
// string does not exist
|
||||
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() {
|
||||
free(g_textBuffer);
|
||||
g_textBuffer = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from TINLIB.C from DeclareLanguage().
|
||||
*/
|
||||
|
||||
void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
|
||||
assert(language >= 0 && language < NUM_LANGUAGES);
|
||||
|
||||
g_languages[language].hDescription = hDescription;
|
||||
g_languages[language].hFlagFilm = hFlagFilm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current subtitles language
|
||||
*/
|
||||
LANGUAGE TextLanguage() {
|
||||
return g_textLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current voice language
|
||||
*/
|
||||
LANGUAGE SampleLanguage() {
|
||||
return g_sampleLanguage;
|
||||
}
|
||||
|
||||
int NumberOfLanguages() {
|
||||
int i, count;
|
||||
|
||||
for (i = 0, count = 0; i < NUM_LANGUAGES; i++) {
|
||||
if (g_languages[i].bPresent)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
SCNHANDLE LanguageDesc(LANGUAGE thisOne) {
|
||||
return g_languages[thisOne].hDescription;
|
||||
}
|
||||
|
||||
SCNHANDLE LanguageFlag(LANGUAGE thisOne) {
|
||||
return g_languages[thisOne].hFlagFilm;
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
95
engines/tinsel/strres.h
Normal file
95
engines/tinsel/strres.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* String resource management routines
|
||||
*/
|
||||
|
||||
#ifndef TINSEL_STRRES_H
|
||||
#define TINSEL_STRRES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "tinsel/dw.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
#define STRINGS_PER_CHUNK 64 // number of strings per chunk in the language text files
|
||||
#define FIRST_STR_ID 1 // id number of first string in string table
|
||||
#define MAX_STRING_SIZE 255 // maximum size of a string in the resource table
|
||||
#define MAX_STRRES_SIZE 300000 // maximum size of string resource file
|
||||
|
||||
// Set if we're handling 2-byte characters.
|
||||
extern bool g_bMultiByte;
|
||||
|
||||
/*----------------------------------------------------------------------*\
|
||||
|* Function Prototypes *|
|
||||
\*----------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Called to load a resource file for a different language
|
||||
* @param newLang The new language
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* Gets the current voice language
|
||||
*/
|
||||
LANGUAGE SampleLanguage();
|
||||
|
||||
int NumberOfLanguages();
|
||||
SCNHANDLE LanguageDesc(LANGUAGE thisOne);
|
||||
SCNHANDLE LanguageFlag(LANGUAGE thisOne);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
214
engines/tinsel/sysvar.cpp
Normal file
214
engines/tinsel/sysvar.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
// Return for SYS_Platform
|
||||
typedef enum { DOS_PC, WIN_PC, APPLE_MAC, SONY_PSX, SEGA_SATURN } platform;
|
||||
|
||||
//----------------- GLOBAL GLOBAL DATA --------------------
|
||||
|
||||
extern int NewestSavedGame();
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
static int g_systemVars[SV_TOPVALID_T3];
|
||||
static SCNHANDLE g_systemStrings[SS_MAX_VALID];
|
||||
|
||||
//----------------- FUNCTIONS --------------------------------
|
||||
|
||||
void ResetVarsSysVar() {
|
||||
memset(g_systemVars, 0, sizeof(g_systemVars));
|
||||
memset(g_systemStrings, 0, sizeof(g_systemStrings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the system variable list
|
||||
*/
|
||||
|
||||
void InitSysVars() {
|
||||
int initialSystemVars[SV_TOPVALID_T3] = {
|
||||
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 color 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_COLOR
|
||||
};
|
||||
|
||||
memcpy(g_systemVars, initialSystemVars, sizeof(g_systemVars));
|
||||
|
||||
g_systemVars[SV_SCROLL_XDISTANCE] = SCREEN_WIDTH / 2;
|
||||
g_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:
|
||||
g_systemVars[varId] = 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 _vm->_config->_useSubtitles;
|
||||
|
||||
case SV_SAVED_GAME_EXISTS:
|
||||
return NewestSavedGame() != -1;
|
||||
|
||||
case SYS_Debug:
|
||||
// FIXME: This isn't currently defined
|
||||
return false;
|
||||
//return bDebuggingAllowed;
|
||||
|
||||
default:
|
||||
return g_systemVars[varId];
|
||||
}
|
||||
}
|
||||
|
||||
void SaveSysVars(int *pSv) {
|
||||
memcpy(pSv, g_systemVars, sizeof(g_systemVars));
|
||||
}
|
||||
|
||||
void RestoreSysVars(int *pSv) {
|
||||
memcpy(g_systemVars, pSv, sizeof(g_systemVars));
|
||||
}
|
||||
|
||||
void SetSysString(int number, SCNHANDLE hString) {
|
||||
assert(number >= 0 && number < SS_MAX_VALID);
|
||||
|
||||
g_systemStrings[number] = hString;
|
||||
}
|
||||
|
||||
SCNHANDLE SysString(int number) {
|
||||
assert(number >= 0 && number < SS_MAX_VALID);
|
||||
|
||||
return g_systemStrings[number];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the no blocking flag. Note that for convenience, the systemVars array
|
||||
* entry is used even for Tinsel 1, which originally used a separate variable.
|
||||
*/
|
||||
bool GetNoBlocking() {
|
||||
return SysVar(ISV_NO_BLOCKING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the no blocking flag. Note that for convenience, the systemVars array
|
||||
* entry is used even for Tinsel 1, which originally used a separate variable.
|
||||
*/
|
||||
void SetNoBlocking(bool flag) {
|
||||
SetSysVar(ISV_NO_BLOCKING, flag);
|
||||
}
|
||||
|
||||
} // End of namespace Tinsel
|
||||
168
engines/tinsel/sysvar.h
Normal file
168
engines/tinsel/sysvar.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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_TAGCOLOR, // if set, default actor's text color 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_T2 = 0x28,
|
||||
ISV_NO_BLOCKING_T2 = 0x29,
|
||||
ISV_GHOST_ACTOR_T2 = 0x2A,
|
||||
ISV_GHOST_BASE_T2 = 0x2B,
|
||||
ISV_GHOST_COLOR_T2 = 0x2C,
|
||||
|
||||
SV_TOPVALID_T2 = 0x2D,
|
||||
|
||||
SV_SPRITER_SCENE_ID = 0x2F, // Noir, loaded scene
|
||||
ISV_DIVERT_ACTOR_T3 = 0x32,
|
||||
ISV_NO_BLOCKING_T3 = 0x33,
|
||||
ISV_GHOST_ACTOR_T3 = 0x34,
|
||||
ISV_GHOST_BASE_T3 = 0x35,
|
||||
ISV_GHOST_COLOR_T3 = 0x36,
|
||||
SV_SPRITER_SCALE = 0x37, // Noir, scale used for 3D rendering
|
||||
SV_SPRITER_OVERLAY = 0x38, // Noir, if additional model is loaded
|
||||
|
||||
SV_TOPVALID_T3 } SYSVARS;
|
||||
|
||||
#define ISV_DIVERT_ACTOR ((TinselVersion == 3) ? ISV_DIVERT_ACTOR_T3 : ISV_DIVERT_ACTOR_T2)
|
||||
#define ISV_NO_BLOCKING ((TinselVersion == 3) ? ISV_NO_BLOCKING_T3 : ISV_NO_BLOCKING_T2)
|
||||
#define ISV_GHOST_ACTOR ((TinselVersion == 3) ? ISV_GHOST_ACTOR_T3 : ISV_GHOST_ACTOR_T2)
|
||||
#define ISV_GHOST_BASE ((TinselVersion == 3) ? ISV_GHOST_BASE_T3 : ISV_GHOST_BASE_T2)
|
||||
#define ISV_GHOST_COLOR ((TinselVersion == 3) ? ISV_GHOST_COLOR_T3 : ISV_GHOST_COLOR_T2)
|
||||
#define SV_TOPVALID ((TinselVersion == 3) ? SV_TOPVALID_T3 : SV_TOPVALID_T2)
|
||||
|
||||
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,
|
||||
|
||||
// Noir only:
|
||||
SS_NOIR1,
|
||||
SS_NOIR2,
|
||||
SS_NOIR3,
|
||||
SS_NOIR4,
|
||||
|
||||
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 SetNoBlocking(bool flag);
|
||||
|
||||
} // End of namespace Tinsel
|
||||
|
||||
#endif
|
||||
292
engines/tinsel/text.cpp
Normal file
292
engines/tinsel/text.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Text utilities.
|
||||
*/
|
||||
|
||||
#include "tinsel/dw.h"
|
||||
#include "tinsel/graphics.h" // object plotting
|
||||
#include "tinsel/handle.h"
|
||||
#include "tinsel/sched.h" // process scheduler defines
|
||||
#include "tinsel/strres.h" // g_bMultiByte
|
||||
#include "tinsel/text.h" // text defines
|
||||
|
||||
namespace Tinsel {
|
||||
|
||||
|
||||
//----------------- LOCAL GLOBAL DATA --------------------
|
||||
/** TinselV3, base color for the text color replacement */
|
||||
static uint32 g_t3fontBaseColor;
|
||||
|
||||
/**
|
||||
* Returns the length of one line of a string in pixels.
|
||||
* @param szStr String
|
||||
* @param pFont Which font to use for dimensions
|
||||
*/
|
||||
int StringLengthPix(char *szStr, const FONT *pFont) {
|
||||
int strLen; // accumulated length of string
|
||||
byte c;
|
||||
SCNHANDLE hImg;
|
||||
|
||||
// while not end of string or end of line
|
||||
for (strLen = 0; (c = *szStr) != EOS_CHAR && c != LF_CHAR; szStr++) {
|
||||
if (g_bMultiByte) {
|
||||
if (c & 0x80)
|
||||
c = ((c & ~0x80) << 8) + *++szStr;
|
||||
}
|
||||
hImg = pFont->fontDef[c];
|
||||
|
||||
if (hImg) {
|
||||
// there is a IMAGE for this character
|
||||
const IMAGE *pChar = _vm->_handle->GetImage(hImg);
|
||||
|
||||
// add width of font bitmap
|
||||
strLen += pChar->imgWidth;
|
||||
|
||||
delete pChar;
|
||||
} else
|
||||
// use width of space character
|
||||
strLen += pFont->spaceSize;
|
||||
|
||||
// finally add the inter-character spacing
|
||||
strLen += pFont->xSpacing;
|
||||
}
|
||||
|
||||
// return length of line in pixels - minus inter-char spacing for last character
|
||||
strLen -= pFont->xSpacing;
|
||||
return (strLen > 0) ? strLen : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the justified x start position of a line of text.
|
||||
* @param szStr String to output
|
||||
* @param xPos X position of string
|
||||
* @param pFont Which font to use
|
||||
* @param mode Mode flags for the string
|
||||
*/
|
||||
int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
|
||||
if (mode & TXT_CENTER) {
|
||||
// center justify the text
|
||||
|
||||
// adjust x positioning by half the length of line in pixels
|
||||
xPos -= StringLengthPix(szStr, pFont) / 2;
|
||||
} else if (mode & TXT_RIGHT) {
|
||||
// right justify the text
|
||||
|
||||
// adjust x positioning by length of line in pixels
|
||||
xPos -= StringLengthPix(szStr, pFont);
|
||||
}
|
||||
|
||||
// return text line x start position
|
||||
return xPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 color Color 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(OBJECT **pList, char *szStr, int color,
|
||||
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
|
||||
OBJECT *pChar = 0; // object ptr for the character
|
||||
byte c;
|
||||
SCNHANDLE hImg;
|
||||
|
||||
// make sure there is a linked list to add text to
|
||||
assert(pList);
|
||||
|
||||
// get font pointer
|
||||
FONT *pFont = _vm->_handle->GetFont(hFont);
|
||||
const OBJ_INIT *pFontInit = &pFont->fontInit;
|
||||
|
||||
// init head of text list
|
||||
pFirst = nullptr;
|
||||
|
||||
// get image for capital W
|
||||
SCNHANDLE imgHandle = pFont->fontDef[(int)'W'];
|
||||
assert(imgHandle);
|
||||
|
||||
// get height of capital W for offset to next line
|
||||
const IMAGE *pImgCapitalW = _vm->_handle->GetImage(imgHandle);
|
||||
yOffset = pImgCapitalW->imgHeight & ~C16_FLAG_MASK;
|
||||
delete pImgCapitalW;
|
||||
|
||||
while (*szStr) {
|
||||
// x justify the text according to the mode flags
|
||||
xJustify = JustifyText(szStr, xPos, pFont, mode);
|
||||
|
||||
// repeat until end of string or end of line
|
||||
while ((c = *szStr) != EOS_CHAR && c != LF_CHAR) {
|
||||
if (g_bMultiByte) {
|
||||
if (c & 0x80)
|
||||
c = ((c & ~0x80) << 8) + *++szStr;
|
||||
}
|
||||
hImg = pFont->fontDef[c];
|
||||
|
||||
if (hImg == 0) {
|
||||
// no image for this character
|
||||
|
||||
// add font spacing for a space character
|
||||
xJustify += pFont->spaceSize;
|
||||
} else { // printable character
|
||||
|
||||
int aniX, aniY; // char image animation offsets
|
||||
|
||||
// allocate and init a character object
|
||||
if (pFirst == NULL)
|
||||
// first time - init head of list
|
||||
pFirst = pChar = InitObject(pFontInit);
|
||||
else
|
||||
// chain to multi-char list
|
||||
pChar = pChar->pSlave = InitObject(pFontInit);
|
||||
|
||||
// convert image handle to pointer
|
||||
const IMAGE *pImg = _vm->_handle->GetImage(hImg);
|
||||
|
||||
// fill in character object
|
||||
pChar->hImg = hImg; // image def
|
||||
pChar->width = pImg->imgWidth; // width of chars bitmap
|
||||
pChar->height = pImg->imgHeight & ~C16_FLAG_MASK; // height of chars bitmap
|
||||
pChar->hBits = pImg->hImgBits; // bitmap
|
||||
|
||||
// check for absolute positioning
|
||||
if (mode & TXT_ABSOLUTE)
|
||||
pChar->flags |= DMA_ABS;
|
||||
|
||||
// set characters color - only effective for mono fonts
|
||||
pChar->constant = color;
|
||||
|
||||
// set the base font color to be replaced with supplied color, only for Tinsel V3
|
||||
g_t3fontBaseColor = (TinselVersion == 3) ? pFont->baseColor : 0;
|
||||
|
||||
// get Y animation offset
|
||||
GetAniOffset(hImg, pChar->flags, &aniX, &aniY);
|
||||
|
||||
// set x position - ignore animation point
|
||||
pChar->xPos = intToFrac(xJustify);
|
||||
|
||||
// set y position - adjust for animation point
|
||||
pChar->yPos = intToFrac(yPos - aniY);
|
||||
|
||||
if (mode & TXT_SHADOW) {
|
||||
// we want to shadow the character
|
||||
OBJECT *pShad;
|
||||
|
||||
// allocate a object for the shadow and chain to multi-char list
|
||||
pShad = pChar->pSlave = AllocObject();
|
||||
|
||||
// copy the character for a shadow
|
||||
CopyObject(pShad, pChar);
|
||||
|
||||
// add shadow offsets to characters position
|
||||
pShad->xPos += intToFrac(pFont->xShadow);
|
||||
pShad->yPos += intToFrac(pFont->yShadow);
|
||||
|
||||
// shadow is behind the character
|
||||
pShad->zPos--;
|
||||
|
||||
// shadow is always mono
|
||||
pShad->flags = DMA_CNZ | DMA_CHANGED;
|
||||
|
||||
// check for absolute positioning
|
||||
if (mode & TXT_ABSOLUTE)
|
||||
pShad->flags |= DMA_ABS;
|
||||
|
||||
// shadow always uses first palette entry
|
||||
// should really alloc a palette here also ????
|
||||
pShad->constant = 1;
|
||||
|
||||
// add shadow to object list
|
||||
InsertObject(pList, pShad);
|
||||
}
|
||||
|
||||
// add character to object list
|
||||
InsertObject(pList, pChar);
|
||||
|
||||
// move to end of list
|
||||
if (pChar->pSlave)
|
||||
pChar = pChar->pSlave;
|
||||
|
||||
// add character spacing
|
||||
xJustify += pImg->imgWidth;
|
||||
|
||||
delete pImg;
|
||||
}
|
||||
|
||||
// finally add the inter-character spacing
|
||||
xJustify += pFont->xSpacing;
|
||||
|
||||
// next character in string
|
||||
++szStr;
|
||||
}
|
||||
|
||||
// adjust the text y position and add the inter-line spacing
|
||||
yPos += yOffset + pFont->ySpacing;
|
||||
|
||||
// check for newline
|
||||
if (c == LF_CHAR)
|
||||
// next character in string
|
||||
++szStr;
|
||||
}
|
||||
|
||||
delete pFont;
|
||||
|
||||
// return head of list
|
||||
return pFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an image for this character in this font?
|
||||
* @param hFont which font to use
|
||||
* @param c character to test
|
||||
*/
|
||||
bool IsCharImage(SCNHANDLE hFont, char c) {
|
||||
byte c2 = (byte)c;
|
||||
|
||||
// Inventory save game name editor needs to be more clever for
|
||||
// multi-byte characters. This bodge will stop it erring.
|
||||
if (g_bMultiByte && (c2 & 0x80))
|
||||
return false;
|
||||
|
||||
// get font pointer
|
||||
FONT *pFont = _vm->_handle->GetFont(hFont);
|
||||
bool result = pFont->fontDef[c2] != 0;
|
||||
delete pFont;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 t3GetBaseColor()
|
||||
{
|
||||
return g_t3fontBaseColor;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Tinsel
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user