Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
engines/trecision/detection.cpp
engines/trecision/metaengine.cpp
engines/trecision/saveload.cpp

481
engines/trecision/actor.cpp Normal file
View File

@@ -0,0 +1,481 @@
/* 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 "trecision/actor.h"
#include "trecision/sound.h"
#include "trecision/pathfinding3d.h"
#include "trecision/scheduler.h"
#include "trecision/defines.h"
#include "trecision/graphics.h"
#include "trecision/trecision.h"
namespace Trecision {
Actor::Actor(TrecisionEngine *vm) : _vm(vm) {
_vertex = nullptr;
_face = nullptr;
_light = nullptr;
_camera = nullptr;
_textures = nullptr;
_textureData = nullptr;
_vertexNum = 0;
_faceNum = 0;
_lightNum = 0;
_px = _pz = 0.0f;
_dx = _dz = 0.0f;
_theta = 0.0f;
for (uint8 i = 0; i < 6; ++i)
_area[i] = 0;
_curFrame = 0;
_curAction = 0;
for (uint16 i = 0; i < MAXFACE; ++i) {
for (uint8 j = 0; j < 3; ++j) {
_textureCoord[i][j][0] = 0;
_textureCoord[i][j][1] = 0;
}
}
_characterArea = nullptr;
_textureData = _vm->readData("textur.bm");
initTextures();
readModel("jm.om");
for (int i = 0; i < MAXLIGHT; ++i)
_lightArea[i].clear();
_cameraArea.clear();
_light = (SLight *)&_lightArea;
_camera = (SCamera *)&_cameraArea;
}
Actor::~Actor() {
delete[] _characterArea;
delete[] _face;
delete[] _textureData;
}
void Actor::initTextures() {
for (int i = 0; i < MAXMAT; ++i)
_textureArea[i].clear();
// head
_textureArea[0].set(300 / 2, 208 / 2, _textureData);
// body
_textureArea[1].set(300, 300, _textureData + (300 * 208 / 4));
// arms
_textureArea[2].set(300, 150, _textureData + (300 * 208 / 4) + (300 * 300));
_textures = (STexture *)&_textureArea[0];
}
void Actor::updateStepSound() {
_vm->_soundMgr->soundStep((_area[1] + _area[0]) / 2, (_area[5] + _area[4]) / 2, _curAction, _curFrame);
}
static const float _vertsCorr[104][3] = {
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000001f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, -0.061717f, 0.833191f}, {0.000000f, -0.120163f, 0.330445f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, -0.432022f, 0.216004f}, {0.000000f, -0.030041f, 0.360489f},
{0.310895f, 0.000000f, 0.000000f}, {0.312943f, 0.000000f, 0.000000f},
{0.114858f, 0.000000f, 0.000000f}, {0.000000f, 1.051431f, 0.300415f},
{0.000000f, 0.000000f, 0.246856f}, {0.000000f, 0.120163f, 0.480652f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.180247f, 0.600815f}, {0.000000f, 0.000000f, 0.000000f},
{0.530074f, 0.041892f, 0.670273f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.060081f, 0.540726f}, {0.000000f, -0.318127f, -0.249817f},
{0.000000f, 0.180244f, 0.540741f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, -0.922172f, 0.201188f}, {0.000000f, -0.442684f, -0.328400f},
{0.353384f, 1.047291f, -1.005401f}, {0.000000f, -0.646931f, -0.933030f},
{0.000000f, 2.283107f, -0.420562f}, {0.412281f, -1.633775f, -1.193909f},
{0.312389f, 0.000000f, 0.000000f}, {0.000000f, 0.020947f, -0.083786f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, -1.021390f, -1.141556f},
{0.000000f, 0.020946f, -0.146637f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.020946f, -0.146637f}, {0.000000f, 0.020947f, -0.146637f},
{0.000000f, 0.020946f, -0.083786f}, {0.000000f, 0.020946f, -0.125687f},
{0.000000f, 0.020947f, -0.146637f}, {0.000000f, 0.020947f, -0.125687f},
{0.000000f, 0.020946f, -0.083786f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.020947f, -0.125687f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.020947f, -0.125686f}, {0.000000f, 0.020946f, -0.125687f},
{0.000000f, 0.020946f, -0.083786f}, {0.000000f, 0.020946f, -0.146637f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, -0.061717f, 0.833191f}, {0.000000f, -0.090122f, 0.330460f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, -0.432022f, 0.185150f},
{-0.310895f, 0.000000f, 0.000000f}, {-0.312943f, 0.000001f, 0.000000f},
{-0.114858f, 0.000000f, 0.000000f}, {0.000000f, 1.051431f, 0.270371f},
{0.000000f, -0.030858f, 0.246856f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f},
{-0.647869f, 0.041892f, 0.628372f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, -0.442684f, -0.328400f}, {-0.294485f, 1.026345f, -1.005401f},
{-0.353383f, -1.633775f, -1.214859f}, {-0.312389f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.020947f, -0.146637f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.020946f, -0.146637f},
{0.000000f, 0.020946f, -0.083786f}, {0.000000f, 0.020947f, -0.146637f},
{0.000000f, 0.020947f, -0.125687f}, {0.000000f, 0.020947f, -0.083786f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.020947f, -0.125687f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.020946f, -0.125687f},
{0.000000f, 0.020946f, -0.146637f}, {0.000000f, 0.000000f, 0.000000f},
{0.000000f, 0.000000f, 0.000000f}, {0.000000f, 0.000000f, 0.000000f}};
static const int _vertsCorrList[84] = {
289, 290, 293, 294, 295, 296, 297, 298,
299, 300, 300, 302, 303, 304, 305, 305,
307, 307, 309, 310, 311, 312, 313, 314,
315, 316, 317, 318, 319, 320, 321, 322,
323, 324, 325, 326, 327, 328, 329, 330,
331, 332, 333, 334, 335, 336, 337, 338,
339, 340, 341, 349, 350, 352, 353, 354,
355, 356, 357, 358, 359, 360, 361, 362,
363, 364, 365, 366, 367, 368, 369, 370,
371, 372, 373, 374, 375, 376, 377, 378,
379, 380, 381, 382};
/**********************************************
Microprose head correction
**********************************************/
void Actor::microproseHeadFix(uint32 actionNum) {
static const uint16 idx1 = 306;
static const uint16 idx2 = 348;
static const uint16 idx3 = 288;
double v1[3], v2[3], v[3], q[3], m1[3][3], m2[3][3];
v1[0] = _vertex[idx2]._x - _vertex[idx1]._x;
v1[1] = _vertex[idx2]._y - _vertex[idx1]._y;
v1[2] = _vertex[idx2]._z - _vertex[idx1]._z;
double s = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);
v1[0] /= s;
v1[1] /= s;
v1[2] /= s;
v2[0] = _vertex[idx3]._x - _vertex[idx1]._x;
v2[1] = _vertex[idx3]._y - _vertex[idx1]._y;
v2[2] = _vertex[idx3]._z - _vertex[idx1]._z;
s = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]);
v2[0] /= s;
v2[1] /= s;
v2[2] /= s;
m1[1][0] = v2[1] * v1[2] - v1[1] * v2[2];
m1[1][1] = v2[2] * v1[0] - v1[2] * v2[0];
m1[1][2] = v2[0] * v1[1] - v1[0] * v2[1];
s = sqrt(m1[1][0] * m1[1][0] + m1[1][1] * m1[1][1] + m1[1][2] * m1[1][2]);
m1[1][0] /= s;
m1[1][1] /= s;
m1[1][2] /= s;
m1[2][0] = m1[1][1] * v1[2] - v1[1] * m1[1][2];
m1[2][1] = m1[1][2] * v1[0] - v1[2] * m1[1][0];
m1[2][2] = m1[1][0] * v1[1] - v1[0] * m1[1][1];
s = sqrt(m1[2][0] * m1[2][0] + m1[2][1] * m1[2][1] + m1[2][2] * m1[2][2]);
m1[2][0] /= s;
m1[2][1] /= s;
m1[2][2] /= s;
m1[0][0] = v1[0];
m1[0][1] = v1[1];
m1[0][2] = v1[2];
for (uint i = 0; i < actionNum; ++i) {
SVertex *sv = &_vertex[i * _vertexNum];
v1[0] = sv[idx2]._x - sv[idx1]._x;
v1[1] = sv[idx2]._y - sv[idx1]._y;
v1[2] = sv[idx2]._z - sv[idx1]._z;
s = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);
v1[0] /= s;
v1[1] /= s;
v1[2] /= s;
v2[0] = sv[idx3]._x - sv[idx1]._x;
v2[1] = sv[idx3]._y - sv[idx1]._y;
v2[2] = sv[idx3]._z - sv[idx1]._z;
s = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]);
v2[0] /= s;
v2[1] /= s;
v2[2] /= s;
m2[1][0] = v2[1] * v1[2] - v1[1] * v2[2];
m2[1][1] = v2[2] * v1[0] - v1[2] * v2[0];
m2[1][2] = v2[0] * v1[1] - v1[0] * v2[1];
s = sqrt(m2[1][0] * m2[1][0] + m2[1][1] * m2[1][1] + m2[1][2] * m2[1][2]);
m2[1][0] /= s;
m2[1][1] /= s;
m2[1][2] /= s;
m2[2][0] = m2[1][1] * v1[2] - v1[1] * m2[1][2];
m2[2][1] = m2[1][2] * v1[0] - v1[2] * m2[1][0];
m2[2][2] = m2[1][0] * v1[1] - v1[0] * m2[1][1];
s = sqrt(m2[2][0] * m2[2][0] + m2[2][1] * m2[2][1] + m2[2][2] * m2[2][2]);
m2[2][0] /= s;
m2[2][1] /= s;
m2[2][2] /= s;
m2[0][0] = v1[0];
m2[0][1] = v1[1];
m2[0][2] = v1[2];
v2[0] = sv[idx1]._x;
v2[1] = sv[idx1]._y;
v2[2] = sv[idx1]._z;
v1[0] = _vertex[idx1]._x;
v1[1] = _vertex[idx1]._y;
v1[2] = _vertex[idx1]._z;
for (int j = 279; j < 383; ++j) {
int f;
for (f = 0; f < 84; ++f) {
if (_vertsCorrList[f] == j)
break;
}
if (f == 84)
continue;
v[0] = _vertsCorr[j - 279][0];
v[1] = _vertsCorr[j - 279][2];
v[2] = _vertsCorr[j - 279][1];
q[0] = 0.0;
q[1] = 0.0;
q[2] = 0.0;
for (int d = 0; d < 3; ++d) {
for (int c = 0; c < 3; ++c)
q[c] += m1[c][d] * v[d];
}
v[0] = 0.0;
v[1] = 0.0;
v[2] = 0.0;
for (int d = 0; d < 3; ++d) {
for (int c = 0; c < 3; ++c)
v[c] += m2[d][c] * q[d];
}
if (i < 42) {
sv[j]._x += _vertsCorr[j - 279][0];
sv[j]._y += _vertsCorr[j - 279][2];
sv[j]._z += _vertsCorr[j - 279][1];
} else {
sv[j]._x += v[0];
sv[j]._y += v[1];
sv[j]._z += v[2];
}
}
}
}
void Actor::readModel(const char *filename) {
Common::SeekableReadStreamEndian *ff = _vm->readEndian(_vm->_dataFile.createReadStreamForMember(filename));
if (ff == nullptr)
error("readModel - Error opening file %s", filename);
uint32 actionNum = ff->readUint32();
_vertexNum = ff->readUint32();
_characterArea = new SVertex[_vertexNum * actionNum];
for (uint i = 0; i < _vertexNum * actionNum; ++i) {
_characterArea[i]._x = ff->readFloat();
_characterArea[i]._y = ff->readFloat();
_characterArea[i]._z = ff->readFloat();
_characterArea[i]._nx = ff->readFloat();
_characterArea[i]._ny = ff->readFloat();
_characterArea[i]._nz = ff->readFloat();
}
_vertex = _characterArea;
_faceNum = ff->readUint32();
delete ff;
ff = _vm->readEndian(_vm->_dataFile.createReadStreamForMember("mat.tex"));
if (ff == nullptr)
error("readModel - Error opening file mat.tex");
_vm->_graphicsMgr->readTexture(ff);
for (uint16 i = 0; i < MAXFACE; ++i) {
for (uint16 j = 0; j < 3; ++j) {
_textureCoord[i][j][0] = ff->readSint16();
_textureCoord[i][j][1] = ff->readSint16();
}
}
_face = new SFace[_faceNum];
for (uint i = 0; i < _faceNum; ++i) {
_face[i]._a = ff->readUint16();
_face[i]._b = ff->readUint16();
_face[i]._c = ff->readUint16();
_face[i]._mat = ff->readUint16();
}
delete ff;
_curFrame = 0;
_curAction = hSTAND;
microproseHeadFix(actionNum);
}
void Actor::syncGameStream(Common::Serializer &ser) {
float unused = 0;
ser.syncAsFloatLE(_px);
ser.syncAsFloatLE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);
ser.syncAsFloatLE(_pz);
ser.syncAsFloatLE(_dx);
ser.syncAsFloatLE(_dz);
ser.syncAsFloatLE(_theta);
}
void Actor::actorDoAction(int action) {
if (action > hLAST)
error("error in actorDoAction, invalid action (should be called as an animation)");
_vm->_pathFind->_curStep = 1;
float px = _px + _dx;
float pz = _pz + _dz;
float theta = _theta;
_vm->_pathFind->reset(0, px, pz, theta);
float t = ((270.0f - theta) * M_PI * 2) / 360.0f;
float ox = cos(t);
float oz = sin(t);
SVertex *v = _characterArea;
float firstFrame = frameCenter(v);
int cfp = 0;
int cur = 0;
while (cur < action)
cfp += defActionLen[cur++];
v = &_characterArea[cfp * _vertexNum];
if (action == hWALKOUT)
v = &_characterArea[_vertexNum];
else if (action == hLAST)
v = _characterArea;
int len = defActionLen[action];
int stepIdx;
for (stepIdx = _vm->_pathFind->_curStep; stepIdx < len + _vm->_pathFind->_curStep; ++stepIdx) {
float curLen = frameCenter(v) - firstFrame;
SStep *curStep = &_vm->_pathFind->_step[stepIdx];
curStep->_dx = curLen * ox;
curStep->_dz = curLen * oz;
curStep->_px = px;
curStep->_pz = pz;
curStep->_curAction = action;
curStep->_curFrame = stepIdx - _vm->_pathFind->_curStep;
curStep->_theta = theta;
curStep->_curPanel = _vm->_pathFind->_curPanel;
v += _vertexNum;
if (action == hLAST)
v = _characterArea;
}
_vm->_pathFind->reset(stepIdx, px, pz, theta);
_vm->_pathFind->_lastStep = stepIdx; // Last step
// Starts action
if (_vm->_obj[_vm->_curObj].isFlagRoomOut())
_vm->_scheduler->doEvent(MC_CHARACTER, ME_CHARACTERGOTOEXIT, MP_DEFAULT, _vm->_obj[_vm->_curObj]._goRoom, 0, _vm->_obj[_vm->_curObj]._ninv, _vm->_curObj);
else
_vm->_scheduler->doEvent(MC_CHARACTER, ME_CHARACTERDOACTION, MP_DEFAULT, 0, 0, 0, 0);
}
void Actor::actorStop() {
_vm->_pathFind->reset(0, _px + _dx, _pz + _dz, _theta);
_vm->_pathFind->_characterGoToPosition = -1;
_vm->_pathFind->_curStep = 0;
_vm->_pathFind->_lastStep = 0;
}
void Actor::read3D(Common::SeekableReadStreamEndian *ff) {
// read rooms and lights
SCamera *cam = _camera;
cam->_ex = ff->readFloat();
cam->_ey = ff->readFloat();
cam->_ez = ff->readFloat();
for (int i = 0; i < 3; ++i)
cam->_e1[i] = ff->readFloat();
for (int i = 0; i < 3; ++i)
cam->_e2[i] = ff->readFloat();
for (int i = 0; i < 3; ++i)
cam->_e3[i] = ff->readFloat();
cam->_fovX = ff->readFloat();
cam->_fovY = ff->readFloat();
_lightNum = ff->readUint32();
if (_lightNum > MAXLIGHT)
error("read3D(): Too many lights");
for (uint32 i = 0; i < _lightNum; ++i) {
_light[i]._x = ff->readFloat();
_light[i]._y = ff->readFloat();
_light[i]._z = ff->readFloat();
_light[i]._dx = ff->readFloat();
_light[i]._dy = ff->readFloat();
_light[i]._dz = ff->readFloat();
_light[i]._inr = ff->readFloat();
_light[i]._outr = ff->readFloat();
_light[i]._hotspot = ff->readByte();
_light[i]._fallOff = ff->readByte();
_light[i]._inten = ff->readSByte();
_light[i]._position = ff->readSByte();
}
}
float Actor::frameCenter(SVertex *v) {
return (-v[86]._z - v[164]._z) / 2.0;
}
bool Actor::actorRectIsValid() const {
return _area[0] < _area[1] && _area[2] < _area[3];
}
Common::Rect Actor::getActorRect() const {
return Common::Rect(_area[0], _area[2], _area[1], _area[3]);
}
} // End of namespace Trecision

86
engines/trecision/actor.h Normal file
View File

@@ -0,0 +1,86 @@
/* 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 TRECISION_ACTOR_H
#define TRECISION_ACTOR_H
#include "trecision/struct.h"
#include "common/scummsys.h"
#include "common/serializer.h"
namespace Trecision {
#define MAXLIGHT 40
class TrecisionEngine;
class Actor {
TrecisionEngine *_vm;
SLight _lightArea[MAXLIGHT];
SCamera _cameraArea;
uint8 *_textureData;
STexture _textureArea[MAXMAT];
void initTextures();
void readModel(const char *filename);
void microproseHeadFix(uint32 actionNum);
public:
Actor(TrecisionEngine *vm);
~Actor();
SVertex *_characterArea;
SVertex *_vertex;
SFace *_face;
SLight *_light;
SCamera *_camera;
STexture *_textures;
int16 _textureCoord[MAXFACE][3][2];
uint32 _vertexNum;
uint32 _faceNum;
uint32 _lightNum;
float _px, _pz;
float _dx, _dz;
float _theta;
int _area[6];
int _curFrame;
int _curAction;
void syncGameStream(Common::Serializer &ser);
void actorDoAction(int action);
void actorStop();
void read3D(Common::SeekableReadStreamEndian *ff);
float frameCenter(SVertex *v);
void updateStepSound();
bool actorRectIsValid() const;
Common::Rect getActorRect() const;
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,578 @@
/* 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/config-manager.h"
#include "common/events.h"
#include "common/file.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "audio/decoders/raw.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/sound.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
AnimManager::AnimManager(TrecisionEngine *vm) : _vm(vm) {
for (int i = 0; i < MAXACTIVEANIM; ++i) {
_animations[i] = nullptr;
_playingAnims[i] = 0;
}
for (int i = 0; i < MAXANIM; ++i) {
_animTab[i]._flag = 0;
_animTab[i]._name[0] = '\0';
}
_curCD = 1;
swapCD(_curCD);
_bgAnimRestarted = false;
}
AnimManager::~AnimManager() {
for (int i = 0; i < MAXACTIVEANIM; ++i) {
delete _animations[i];
_animations[i] = nullptr;
_animFile[i].close();
}
}
void AnimManager::playMovie(const Common::Path &filename, int startFrame, int endFrame, bool singleChoice) {
NightlongVideoDecoder *videoDecoder;
if (!_vm->isAmiga())
videoDecoder = new NightlongSmackerDecoder();
else
videoDecoder = new NightlongAmigaDecoder();
if (!videoDecoder->loadFile(filename)) {
warning("playMovie: File %s not found", filename.toString().c_str());
delete videoDecoder;
_vm->_dialogMgr->afterChoice();
return;
}
Common::Event event;
bool skipVideo = false;
uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
_vm->_drawText._text.clear();
videoDecoder->start();
// WORKAROUND: If the video has a single choice, and it starts from
// the beginning, ignore the calculated end frame and play all of it
if (singleChoice && startFrame < 10 && endFrame < (int)videoDecoder->getFrameCount() - 1)
endFrame = videoDecoder->getFrameCount() - 1;
setVideoRange(videoDecoder, startFrame, endFrame);
while (!_vm->shouldQuit() && startFrame != endFrame && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
drawFrame(videoDecoder, x, y, true);
}
while (_vm->getEventManager()->pollEvent(event)) {
if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == kActionSkipVideo)
skipVideo = true;
}
g_system->delayMillis(10);
}
delete videoDecoder;
_vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
_vm->freeKey();
_vm->_dialogMgr->afterChoice();
}
void AnimManager::setVideoRange(NightlongVideoDecoder *smkDecoder, int &startFrame, int &endFrame) {
// Trecision starts at 1 but ScummVM starts at 0
startFrame = CLIP<int32>(startFrame - 1, 0, smkDecoder->getFrameCount() - 1);
endFrame = CLIP<int32>(endFrame - 1, 0, smkDecoder->getFrameCount() - 1);
// If choices are attached
if (startFrame > 0 && startFrame > smkDecoder->getCurFrame()) {
smkDecoder->forceSeekToFrame(startFrame - 1);
}
smkDecoder->setEndFrame(endFrame);
}
void AnimManager::drawFrame(NightlongVideoDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen) {
const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
if (frame) {
Graphics::Surface *frame16 = frame->convertTo(g_system->getScreenFormat(), smkDecoder->getPalette());
drawFrameSubtitles(frame16, smkDecoder->getCurFrame());
g_system->copyRectToScreen(frame16->getPixels(), frame16->pitch, x, y, frame16->w, frame16->h);
frame16->free();
delete frame16;
if (updateScreen)
_vm->_system->updateScreen();
}
}
void AnimManager::drawFrameSubtitles(Graphics::Surface *surface, int frameNum) {
if (!ConfMan.getBool("subtitles"))
return;
_vm->_dialogMgr->dialogHandler(frameNum);
if (_vm->_drawText._text.empty())
return;
// Subtitles can be placed in different coordinates in the video,
// which are set inside dialogHandler(), but are then reset to
// fixed coordinates
_vm->_drawText._rect.left = 20;
_vm->_drawText._rect.top = 380 - TOP;
_vm->_drawText._rect.setWidth(MAXX - 40);
_vm->_drawText._rect.setHeight(_vm->_drawText.calcHeight(_vm));
_vm->_drawText._subtitleRect = Common::Rect(MAXX, MAXY);
_vm->_drawText.draw(_vm, false, surface);
}
void AnimManager::openSmkAnim(int slot, const Common::Path &name) {
for (int i = 0; i < 3; i++) {
// Open the animation, or swap the 3 CDs to find it
if (_animFile[slot].hasFile(name)) {
openSmk(slot, name);
return;
}
_curCD = _curCD < 3 ? _curCD + 1 : 1;
swapCD(_curCD);
}
error("openSmkAnim(): File %s not found", name.toString().c_str());
}
void AnimManager::openSmk(int slot, const Common::Path &name) {
Common::SeekableReadStream *stream =_animFile[slot].createReadStreamForMember(name);
if (!stream) {
warning("Can't open SMK file");
closeSmk(slot);
return;
}
if (!_vm->isAmiga()) {
_animations[slot] = new NightlongSmackerDecoder();
if (!_animations[slot]->loadStream(stream)) {
warning("Invalid SMK file");
closeSmk(slot);
return;
}
} else {
NightlongAmigaDecoder *amigaDecoder = new NightlongAmigaDecoder();
_animations[slot] = amigaDecoder;
if (!_animations[slot]->loadStream(stream)) {
warning("Invalid SMK file");
closeSmk(slot);
return;
}
Common::String baseName("a" + name.baseName());
Common::Path audioPath = name.getParent().appendComponent(baseName);
if (Common::File::exists(audioPath)) {
amigaDecoder->addAudioSideTrack(audioPath);
}
}
_animations[slot]->start();
}
void AnimManager::closeSmk(int slot) {
delete _animations[slot];
_animations[slot] = nullptr;
}
void AnimManager::smkGoto(int slot, int frame) {
if (_animations[slot] == nullptr)
return;
_animations[slot]->forceSeekToFrame(frame);
}
void AnimManager::smkToggleTrackAudio(int slot, int track, bool on) {
if (_animations[slot] == nullptr)
return;
_animations[slot]->muteTrack(track, !on);
}
void AnimManager::smkToggleAudio(int slot, bool on) {
if (_animations[slot] == nullptr)
return;
_animations[slot]->setMute(!on);
}
int16 AnimManager::smkCurFrame(int slot) {
if (_animations[slot] == nullptr)
return -1;
return _animations[slot]->getCurFrame();
}
void AnimManager::startSmkAnim(uint16 animation) {
int slot;
uint16 animFlag = _animTab[animation]._flag;
// choose the buffer to use
if (animFlag & SMKANIM_BKG)
slot = kSmackerBackground;
else if (animFlag & SMKANIM_ICON)
slot = kSmackerIcon;
else
slot = kSmackerAction;
smkStop(slot);
_playingAnims[slot] = animation;
// choose how to open
if (slot == kSmackerBackground) {
openSmkAnim(kSmackerBackground, _animTab[animation]._name);
_bgAnimRestarted = false;
toggleMuteBgAnim(animation);
} else if (slot == kSmackerIcon) {
openSmkAnim(kSmackerIcon, _animTab[animation]._name);
} else {
uint32 st = _vm->readTime();
openSmkAnim(kSmackerAction, _animTab[animation]._name);
_vm->_nextRefresh += _vm->readTime() - st; // fixup opening time
}
}
void AnimManager::toggleMuteBgAnim(uint16 animation) {
const bool area1Shown = _animTab[animation].isAnimAreaShown(1);
const bool area2Shown = _animTab[animation].isAnimAreaShown(2);
const bool area4Shown = _animTab[animation].isAnimAreaShown(4);
NightlongVideoDecoder *decoder = _animations[kSmackerBackground];
if (decoder == nullptr)
return;
// Turns off when not needed
if (animation == aBKG11 && !area1Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG14 && !area1Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG1C && _vm->_obj[oFAX17].isFlagExtra()) {
_animTab[animation].toggleAnimArea(1, false);
decoder->muteTrack(1, true);
} else if (animation == aBKG1D && !area1Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG22 && !area1Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG48 && !area1Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG4P && !area1Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG28 && area4Shown)
decoder->muteTrack(1, true);
else if (animation == aBKG37 && !_vm->_room[_vm->_curRoom].hasExtra())
decoder->muteTrack(1, true);
else if (animation == aBKG2E && area2Shown)
decoder->muteTrack(2, true);
else if (animation == aBKG2G && _vm->_dialogMgr->isDialogFinished(556))
decoder->muteTrack(2, true);
else if (animation == aBKG34 && // If it's BKG 34 and
(_vm->_dialogMgr->isDialogFinished(616) || // if the FMV is already done or
_vm->isObjectVisible(oTUBOT34) || // if the whole tube is available or
_vm->isObjectVisible(oTUBOFT34) || // if the outside of the tube is available or
_vm->isObjectVisible(oVALVOLAC34))) // if the valve is closed
decoder->muteTrack(2, true);
}
void AnimManager::smkStop(uint16 slot) {
_playingAnims[slot] = 0;
closeSmk(slot);
_vm->_lightIcon = 0xFF;
}
void AnimManager::stopAllSmkAnims() {
for (int slot = 0; slot < MAXACTIVEANIM; slot++) {
if (_playingAnims[slot])
smkStop(slot);
}
}
void AnimManager::startFullMotion() {
stopAllSmkAnims();
_vm->_flagDialogActive = true;
_vm->_flagShowCharacter = false;
_vm->_textStatus = TEXT_OFF;
_vm->_inventoryStatus = INV_OFF;
_vm->_inventoryCounter = INVENTORY_HIDE;
_vm->_textMgr->clearTextStack();
_vm->_graphicsMgr->clearScreen();
_vm->_scheduler->resetQueues();
_vm->_actor->actorStop();
_vm->_graphicsMgr->hideCursor();
}
void AnimManager::stopFullMotion() {
const uint16 curDialog = _vm->_dialogMgr->getCurDialog();
_vm->_flagDialogActive = false;
_vm->_flagDialogMenuActive = false;
_vm->_flagSomeoneSpeaks = false;
_vm->_lightIcon = 0xFF;
_vm->_graphicsMgr->showCursor();
if (curDialog == dFCRED) {
_vm->quitGame();
return;
}
if (!((curDialog == dSHOPKEEPER1A) && _vm->_dialogMgr->getCurChoice() == 185)) {
if ((curDialog == dF582) || (curDialog == dFLOG) || (curDialog == dINTRO) || (curDialog == dF362) || (curDialog == dC381) || (curDialog == dF381) ||
(curDialog == dF491) || ((curDialog == dC581) && !_vm->_dialogMgr->isDialogFinished(886) && _vm->_dialogMgr->isDialogFinished(258)) ||
((curDialog == dC5A1) && _vm->_room[kRoom5A].hasExtra()))
_vm->_flagShowCharacter = false;
else
_vm->redrawRoom();
if (curDialog == dF582)
_vm->_soundMgr->stopAllExceptMusic();
}
}
void AnimManager::refreshAnim(int box) {
for (int i = 0; i < MAXACTIVEANIM; i++) {
if (_playingAnims[i] != 0 && box == BOX_BACKGROUND && i != kSmackerAction) {
refreshSmkAnim(_playingAnims[i]);
}
}
}
void AnimManager::refreshSmkAnim(uint16 animation) {
if (animation == 0)
return;
if (_animTab[animation]._flag & SMKANIM_ICON) {
drawSmkIconFrame(_vm->_inventoryRefreshStartIcon, animation);
} else if (_animTab[animation]._flag & SMKANIM_BKG) {
drawSmkBackgroundFrame(animation);
handleEndOfVideo(animation, kSmackerBackground);
} else {
drawSmkActionFrame();
handleEndOfVideo(animation, kSmackerAction);
}
for (int32 i = 0; i < MAXAREA; i++) {
if (_animTab[animation].isAnimAreaShown(i + 1) && _animTab[animation]._area[i].bottom != 0) {
_vm->_graphicsMgr->addDirtyRect(_animTab[animation]._area[i], true);
}
}
}
void AnimManager::handleEndOfVideo(int animation, int slot) {
const bool isLoopingOrBackground = (_animTab[animation]._flag & SMKANIM_LOOP) || (_animTab[animation]._flag & SMKANIM_BKG);
if (_animations[slot] == nullptr) {
smkStop(slot);
return;
}
if (!_animations[slot]->endOfFrames())
return;
if (!isLoopingOrBackground) {
smkStop(slot);
_vm->_flagPaintCharacter = true;
} else {
_animations[slot]->rewind();
_vm->_animTypeMgr->init(animation, 0);
_bgAnimRestarted = true;
}
}
static bool rectsIntersect(Common::Rect r1, Common::Rect r2) {
return (r1.left <= r2.right) && (r1.right >= r2.left) && (r1.top <= r2.bottom) && (r1.bottom >= r2.top);
}
bool AnimManager::shouldShowAnim(int animation, Common::Rect curRect) {
for (int32 i = 0; i < MAXAREA; i++) {
const bool intersect = rectsIntersect(_animTab[animation]._area[i], curRect);
if (intersect && !_animTab[animation].isAnimAreaShown(i + 1))
return false;
}
return true;
}
void AnimManager::drawSmkBackgroundFrame(int animation) {
NightlongVideoDecoder *videoDecoder = _animations[kSmackerBackground];
if (videoDecoder == nullptr)
return;
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (!frame)
return;
const Common::Rect *lastRect = videoDecoder->getNextDirtyRect();
const byte *palette = videoDecoder->getPalette();
if (videoDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
_vm->_graphicsMgr->blitToScreenBuffer(frame, 0, TOP, palette, true);
} else {
while (lastRect) {
if (videoDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
Graphics::Surface anim = frame->getSubArea(*lastRect);
_vm->_graphicsMgr->blitToScreenBuffer(&anim, lastRect->left, lastRect->top + TOP, palette, true);
}
lastRect = videoDecoder->getNextDirtyRect();
}
}
}
void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
NightlongVideoDecoder *videoDecoder = _animations[kSmackerIcon];
if (videoDecoder == nullptr)
return;
int stx = ICONMARGSX;
uint a;
for (a = 0; a < ICONSHOWN; ++a) {
if (a + startIcon >= _vm->_inventory.size())
break;
if (_vm->_inventory[a + startIcon] == iconNum - FIRST_INV_ITEM + 1) {
stx = a * ICONDX + ICONMARGSX;
break;
}
}
if (a == ICONSHOWN)
return;
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (!frame)
return;
_vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, videoDecoder->getPalette());
if (videoDecoder->endOfVideo())
videoDecoder->rewind();
}
void AnimManager::drawSmkActionFrame() {
NightlongVideoDecoder *smkDecoder = _animations[kSmackerAction];
if (smkDecoder == nullptr)
return;
const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
if (!frame)
return;
const byte *palette = smkDecoder->getPalette();
if (smkDecoder->getCurFrame() == 0) {
_animRect = *smkDecoder->getNextDirtyRect();
}
if (_animRect.width() > 0 && _animRect.height() > 0) {
Graphics::Surface anim = frame->getSubArea(_animRect);
_vm->_graphicsMgr->blitToScreenBuffer(&anim, _animRect.left, _animRect.top + TOP, palette, false);
_vm->_graphicsMgr->addDirtyRect(_animRect, true);
}
}
void AnimManager::swapCD(int cd) {
Common::Path animFileName(Common::String::format("nlanim.cd%d", cd));
for (uint8 i = 0; i < MAXACTIVEANIM; ++i) {
_animFile[i].close();
_animFile[i].open(_vm, animFileName);
}
}
void AnimManager::syncGameStream(Common::Serializer &ser) {
for (int i = 0; i < MAXANIM; i++) {
SAnim *cur = &_animTab[i];
ser.syncBytes((byte *)cur->_name, 14);
ser.syncAsUint16LE(cur->_flag);
for (uint8 j = 0; j < MAXAREA; ++j) {
ser.syncAsUint16LE(cur->_area[j].left);
ser.syncAsUint16LE(cur->_area[j].top);
ser.syncAsUint16LE(cur->_area[j].right);
ser.syncAsUint16LE(cur->_area[j].bottom);
}
ser.syncAsByte(cur->_nbox);
ser.skip(1, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);
for (uint8 j = 0; j < MAXATFRAME; ++j) {
ser.syncAsByte(cur->_atFrame[j]._type);
ser.syncAsByte(cur->_atFrame[j]._area);
ser.syncAsUint16LE(cur->_atFrame[j]._numFrame);
ser.syncAsUint16LE(cur->_atFrame[j]._index);
}
}
patchAnimTab();
}
void AnimManager::loadAnimTab(Common::SeekableReadStreamEndian *stream) {
for (uint16 i = 0; i < MAXANIM; ++i) {
stream->read(&_animTab[i]._name, 14);
_animTab[i]._flag = stream->readUint16();
for (uint8 j = 0; j < MAXAREA; ++j) {
_animTab[i]._area[j].left = stream->readUint16();
_animTab[i]._area[j].top = stream->readUint16();
_animTab[i]._area[j].right = stream->readUint16();
_animTab[i]._area[j].bottom = stream->readUint16();
}
_animTab[i]._nbox = stream->readByte();
stream->readByte(); // Padding
for (uint8 j = 0; j < MAXATFRAME; ++j) {
_animTab[i]._atFrame[j]._type = stream->readByte();
_animTab[i]._atFrame[j]._area = stream->readByte();
_animTab[i]._atFrame[j]._numFrame = stream->readUint16();
_animTab[i]._atFrame[j]._index = stream->readUint16();
}
}
patchAnimTab();
}
void AnimManager::patchAnimTab() {
_animTab[aBKG28]._area[3].left = 308; // Patch the brazier animation rect in kRoom28 - bug #12628
_animTab[aBKG35]._area[0].right = 200; // Patch the terrorist animation rect in kRoom35
}
} // End of namespace Trecision

View 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 TRECISION_ANIMMANAGER_H
#define TRECISION_ANIMMANAGER_H
#include "trecision/fastfile.h"
#include "trecision/struct.h"
namespace Trecision {
#define MAXANIM 750
#define MAXACTIVEANIM 3
// SMACKER ANIMATION FLAGS
#define SMKANIM_BKG 1
#define SMKANIM_ICON 2
#define SMKANIM_LOOP 4
#define SMKANIM_OLD 8
#define SMKANIM_ON 16
enum SmackerType {
kSmackerBackground = 0, // Scene background animations
kSmackerAction = 1, // Main character action animations
kSmackerIcon = 2 // Smacker inventory animations
};
class TrecisionEngine;
class NightlongVideoDecoder;
class AnimManager {
public:
AnimManager(TrecisionEngine *vm);
~AnimManager();
private:
TrecisionEngine *_vm;
NightlongVideoDecoder *_animations[MAXACTIVEANIM];
uint16 _playingAnims[MAXACTIVEANIM];
FastFile _animFile[MAXACTIVEANIM]; // nlanim.cd1 / nlanim.cd2 / nlanim.cd3
int _curCD;
bool _bgAnimRestarted;
void openSmk(int slot, const Common::Path &name);
void openSmkAnim(int slot, const Common::Path &name);
void toggleMuteBgAnim(uint16 animation);
void closeSmk(int slot);
void drawFrame(NightlongVideoDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen);
void drawFrameSubtitles(Graphics::Surface *surface, int frameNum);
void setVideoRange(NightlongVideoDecoder *smkDecoder, int &startFrame, int &endFrame);
void refreshSmkAnim(uint16 animation);
void handleEndOfVideo(int animation, int slot);
bool shouldShowAnim(int animation, Common::Rect curRect);
void drawSmkBackgroundFrame(int animation);
void drawSmkIconFrame(uint16 startIcon, uint16 iconNum);
void drawSmkActionFrame();
void swapCD(int cd);
void patchAnimTab();
public:
Common::Rect _animRect;
SAnim _animTab[MAXANIM];
void smkGoto(int slot, int frame);
void smkToggleAudio(int slot, bool on);
void smkToggleTrackAudio(int slot, int track, bool on);
int16 smkCurFrame(int slot);
void smkStop(uint16 slot);
void refreshActionAnimation() { refreshSmkAnim(_playingAnims[kSmackerAction]); }
bool isActionActive() const { return _playingAnims[kSmackerAction] != 0; }
void playMovie(const Common::Path &filename, int startFrame = 0, int endFrame = -1, bool singleChoice = false);
void startFullMotion();
void stopFullMotion();
void refreshAnim(int box);
void startSmkAnim(uint16 animation);
void stopAllSmkAnims();
void syncGameStream(Common::Serializer &ser);
void loadAnimTab(Common::SeekableReadStreamEndian *stream);
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,379 @@
/* 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 "trecision/animmanager.h"
#include "trecision/animtype.h"
#include "trecision/dialog.h"
#include "trecision/logic.h"
#include "trecision/pathfinding3d.h"
#include "trecision/scheduler.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
#define ATF_WAITTEXT 1
AnimTypeManager::AnimTypeManager(TrecisionEngine *vm) : _vm(vm) {
for (int i = 0; i < 3; ++i) {
_animType[i]._curFrame = 1;
_animType[i]._lastFrame = 0;
_animType[i]._object = 0;
_animType[i]._status = 0;
_animType[i]._curAnim = nullptr;
}
_oneSpeakDialogCount = 0;
}
AnimTypeManager::~AnimTypeManager() {
}
void AnimTypeManager::executeAtFrameDoit(ATFHandle *h, int doit, uint16 objectId) {
SAnim *anim = &_vm->_animMgr->_animTab[_vm->_room[_vm->_curRoom]._bkgAnim];
switch (doit) {
case fCLROBJSTATUS:
_vm->setObjectVisible(objectId, false);
break;
case fSETOBJSTATUS:
_vm->setObjectVisible(objectId, true);
break;
case fONETIME:
_vm->setObjectAnim(objectId, 0);
break;
case fCREPACCIO:
if (_vm->_room[kRoom2E].hasExtra())
_vm->_obj[oCRACK2E]._position = 7;
else
_vm->_obj[oCRACK2E]._position = 6;
break;
case fSERPVIA:
_vm->_scheduler->doEvent(_vm->_snake52._class, _vm->_snake52._event, _vm->_snake52._priority, _vm->_snake52._u16Param1, _vm->_snake52._u16Param2, _vm->_snake52._u8Param, _vm->_snake52._u32Param);
break;
case fPIRANHA:
_vm->setObjectAnim(oLUCCHETTO53, 0);
_vm->setObjectAnim(oGRATAC53, 0);
_vm->setObjectAnim(oGRATAA53, 0);
_vm->_obj[oLUCCHETTO53]._action = 1240;
_vm->_obj[oGRATAC53]._action = 1243;
_vm->_obj[oGRATAA53]._action = 1246;
_vm->_obj[oLAGO53]._examine = 1237;
break;
case fMOREAU:
_vm->setObjectAnim(oWINDOWB58, 0);
_vm->_obj[oWINDOWB58]._action = 1358;
break;
case fDOOR58:
_vm->_scheduler->leftClick(468, 180 + TOP);
break;
case fHELLEN:
_vm->_scheduler->leftClick(336, 263 + TOP);
break;
case fVALVEON34:
if (!(_vm->_dialogMgr->isDialogFinished(616)) && // if the fmv is not done
(_vm->isObjectVisible(oTUBOA34)) && // if there's a cut pipe
!(_vm->isObjectVisible(oTUBOFT34))) // if there's not tube outside
_vm->_animMgr->smkToggleTrackAudio(0, 2, true);
break;
case fVALVEOFF34:
_vm->_animMgr->smkToggleTrackAudio(0, 2, false);
break;
case fCHARACTEROFF:
_vm->_flagShowCharacter = false;
break;
case fCHARACTERON:
_vm->_flagShowCharacter = true;
break;
case fCHARACTERFOREGROUND:
_vm->_pathFind->setForcedActorPos(BOX_FOREGROUND);
break;
case fCHARACTERBACKGROUND:
_vm->_pathFind->setForcedActorPos(BOX_BACKGROUND);
break;
case fCHARACTERNORM:
_vm->_pathFind->setForcedActorPos(BOX_NORMAL);
break;
case fSETEXTRA:
_vm->_obj[objectId].setFlagExtra(true);
break;
case fCLREXTRA:
_vm->_obj[objectId].setFlagExtra(false);
break;
case fANIMOFF1:
anim->toggleAnimArea(1, false);
if (_vm->_curRoom == kRoom11 ||
_vm->_curRoom == kRoom1D ||
_vm->_curRoom == kRoom14 ||
_vm->_curRoom == kRoom22 ||
_vm->_curRoom == kRoom48 ||
_vm->_curRoom == kRoom4P)
_vm->_animMgr->smkToggleTrackAudio(0, 1, false);
break;
case fANIMOFF2:
anim->toggleAnimArea(2, false);
if (_vm->_curRoom == kRoom2E)
_vm->_animMgr->smkToggleTrackAudio(0, 2, false);
break;
case fANIMOFF3:
anim->toggleAnimArea(3, false);
break;
case fANIMOFF4:
anim->toggleAnimArea(4, false);
if (_vm->_curRoom == kRoom28)
_vm->_animMgr->smkToggleTrackAudio(0, 1, false);
break;
case fANIMON1:
anim->toggleAnimArea(1, true);
if (_vm->_curRoom == kRoom14 || _vm->_curRoom == kRoom1D || _vm->_curRoom == kRoom22 || _vm->_curRoom == kRoom48 || _vm->_curRoom == kRoom4P) {
_vm->_animMgr->smkToggleTrackAudio(0, 1, true);
}
break;
case fANIMON2:
anim->toggleAnimArea(2, true);
if (_vm->_curRoom == kRoom2E)
_vm->_animMgr->smkToggleTrackAudio(0, 2, true);
break;
case fANIMON3:
anim->toggleAnimArea(3, true);
break;
case fANIMON4:
anim->toggleAnimArea(4, true);
break;
case fENDDEMO:
_vm->demoOver();
_vm->quitGame();
break;
case fSTOP2TXT:
h->_status |= ATF_WAITTEXT;
// Sets a flag that is always cleared when you finish speaking
// if the flag is cleared the anim no longer plays
// (to be done in the smacker player)
// also the counters in next() stops
break;
default:
break;
}
}
void AnimTypeManager::processAtFrame(ATFHandle *h, int type, int atf) {
const uint16 index = h->_curAnim->_atFrame[atf]._index;
switch (type) {
case ATFTEXT:
_vm->_textMgr->characterSayInAction(index);
break;
case ATFTEXTACT:
_vm->_textMgr->characterSayInAction(_vm->_obj[h->_object]._action);
break;
case ATFTEXTEX:
_vm->_textMgr->characterSayInAction(_vm->_obj[h->_object]._examine);
break;
case ATFCLR:
_vm->setObjectVisible(index, false);
break;
case ATFCLRI:
_vm->removeIcon(index);
break;
case ATFCEX:
_vm->_obj[h->_object]._examine = index;
break;
case ATFCACT:
_vm->_obj[h->_object]._action = index;
break;
case ATFSET:
_vm->setObjectVisible(index, true);
break;
case ATFSETI:
_vm->addIcon(index);
break;
case ATFDO:
executeAtFrameDoit(h, index, h->_object);
break;
case ATFROOM:
_vm->changeRoom(index);
break;
case ATFSETPOS:
_vm->_pathFind->setPosition(index);
break;
case ATFDIALOG:
_vm->_dialogMgr->playDialog(index);
break;
case ATFCOBJANIM:
_vm->_obj[h->_object]._anim = index;
break;
case ATFCOBJBOX:
_vm->_obj[h->_object]._nbox = index;
break;
case ATFCOBJPOS:
_vm->_obj[h->_object]._position = index;
break;
case ATFSETFORE:
_vm->_obj[index]._nbox = BOX_FOREGROUND;
break;
case ATFSETBACK:
_vm->_obj[index]._nbox = BOX_BACKGROUND;
break;
case ATFSWITCH:
_vm->setObjectVisible(index, !_vm->isObjectVisible(index));
break;
case ATFSETROOMT:
_vm->_logicMgr->setupAltRoom(index, true);
break;
case ATFSETROOMF:
_vm->_logicMgr->setupAltRoom(index, false);
break;
case ATFREADBOX:
switch (index) {
case 1: {
const Common::Path filename(Common::String::format("%s.3d", _vm->_room[_vm->_curRoom]._baseName));
_vm->read3D(filename);
_vm->_room[_vm->_curRoom].setExtra(false);
}
break;
case 2: {
const Common::Path filename(Common::String::format("%s2.3d", _vm->_room[_vm->_curRoom]._baseName));
_vm->read3D(filename);
_vm->_room[_vm->_curRoom].setExtra(true);
if (_vm->_curRoom == kRoom37)
_vm->_animMgr->smkToggleTrackAudio(0, 1, true);
} break;
default:
break;
}
break;
case ATFONESPEAK:
switch (index) {
case 1: // Storekeeper's wife
if (_vm->_room[kRoom1D].hasExtra())
break;
// Quotes spoken by the storekeeper's wife while she is in the cellar
_vm->_textMgr->someoneSay(307 + _oneSpeakDialogCount, oDONNA1D);
if (_oneSpeakDialogCount < 6)
++_oneSpeakDialogCount;
break;
case 2: // Storekeeper
// Quote when you enter the liquor store: "Ah, it's you again... look round
// if you want, but don't disturb me, I've got a lot to do"
_vm->_textMgr->someoneSay(1788, ocNEGOZIANTE1A);
break;
default:
break;
}
break;
case ATFEND:
_vm->demoOver();
_vm->quitGame();
break;
default:
break;
}
}
void AnimTypeManager::init(uint16 an, uint16 obj) {
SAnim *anim = &_vm->_animMgr->_animTab[an];
ATFHandle *handle = &_animType[kAnimTypeCharacter];
if (anim->_flag & SMKANIM_BKG)
handle = &_animType[kAnimTypeBackground];
if (anim->_flag & SMKANIM_ICON)
handle = &_animType[kAnimTypeIcon];
handle->_curAnim = anim;
handle->_object = obj ? obj : _vm->_curObj;
handle->_curFrame = 0;
handle->_lastFrame = -1;
handle->_status = 0;
}
void AnimTypeManager::next() {
for (int i = 0; i < 3; ++i) {
if (!(_animType[i]._status & ATF_WAITTEXT) || !_vm->_flagCharacterSpeak)
++_animType[i]._curFrame;
}
}
void AnimTypeManager::end(int type) {
ATFHandle *h = &_animType[type];
SAnim *anim = h->_curAnim;
h->_curFrame = 0;
// if this ATFrame has already been handled
if (h->_curFrame == h->_lastFrame)
return;
h->_lastFrame = h->_curFrame;
for (int32 i = 0; i < MAXATFRAME; ++i) {
// if it's time to run this AtFrame
if (anim->_atFrame[i]._numFrame == 0 && anim->_atFrame[i]._type) {
const uint8 area = anim->_atFrame[i]._area;
if ( area == 0 ||
(area == 1 && anim->isAnimAreaShown(1)) ||
(area == 2 && anim->isAnimAreaShown(2)) ||
(area == 3 && anim->isAnimAreaShown(3)) ||
(area == 4 && anim->isAnimAreaShown(4)))
processAtFrame(h, anim->_atFrame[i]._type, i);
}
}
h->_curAnim = nullptr;
}
void AnimTypeManager::handler(int type) {
ATFHandle *h = &_animType[type];
SAnim *anim = h->_curAnim;
if (anim == nullptr)
return;
if (h->_curFrame == 0)
++h->_curFrame;
// if this ATFrame has already been applied
if (h->_curFrame <= h->_lastFrame)
return;
for (int32 i = 0; i < MAXATFRAME; ++i) {
// if it's time to run this AtFrame
if (anim->_atFrame[i]._numFrame > h->_lastFrame &&
anim->_atFrame[i]._numFrame <= h->_curFrame &&
anim->_atFrame[i]._numFrame != 0) {
const uint8 child = anim->_atFrame[i]._area;
if ( child == 0 ||
(child == 1 && anim->isAnimAreaShown(1)) ||
(child == 2 && anim->isAnimAreaShown(2)) ||
(child == 3 && anim->isAnimAreaShown(3)) ||
(child == 4 && anim->isAnimAreaShown(4)))
processAtFrame(h, anim->_atFrame[i]._type, i);
}
}
// set _lastFrame
h->_lastFrame = h->_curFrame;
}
} // End of namespace Trecision

View File

@@ -0,0 +1,60 @@
/* 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 TRECISION_ANIMTYPE_H
#define TRECISION_ANIMTYPE_H
#include "trecision/struct.h"
namespace Trecision {
class TrecisionEngine;
struct ATFHandle {
int16 _curFrame;
int16 _lastFrame;
uint16 _object;
uint16 _status;
SAnim *_curAnim;
};
class AnimTypeManager {
private:
TrecisionEngine *_vm;
void executeAtFrameDoit(ATFHandle *h, int doit, uint16 obj);
void processAtFrame(ATFHandle *h, int type, int atf);
ATFHandle _animType[3];
int _oneSpeakDialogCount;
public:
AnimTypeManager(TrecisionEngine *vm);
~AnimTypeManager();
void init(uint16 an, uint16 obj);
void next();
void end(int type);
void handler(int type);
};
} // End of namespace Trecision
#endif

View 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 trecision "Trecision Adventure Module" yes "" "" "highres 16bit"

View File

@@ -0,0 +1,194 @@
/* 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/file.h"
#include "gui/debugger.h"
#include "trecision/console.h"
#include "trecision/dialog.h"
#include "trecision/pathfinding3d.h"
#include "trecision/scheduler.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
namespace Trecision {
Console::Console(TrecisionEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("room", WRAP_METHOD(Console, Cmd_Room));
registerCmd("dumpanim", WRAP_METHOD(Console, Cmd_DumpAnim));
registerCmd("dumpfile", WRAP_METHOD(Console, Cmd_DumpFile));
registerCmd("dialog", WRAP_METHOD(Console, Cmd_Dialog));
registerCmd("item", WRAP_METHOD(Console, Cmd_Item));
registerCmd("say", WRAP_METHOD(Console, Cmd_Say));
registerCmd("position", WRAP_METHOD(Console, Cmd_Position));
registerCmd("toggle_object", WRAP_METHOD(Console, Cmd_ToggleObject));
}
Console::~Console() {
}
bool Console::Cmd_Room(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Current room: %d\n", _vm->_curRoom);
debugPrintf("Use %s <roomId> to teleport\n", argv[0]);
return true;
}
const int newRoom = atoi(argv[1]);
_vm->changeRoom(newRoom);
return false;
}
bool Console::Cmd_DumpAnim(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: %s <file name>\n", argv[0]);
return true;
}
FastFile animFile;
Common::Path fileName(argv[1]);
bool found = false;
for (int i = 1; i <= 3; i++) {
Common::Path animFileName(Common::String::format("nlanim.cd%d", i));
animFile.open(_vm, animFileName);
if (animFile.hasFile(fileName)) {
found = true;
break;
}
}
if (!found) {
debugPrintf("File not found\n");
animFile.close();
return true;
}
Common::SeekableReadStream *dataFile = animFile.createReadStreamForMember(fileName);
Common::DumpFile outFile;
Common::Path outName = fileName.append(".dump");
outFile.open(outName);
outFile.writeStream(dataFile, dataFile->size());
outFile.finalize();
outFile.close();
animFile.close();
return true;
}
bool Console::Cmd_DumpFile(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: %s <file name>\n", argv[0]);
return true;
}
Common::Path fileName(argv[1]);
if (!_vm->_dataFile.hasFile(fileName)) {
debugPrintf("File not found\n");
return true;
}
Common::SeekableReadStream *dataFile = fileName.baseName().hasSuffix(".cr") ? _vm->_dataFile.createReadStreamForCompressedMember(fileName) : _vm->_dataFile.createReadStreamForMember(fileName);
Common::DumpFile outFile;
Common::Path outName = fileName.append(".dump");
outFile.open(outName);
outFile.writeStream(dataFile, dataFile->size());
outFile.finalize();
outFile.close();
return true;
}
bool Console::Cmd_Dialog(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Use %s <dialogId> to start a dialog\n", argv[0]);
return true;
}
const int dialogId = atoi(argv[1]);
_vm->_dialogMgr->playDialog(dialogId);
return false;
}
bool Console::Cmd_Item(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Use %s <itemId> to add an item to the inventory\n", argv[0]);
debugPrintf("Use %s <itemId> remove to remove an item from the inventory\n", argv[0]);
return true;
}
const int itemId = atoi(argv[1]);
if (argc >= 3 && !scumm_stricmp(argv[2], "remove")) {
_vm->removeIcon(itemId);
} else {
_vm->addIcon(itemId);
}
return false;
}
bool Console::Cmd_Say(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Use %s <sentenceId> to hear a sentence from Joshua\n", argv[0]);
return true;
}
const uint16 sentenceId = (uint16)atoi(argv[1]);
_vm->_textMgr->characterSay(sentenceId);
return false;
}
bool Console::Cmd_Position(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Use %s <positionId> to set Joshua's position\n", argv[0]);
return true;
}
const uint16 positionId = (uint16)atoi(argv[1]);
_vm->_pathFind->setPosition(positionId);
return false;
}
bool Console::Cmd_ToggleObject(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("Use %s <objectId> <status> to show or hide an object\n", argv[0]);
debugPrintf("Status can be true (or 1) to show an object, or false (or 0) to hide it\n");
return true;
}
const uint16 objectId = (uint16)atoi(argv[1]);
const bool visible = !strcmp(argv[2], "1") || !scumm_stricmp(argv[2], "true");
_vm->setObjectVisible(objectId, visible);
return false;
}
} // End of namespace Trecision

View 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 TRECISION_CONSOLE_H
#define TRECISION_CONSOLE_H
#include "gui/debugger.h"
namespace Trecision {
class TrecisionEngine;
class Console : public GUI::Debugger {
public:
Console(TrecisionEngine *vm);
~Console(void) override;
private:
TrecisionEngine *_vm;
bool Cmd_Room(int argc, const char **argv);
bool Cmd_DumpAnim(int argc, const char **argv);
bool Cmd_DumpFile(int argc, const char **argv);
bool Cmd_Dialog(int argc, const char **argv);
bool Cmd_Item(int argc, const char **argv);
bool Cmd_Say(int argc, const char **argv);
bool Cmd_Position(int argc, const char **argv);
bool Cmd_ToggleObject(int argc, const char **argv);
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,6 @@
begin_section("Trecision");
add_person("Daniel Albano", "SupSuper", "");
add_person("Arnaud Boutonn&eacute;", "Strangerke", "");
add_person("Thomas Fach-Pedersen", "madmoose", "Smacker video support");
add_person("Filippos Karapetis", "bluegr", "");
end_section();

2501
engines/trecision/defines.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,243 @@
/* 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 "common/translation.h"
#include "engines/advancedDetector.h"
#include "trecision/detection.h"
static const PlainGameDescriptor trecisionGames[] = {
{"aot", "Ark of Time"},
{"nl", "Nightlong: Union City Conspiracy"},
{0, 0}
};
namespace Trecision {
#define AD_NL_ENTRY(md5, size) \
{ \
{"data.nl", 0, md5, size}, \
{"nlanim.cd1", 0, nullptr, AD_NO_SIZE}, \
{"nlanim.cd2", 0, nullptr, AD_NO_SIZE}, \
{"nlanim.cd3", 0, nullptr, AD_NO_SIZE}, \
AD_LISTEND \
}
#define AD_NL_DEMO_ENTRY(md5, size) \
{ \
{"data.nl", 0, md5, size}, \
{"nlanim.cd1", 0, nullptr, AD_NO_SIZE}, \
AD_LISTEND \
}
static const ADGameDescription gameDescriptions[] = {
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 436228),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 436598),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 457299),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 436697),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 456209),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 446634),
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 432900),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
// bug #12619
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 429370),
Common::HU_HUN,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
// bug #12619
{
"nl",
0,
AD_NL_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 429731),
Common::HU_HUN,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{
"nl",
"Demo",
AD_NL_DEMO_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 392950),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO0()
},
{
"nl",
"Demo",
AD_NL_DEMO_ENTRY("7665db13ad2a1ceb576531be3e1efb30", 413651),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("2bfc3f5cc1ee5c7e80058db853296416", 436807),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("2bfc3f5cc1ee5c7e80058db853296416", 457508),
Common::DE_DEU,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("2bfc3f5cc1ee5c7e80058db853296416", 436842),
Common::ES_ESP,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("2bfc3f5cc1ee5c7e80058db853296416", 456354),
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO0()
},
{
"nl",
0,
AD_NL_ENTRY("2bfc3f5cc1ee5c7e80058db853296416", 446779),
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO0()
},
{
"aot",
MetaEngineDetection::GAME_NOT_IMPLEMENTED,
{
{"dialogue.dat", 0, "afc71fe29b1be3a9b145b8d61dfa4539", 166155130},
{"sentence.dat", 0, "f38afcd22e7de14f9a2343e911eaa126", 75668232},
},
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_UNSUPPORTED,
GUIO0()
},
AD_TABLE_END_MARKER
};
} // End of namespace Trecision
static const char *const directoryGlobs[] = {
"autorun",
"data",
0
};
class TrecisionMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
TrecisionMetaEngineDetection() : AdvancedMetaEngineDetection(Trecision::gameDescriptions, trecisionGames) {
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
_guiOptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD);
}
const char *getName() const override {
return "trecision";
}
const char *getEngineName() const override {
return "Trecision Adventure Module";
}
const char *getOriginalCopyright() const override {
return "(C) 1993-98 Trecision S.p.A.";
}
};
REGISTER_PLUGIN_STATIC(TRECISION_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, TrecisionMetaEngineDetection);

View File

@@ -0,0 +1,27 @@
/* 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 TRECISION_DETECTION_H
#define TRECISION_DETECTION_H
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
#endif

View File

@@ -0,0 +1,796 @@
/* 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 "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/logic.h"
#include "trecision/pathfinding3d.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
void Dialog::clear() {
_flag = 0;
_interlocutor = 0;
memset(_startAnim, 0, 14);
_startLen = 0;
_firstChoice = 0;
_choiceNumb = 0;
for (uint16 i = 0; i < MAXNEWSMKPAL; ++i)
_newPal[i] = 0;
}
void DialogSubTitle::clear() {
_sentence = 0;
_x = _y = 0;
_color = 0;
_startFrame = 0;
_length = 0;
}
void DialogChoice::clear() {
_flag = 0;
_sentenceIndex = 0;
_firstSubTitle = _subTitleNumb = 0;
for (int i = 0; i < MAXDISPCHOICES; ++i) {
_on[i] = _off[i] = 0;
}
_startFrame = 0;
_nextDialog = 0;
}
/**************************************************************/
DialogManager::DialogManager(TrecisionEngine *vm) : _vm(vm) {
_curDialog = 0;
_curChoice = 0;
_curSubTitle = 0;
_curDispChoice = 0;
_curPos = -1;
_lastPos = -1;
for (int i = 0; i < MAXDIALOG; ++i)
_dialog[i].clear();
for (int i = 0; i < MAXCHOICE; ++i)
_choice[i].clear();
for (int i = 0; i < MAXSUBTITLES; ++i)
_subTitles[i].clear();
for (int i = 0; i < MAXDISPCHOICES; ++i)
_dispChoice[i] = 0;
}
DialogManager::~DialogManager() {}
void DialogManager::dialogPrint(int x, int y, int c, const Common::String &txt) {
SDText curChoice;
curChoice.set(
Common::Rect(x, y, _vm->textLength(txt) + x, y),
Common::Rect(0, 0, MAXX, MAXY),
c,
txt
);
curChoice.draw(_vm);
}
void DialogManager::showChoices(uint16 i) {
assert(i < MAXDIALOG);
Dialog *dialog = &_dialog[i];
int x = 10;
int y = 5;
_curPos = -1;
_lastPos = -1;
_vm->_graphicsMgr->clearScreenBufferTop();
for (int c = 0; c < MAXDISPCHOICES; ++c)
_dispChoice[c] = 0;
_curDispChoice = 0;
for (int c = dialog->_firstChoice; c < dialog->_firstChoice + dialog->_choiceNumb; ++c) {
if (isChoiceVisible(c)) {
_dispChoice[_curDispChoice] = c;
++_curDispChoice;
dialogPrint(x, y, HWHITE, _vm->_sentence[_choice[c]._sentenceIndex]);
y += CARHEI;
}
}
_vm->_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
_vm->_flagDialogMenuActive = true;
_vm->_graphicsMgr->showCursor();
}
void DialogManager::updateChoices(int16 dmx, int16 dmy) {
if ((dmy >= MAXDISPCHOICES) && (dmy < CARHEI * _curDispChoice + 5))
_curPos = (dmy - 5) / CARHEI;
else
_curPos = -1;
if ((_curPos != _lastPos) && ((_curPos != -1) || (_lastPos != -1))) {
for (int c = 0; c < MAXDISPCHOICES; ++c) {
if (_dispChoice[c] != 0) {
if (c == _curPos)
dialogPrint(10, 5 + c * CARHEI, HGREEN, _vm->_sentence[_choice[_dispChoice[c]]._sentenceIndex]);
else
dialogPrint(10, 5 + c * CARHEI, HWHITE, _vm->_sentence[_choice[_dispChoice[c]]._sentenceIndex]);
}
}
_vm->_graphicsMgr->copyToScreen(0, 5, MAXX, (_curDispChoice)*CARHEI + 5);
}
_lastPos = _curPos;
}
void DialogManager::selectChoice(int16 dmx, int16 dmy) {
updateChoices(dmx, dmy);
if (_curPos != -1) {
_vm->_flagDialogMenuActive = false;
playChoice(_dispChoice[_curPos], false);
}
}
void DialogManager::playDialog(uint16 i) {
_vm->closeInventoryImmediately();
_curDialog = i;
_curChoice = 0;
_curSubTitle = 0;
if (_curDialog == dSHOPKEEPER1A)
_dialog[_curDialog]._startLen = 0;
_vm->_animMgr->startFullMotion();
bool skip = false;
int curChoice = 0;
for (int c = _dialog[_curDialog]._firstChoice; c < _dialog[_curDialog]._firstChoice + _dialog[_curDialog]._choiceNumb; ++c) {
if (isChoiceVisible(c))
++curChoice;
}
if ((_curDialog == dC581 && isChoiceVisible(262)) ||
(_curDialog == dC581 && curChoice == 1) ||
(_curDialog == dSHOPKEEPER1A && curChoice == 1))
skip = true;
// if there's a pre-dialog
if (_dialog[i]._startLen > 0 && !skip) {
_vm->_animMgr->playMovie(_dialog[i]._startAnim, 1, _dialog[i]._startLen);
} else {
_vm->_animMgr->smkToggleAudio(1, false);
afterChoice();
}
if (_curDialog == dSHOPKEEPER1A)
_dialog[_curDialog]._startLen = 1;
}
void DialogManager::toggleChoice(uint16 choice, bool enable) {
if (enable)
_choice[choice]._flag &= ~DLGCHOICE_HIDE;
else
_choice[choice]._flag |= DLGCHOICE_HIDE;
}
void DialogManager::clearExitFlag(uint16 choice) {
_choice[choice]._flag &= ~DLGCHOICE_EXITDLG;
}
bool DialogManager::isChoiceVisible(uint16 choice) const {
return !(_choice[choice]._flag & DLGCHOICE_HIDE);
}
bool DialogManager::isDialogFinished(uint16 choice) const {
return _choice[choice]._flag & kObjFlagDone;
}
void DialogManager::afterChoice() {
Dialog *dialog = &_dialog[_curDialog];
_vm->_graphicsMgr->clearScreenBufferTop();
_vm->_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
switch (_curDialog) {
case dTRAMP171:
if (_curChoice == 80) {
_vm->_obj[ocTRAMP17]._action = 213;
_vm->_obj[ocTRAMP17].setFlagPerson(false);
} else if (_curChoice == 77) {
_vm->_obj[ocTRAMP17]._action = 211;
_vm->_obj[ocTRAMP17].setFlagPerson(false);
}
break;
case dTRAMP1714:
if (_curChoice == 106) {
_vm->_obj[ocTRAMP17]._action = 213;
_vm->_obj[ocTRAMP17].setFlagPerson(false);
}
break;
case dTRAMP1713:
if (_curChoice == 91) {
_vm->_obj[ocTRAMP17]._action = 212;
_vm->_obj[ocTRAMP17].setFlagPerson(false);
}
break;
case dTRAMP1716:
if (_curChoice == 122) {
_vm->_obj[ocTRAMP17]._action = 212;
_vm->_obj[ocTRAMP17].setFlagPerson(false);
}
break;
case dTRAMP1717:
if (_curChoice == 136) {
_vm->setObjectVisible(ocTRAMP17, false);
_vm->setObjectVisible(oTRAMPD17, true);
_vm->_room[kRoom17]._bkgAnim = aBKG17B;
_vm->addIcon(kItemSkate);
} else if (_curChoice == 137) {
_vm->_obj[ocTRAMP17].setFlagPerson(true);
}
break;
case dGUARDIAN18:
if ((_curChoice == 151) || (_curChoice == 152)) {
_vm->_inventoryObj[kItemRubysPhoto]._action = 1465;
_vm->_obj[oTESSERA1A]._action = 238;
if (_vm->_obj[oTESSERA1A].isFlagExtra()) {
toggleChoice(154, true);
toggleChoice(153, false);
} else
toggleChoice(153, true);
} else if (_curChoice == 154) {
if (_vm->_obj[oTESSERA1A].isFlagExtra())
toggleChoice(183, true);
} else if (_curChoice == 155)
_vm->_obj[ocGUARD18]._action = 228;
break;
case dF213B:
case dF213:
_vm->_logicMgr->setupAltRoom(kRoom21, true);
break;
case dF212B:
case dF212:
_vm->_logicMgr->setupAltRoom(kRoom21, false);
break;
case dF321:
_vm->removeIcon(kItemMakeshiftTorch);
_vm->_flagShowCharacter = false;
break;
case dF4A3:
_vm->_obj[oCHOCOLATES4A]._examine = 1105;
_vm->_obj[oCHOCOLATES4A]._action = 1106;
_vm->_obj[oDOORC4A]._action = 1118;
_vm->_animMgr->_animTab[aBKG4A].toggleAnimArea(1, false);
_vm->setObjectVisible(ocHELLEN4A, false);
_vm->setObjectVisible(oHELLENA4A, true);
break;
case dC581:
_vm->setObjectVisible(oWINDOWB58, true);
if (_curChoice == 262)
_vm->_obj[oKEYBOARD56]._examine = 1307;
break;
case dF542:
_vm->setObjectVisible(oGRATAC54, false);
_vm->setObjectVisible(oDOORC54, false);
_vm->setObjectVisible(oLAVATRICEL54, false);
_vm->setObjectVisible(oLAVATRICEF54, false);
_vm->setObjectVisible(oGRATAA54, true);
_vm->setObjectVisible(oCHIAVI54, true);
_vm->setObjectVisible(od54TO55, true);
break;
default:
break;
}
// If the player chose to exit the dialog
if (_choice[_curChoice]._flag & DLGCHOICE_EXITDLG) {
_vm->_animMgr->stopFullMotion();
switch (_curDialog) {
case dPOLIZIOTTO16:
if ((isDialogFinished(61)) && (isDialogFinished(62)) && _vm->_obj[ocPOLIZIOTTO16].isFlagExtra())
_vm->setObjectVisible(ocPOLIZIOTTO16, false);
break;
case dTRAMP171:
if (_curChoice == 77) {
_vm->_obj[ocTRAMP17]._action = 211;
_vm->_obj[ocTRAMP17].setFlagPerson(false);
} else if (_curChoice == 80)
_vm->_obj[ocTRAMP17]._action = 213;
else if (_curChoice == 122)
_vm->_obj[ocTRAMP17]._action = 211;
break;
case dGUARDIAN18:
if (_curChoice == 152)
_vm->setObjectVisible(ocGUARD18, false);
else if (_curChoice == 155)
_vm->startCharacterAction(a184ENTRACLUB, kRoom19, 2, 0);
break;
case dEVA19:
_vm->_obj[oDOORC18].setFlagRoomOut(false);
_vm->_obj[oDOORC18]._action = 221;
_vm->_obj[ocEVA19]._action = 1999;
_vm->_obj[ocEVA19].setFlagPerson(false);
break;
case dSHOPKEEPER1A:
if (_curChoice == 185) {
_vm->changeRoom(kRoom18, a1810ENTRADALNEGOZIO, 10);
_vm->_obj[oDOORN18].setFlagRoomOut(false);
_vm->_obj[oDOORN18]._action = 218;
_vm->setObjectAnim(oDOORN18, 0);
} else if (_curChoice == 183)
_vm->_obj[oTESSERA1A]._action = 239;
break;
case dF181:
_vm->setObjectVisible(oRETE17, true);
_vm->_obj[oDOORA17]._examine = 196;
_vm->_obj[oDOORUA17]._examine = 187;
_vm->_obj[oDOORUB17]._examine = 192;
_vm->_obj[oDOORA17]._action = 188;
_vm->_obj[oDOORUA17]._action = 193;
_vm->_obj[oDOORUB17]._action = 197;
_vm->setObjectVisible(oFINGERPAD17, false);
_vm->_room[kRoom17].setDone(false);
_vm->_room[kRoom17].setExtra(true);
break;
case dF1C1:
_vm->_textMgr->characterSay(kSentenceMapZoo);
break;
case dF1D1:
_vm->_textMgr->characterSay(kSentenceItWorked);
break;
case dF2E1:
_vm->_textMgr->characterSay(kSentenceGoodDeterrent);
_vm->_obj[oCATWALKA2E]._action = 622;
break;
case dF2E2:
_vm->_textMgr->characterSay(kSentenceWastedCritter);
_vm->_inventoryObj[kItemMicrowaveGun]._examine = 1562;
break;
case dF231:
_vm->_obj[od21TO23]._goRoom = kRoom23B;
_vm->_obj[od24TO23]._goRoom = kRoom23B;
break;
case dF291:
_vm->_obj[oSWITCH29]._action = 479;
_vm->_obj[od22TO29]._goRoom = kRoom29L;
_vm->_obj[od2ATO29]._goRoom = kRoom29L;
_vm->setObjectVisible(od22TO29, false);
_vm->setObjectVisible(od22TO29I, true);
break;
case dF2G1:
_vm->_obj[oPANELM2G]._action = 660;
_vm->_textMgr->characterSay(kSentenceHopeDidntWasteTheKid);
break;
case dF2G2:
_vm->_obj[od26TO2G]._goRoom = kRoom2GV;
_vm->replaceIcon(kItemMinicom, kItemDamagedMinicom);
break;
case dF321:
_vm->startCharacterAction(a3111TRASCINA, 0, 11, 0);
break;
case dF331:
_vm->_obj[oTUBET33]._area = Common::Rect(0, 0, 0, 0);
_vm->_textMgr->characterSay(kSentenceSecretPassage);
break;
case dF362:
playDialog(dC381);
break;
case dC381:
playDialog(dF381);
break;
case dF381:
_vm->changeRoom(kRoom41, 0, 18);
_vm->_cyberInventory = _vm->_inventory;
_vm->_iconBase = 0;
_vm->_inventory.clear();
_vm->_inventory.push_back(kItemPositioner);
break;
case dF371:
_vm->setObjectAnim(oSCAFFALE36, a3615AAPRENDESCAFFALE);
_vm->_animMgr->smkToggleTrackAudio(0, 1, true);
break;
case dF431:
_vm->_flagShowCharacter = true;
_vm->startCharacterAction(aWALKIN, 0, 11, 0);
break;
case dF451:
_vm->_obj[od44TO45]._goRoom = kRoom45S;
_vm->_textMgr->characterSay(kSentenceTheSpiderHasEscaped);
break;
case dF491:
for (int c = oPULSANTE1AD; c <= oPULSANTE33AD; ++c) {
if (!_vm->_obj[c]._goRoom) {
_vm->_obj[c]._goRoom = kRoom4A;
_vm->setObjectVisible(c, true);
_vm->setObjectVisible(c - 40, false);
break;
}
}
_vm->removeIcon(kItemShaft);
playDialog(dC4A1);
_vm->_pathFind->setPosition(12);
break;
case dC4A1:
_vm->_flagShowCharacter = true;
_vm->_actor->actorStop();
_vm->_pathFind->nextStep();
break;
case dF4C1:
_vm->_inventory = _vm->_cyberInventory;
_vm->_iconBase = 0;
_vm->removeIcon(kItemLiftCard);
_vm->removeIcon(kItemPen);
_vm->removeIcon(kItemLetterboxKey);
_vm->removeIcon(kItemLetter);
_vm->removeIcon(kItemSubwayCard);
_vm->removeIcon(kItemRubysPhoto);
_vm->removeIcon(kItemPistol);
_vm->removeIcon(kItemRubysReport);
_vm->removeIcon(kItemMembershipCard);
_vm->removeIcon(kItemMicrowaveGun);
_vm->removeIcon(kItemFaultyBulb);
_vm->removeIcon(kItemElevatorRemoteControl);
_vm->removeIcon(kItemSecurityCard);
_vm->removeIcon(kItemSecuritySystemSequence);
_vm->removeIcon(kItemStethoscope);
_vm->removeIcon(kItemRubysMedicalReport);
_vm->removeIcon(kItemEgyptologyBook);
_vm->addIcon(kItemPrisonMap);
_vm->addIcon(kItemParallelCutter);
_vm->addIcon(kItemWristComm);
_vm->startCharacterAction(a511, 0, 1, 0);
break;
case dF4P1:
_vm->_textMgr->characterSay(kSentenceItDidntWork);
break;
case dF4P2:
_vm->_textMgr->characterSay(kSentenceTakeThatWolfman);
break;
case dF562:
_vm->_obj[oDOOR58C55]._action = 1287;
_vm->setObjectAnim(oDOOR58C55, 0);
_vm->_obj[oWINDOW58P55]._action = 1292;
_vm->setObjectVisible(oWINDOW58P55, true);
_vm->setObjectAnim(oWINDOW58P55, 0);
break;
case dF5A1:
_vm->_obj[oDOOR58C55]._action = 1286;
_vm->_obj[oWINDOW58P55]._action = 1291;
_vm->_obj[oWINDOWA5A]._action = 1403;
_vm->setObjectVisible(oGUARDIA58, true);
_choice[286]._flag |= kObjFlagDone;
break;
case dC581:
if (!isDialogFinished(886) && isDialogFinished(258)) {
_vm->_pathFind->setPosition(1);
playDialog(dF581);
}
break;
case dC582:
_vm->setObjectVisible(oWINDOWA58, true);
_vm->addIcon(kItemGovernorsCode);
break;
case dC5A1:
_vm->_obj[oWINDOWA5A]._action = 1402;
if (_vm->_room[kRoom5A].hasExtra())
playDialog(dF5A1);
break;
case dFLOG:
playDialog(dINTRO);
break;
case dINTRO:
_vm->changeRoom(kRoom11);
break;
case dF582:
playDialog(dFCRED);
break;
case dFCRED:
_vm->quitGame();
break;
default:
break;
}
return;
}
// If another dialog starts
if (_choice[_curChoice]._nextDialog != 0) {
_curDialog = _choice[_curChoice]._nextDialog;
_vm->_flagDialogActive = true;
_curChoice = 0;
dialog = &_dialog[_curDialog];
// If there is a pre-dialog
if (_dialog[_curDialog]._startLen > 0) {
_vm->_animMgr->playMovie(_dialog[_curDialog]._startAnim, 1, _dialog[_curDialog]._startLen);
return;
}
}
// Immediately starts the fraud choice
for (int c = dialog->_firstChoice; c < dialog->_firstChoice + dialog->_choiceNumb; ++c) {
if ((_choice[c]._flag & DLGCHOICE_FRAUD) && isChoiceVisible(c)) {
const bool singleChoice = dialog->_choiceNumb == 1;
playChoice(c, singleChoice);
return;
}
}
// If there's only one option, show it immediately, otherwise show available choices
int res = 0;
for (int c = dialog->_firstChoice; c < dialog->_firstChoice + dialog->_choiceNumb; ++c) {
if (isChoiceVisible(c)) {
if (_choice[c]._flag & DLGCHOICE_EXITNOW) {
if (res == 0)
res = c;
else {
res = 0;
break;
}
} else {
res = 0;
break;
}
}
}
if (res != 0) {
const bool singleChoice = dialog->_choiceNumb == 1;
playChoice(res, singleChoice);
return;
}
// If no option is visible, close the dialog
res = 0;
for (int c = dialog->_firstChoice; c < dialog->_firstChoice + dialog->_choiceNumb; ++c) {
if (isChoiceVisible(c))
++res;
}
if (res == 0) {
_vm->_animMgr->stopFullMotion();
if (_curDialog == dC381)
playDialog(dF381);
return;
}
showChoices(_curDialog);
}
void DialogManager::dialogHandler(int numFrame) {
if (_vm->_flagDialogActive && !_vm->_flagDialogMenuActive) {
_vm->_graphicsMgr->hideCursor();
if (numFrame == _subTitles[_curSubTitle]._startFrame) {
int i = _curSubTitle;
++_curSubTitle;
_vm->_drawText._rect.left = _subTitles[i]._x;
_vm->_drawText._rect.top = _subTitles[i]._y;
_vm->_drawText._textColor = _subTitles[i]._color;
_vm->_drawText._text = _vm->_sentence[_subTitles[i]._sentence];
}
}
}
void DialogManager::playChoice(uint16 i, bool singleChoice) {
assert(i < MAXCHOICE);
DialogChoice *choice = &_choice[i];
const int startFrame = choice->_startFrame;
const int endSubTitle = choice->_firstSubTitle + choice->_subTitleNumb;
int totalLength = 0;
_vm->_graphicsMgr->clearScreenBufferTop();
_vm->_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
_curChoice = i;
_curSubTitle = choice->_firstSubTitle;
_vm->_flagDialogMenuActive = false;
choice->_flag |= kObjFlagDone;
// if it was 'one time', disable it
if (choice->_flag & DLGCHOICE_ONETIME)
toggleChoice(i, false);
// Disable other choices
for (int c = 0; c < MAXDISPCHOICES; ++c) {
toggleChoice(choice->_off[c], false);
toggleChoice(choice->_on[c], true);
}
for (int c = _curSubTitle; c < endSubTitle; ++c)
totalLength += _subTitles[c]._length - 1;
_vm->_graphicsMgr->hideCursor();
_vm->_animMgr->playMovie(_dialog[_curDialog]._startAnim, startFrame, startFrame + totalLength - 1, singleChoice);
}
void DialogManager::doDialog() {
switch (_vm->_curMessage->_event) {
case ME_STARTDIALOG:
playDialog(_vm->_curMessage->_u16Param1);
break;
default:
break;
}
}
bool DialogManager::showCharacterAfterDialog() const {
switch (_curDialog) {
case dF321:
case dF431:
case dF4C1:
case dASCENSORE12:
case dASCENSORE13:
case dASCENSORE16:
return false;
default:
return true;
}
}
bool DialogManager::handleShopKeeperDialog(uint16 curObj) {
for (int c = _dialog[dSHOPKEEPER1A]._firstChoice; c < (_dialog[dSHOPKEEPER1A]._firstChoice + _dialog[dSHOPKEEPER1A]._choiceNumb); ++c) {
if (isChoiceVisible(c)) {
playDialog(_vm->_obj[curObj]._goRoom);
return false;
}
}
return true;
}
void DialogManager::syncGameStream(Common::Serializer &ser) {
for (int i = 0; i < MAXCHOICE; ++i) {
DialogChoice *choice = &_choice[i];
ser.syncAsUint16LE(choice->_flag);
ser.syncAsUint16LE(choice->_sentenceIndex);
ser.syncAsUint16LE(choice->_firstSubTitle);
ser.syncAsUint16LE(choice->_subTitleNumb);
for (int j = 0; j < MAXDISPCHOICES; ++j)
ser.syncAsUint16LE(choice->_on[j]);
for (int j = 0; j < MAXDISPCHOICES; ++j)
ser.syncAsUint16LE(choice->_off[j]);
ser.syncAsUint16LE(choice->_startFrame);
ser.syncAsUint16LE(choice->_nextDialog);
}
for (int i = 0; i < MAXDIALOG; ++i) {
Dialog *dialog = &_dialog[i];
ser.syncAsUint16LE(dialog->_flag);
ser.syncAsUint16LE(dialog->_interlocutor);
ser.syncBytes((byte *)dialog->_startAnim, 14);
ser.syncAsUint16LE(dialog->_startLen);
ser.syncAsUint16LE(dialog->_firstChoice);
ser.syncAsUint16LE(dialog->_choiceNumb);
for (int j = 0; j < MAXNEWSMKPAL; ++j)
ser.syncAsUint16LE(dialog->_newPal[j]);
}
}
void DialogManager::loadData(Common::SeekableReadStreamEndian *stream) {
for (int i = 0; i < MAXDIALOG; ++i) {
Dialog *dialog = &_dialog[i];
dialog->_flag = stream->readUint16();
dialog->_interlocutor = stream->readUint16();
stream->read(&dialog->_startAnim, 14);
dialog->_startLen = stream->readUint16();
dialog->_firstChoice = stream->readUint16();
dialog->_choiceNumb = stream->readUint16();
for (int j = 0; j < MAXNEWSMKPAL; ++j)
dialog->_newPal[j] = stream->readUint16();
}
for (int i = 0; i < MAXCHOICE; ++i) {
DialogChoice *choice = &_choice[i];
choice->_flag = stream->readUint16();
choice->_sentenceIndex = stream->readUint16();
choice->_firstSubTitle = stream->readUint16();
choice->_subTitleNumb = stream->readUint16();
for (int j = 0; j < MAXDISPCHOICES; ++j)
choice->_on[j] = stream->readUint16();
for (int j = 0; j < MAXDISPCHOICES; ++j)
choice->_off[j] = stream->readUint16();
choice->_startFrame = stream->readUint16();
choice->_nextDialog = stream->readUint16();
}
for (int i = 0; i < MAXSUBTITLES; ++i) {
DialogSubTitle *subTitle = &_subTitles[i];
subTitle->_sentence = stream->readUint16();
subTitle->_x = stream->readUint16();
subTitle->_y = stream->readUint16();
subTitle->_color = stream->readUint16();
subTitle->_startFrame = stream->readUint16();
subTitle->_length = stream->readUint16();
}
if (_vm->isDemo()) {
_subTitles[975]._length = 113;
}
}
} // End of namespace Trecision

112
engines/trecision/dialog.h Normal file
View File

@@ -0,0 +1,112 @@
/* 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 TRECISION_DIALOG_H
#define TRECISION_DIALOG_H
#include "common/scummsys.h"
#include "common/serializer.h"
#include "common/stream.h"
namespace Trecision {
class TrecisionEngine;
#define MAXDISPCHOICES 5
#define MAXSUBTITLES 1500
#define MAXDIALOG 70
#define MAXCHOICE 1000
#define MAXNEWSMKPAL 40
struct Dialog {
uint16 _flag; // DONT_SKIP .. and more
uint16 _interlocutor; // Person I'm talking to... Maybe it's not needed
char _startAnim[14]; // aANIMATION or text table index by filename.
uint16 _startLen;
uint16 _firstChoice;
uint16 _choiceNumb;
uint16 _newPal[MAXNEWSMKPAL];
void clear();
};
struct DialogSubTitle {
uint16 _sentence;
uint16 _x, _y, _color; // you can compact this info using a bit field
uint16 _startFrame, _length; // Frame at which the subtitle starts and its length
void clear();
};
struct DialogChoice {
uint16 _flag; // DLGCHOICE_HIDE|DLGCHOICE_ONETIME|DLGCHOICE_FRAUD...if used...
uint16 _sentenceIndex; // Index in the sentence array.
uint16 _firstSubTitle, _subTitleNumb; // starting index and number of sub title sentences
uint16 _on[MAXDISPCHOICES], _off[MAXDISPCHOICES];
uint16 _startFrame; // Starting frame of the choice
uint16 _nextDialog;
void clear();
};
class DialogManager {
TrecisionEngine *_vm;
void showChoices(uint16 i);
void playChoice(uint16 i, bool singleChoice);
Dialog _dialog[MAXDIALOG];
DialogChoice _choice[MAXCHOICE];
int16 _curPos;
int16 _lastPos;
uint16 _dispChoice[MAXDISPCHOICES];
uint16 _curDispChoice;
DialogSubTitle _subTitles[MAXSUBTITLES];
uint16 _curSubTitle;
uint16 _curDialog;
uint16 _curChoice;
public:
DialogManager(TrecisionEngine *vm);
~DialogManager();
void dialogPrint(int x, int y, int c, const Common::String &txt);
void updateChoices(int16 dmx, int16 dmy);
void selectChoice(int16 dmx, int16 dmy);
void playDialog(uint16 i);
void toggleChoice(uint16 choice, bool enable);
void clearExitFlag(uint16 choice);
bool isChoiceVisible(uint16 choice) const;
bool isDialogFinished(uint16 choice) const;
void afterChoice();
void dialogHandler(int numFrame);
void doDialog();
bool showCharacterAfterDialog() const;
bool handleShopKeeperDialog(uint16 curObj);
uint16 getCurDialog() const { return _curDialog; }
uint16 getCurChoice() const { return _curChoice; }
void syncGameStream(Common::Serializer &ser);
void loadData(Common::SeekableReadStreamEndian *stream);
};
// end of class
} // End of namespace Trecision
#endif

View 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/>.
*
*/
#include "common/scummsys.h"
#include "common/str.h"
#include "common/substream.h"
#include "common/memstream.h"
#include "common/file.h"
#include "trecision/trecision.h"
#include "trecision/fastfile.h"
#include "trecision/video.h"
namespace Trecision {
FastFile::FastFile() : Common::Archive(), _stream(nullptr), _compStream(nullptr), _compBuffer(nullptr) {
}
FastFile::~FastFile() {
close();
}
const FileEntry *FastFile::getEntry(const Common::Path &name) const {
for (Common::Array<FileEntry>::const_iterator it = _fileEntries.begin(); it != _fileEntries.end(); ++it) {
if (it->name.equalsIgnoreCase(name))
return it;
}
return nullptr;
}
bool FastFile::open(TrecisionEngine *vm, const Common::Path &name) {
close();
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name);
if (!stream)
return false;
_stream = vm->readEndian(stream);
int numFiles = _stream->readUint32();
_fileEntries.resize(numFiles);
for (int i = 0; i < numFiles; ++i) {
FileEntry *entry = &_fileEntries[i];
entry->name = Common::Path(_stream->readString(0, 12));
entry->offset = _stream->readUint32();
}
return true;
}
void FastFile::close() {
delete _stream;
_stream = nullptr;
delete _compStream;
_compStream = nullptr;
_fileEntries.clear();
}
bool FastFile::hasFile(const Common::Path &path) const {
const FileEntry *entry = getEntry(path);
return entry != nullptr;
}
int FastFile::listMembers(Common::ArchiveMemberList &list) const {
list.clear();
for (Common::Array<FileEntry>::const_iterator it = _fileEntries.begin(); it != _fileEntries.end(); ++it)
list.push_back(getMember(it->name));
return list.size();
}
const Common::ArchiveMemberPtr FastFile::getMember(const Common::Path &path) const {
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *FastFile::createReadStreamForMember(const Common::Path &path) const {
if (!_stream)
return nullptr;
Common::SeekableReadStream *stream = nullptr;
const FileEntry *entry = getEntry(path);
if (entry) {
uint32 size = (entry + 1)->offset - entry->offset;
if ((int32)(entry->offset + size) <= _stream->size()) {
// Load data from fast file
stream = new Common::SeekableSubReadStream(_stream, entry->offset, entry->offset + size);
}
}
if (!stream) {
// Load data from external file
Common::File *file = new Common::File();
if (file->open(path)) {
stream = file;
} else {
delete file;
}
}
if (!stream) {
warning("FastFile - %s not found", path.toString().c_str());
}
return stream;
}
void FastFile::decompress(const uint8 *src, uint32 srcSize, uint8 *dst, uint32 decompSize) {
const uint16 *sw = (const uint16 *)(src + srcSize);
uint8 *d = dst;
uint32 bytesWritten = 0;
const uint8 *s = src;
unsigned short ctrl = 0, ctrl_cnt = 1;
while (s < (const uint8 *)sw) {
if (!--ctrl_cnt) {
ctrl = READ_LE_UINT16(--sw);
ctrl_cnt = 16;
} else {
ctrl <<= 1;
}
if (ctrl & 0x8000) {
uint16 foo = READ_LE_UINT16(--sw);
const uint8 *cs = d - (foo >> 4);
uint16 num = 16 - (foo & 0xF);
for (uint16 i = 0; i < num; ++i) {
*d++ = *cs++;
++bytesWritten;
assert(bytesWritten <= decompSize);
}
*d++ = *cs++;
*d++ = *cs;
bytesWritten += 2;
assert(bytesWritten <= decompSize);
} else {
*d++ = *s++;
++bytesWritten;
assert(bytesWritten <= decompSize);
}
}
}
#define FAST_COOKIE 0xFA57F00D
Common::SeekableReadStream *FastFile::createReadStreamForCompressedMember(const Common::Path &name) {
Common::SeekableReadStream *ff = createReadStreamForMember(name);
if (ff == nullptr)
error("createReadStreamForCompressedMember - File not found %s", name.toString().c_str());
const int32 dataSize = ff->size() - 8;
const uint32 signature = ff->readUint32LE();
if (signature != FAST_COOKIE)
error("createReadStreamForCompressedMember - %s has a bad signature and can't be loaded", name.toString().c_str());
const int32 decompSize = ff->readSint32LE();
uint8 *ibuf = new uint8[dataSize];
const int32 realSize = MAX(dataSize, decompSize) + 8 + 100; // add extra padding for the decompressor
delete _compStream;
_compBuffer = (uint8 *) malloc (realSize);
ff->read(ibuf, dataSize);
delete ff;
if (dataSize < decompSize)
decompress(ibuf, dataSize, _compBuffer, realSize);
else
memcpy(_compBuffer, ibuf, dataSize);
delete[] ibuf;
_compStream = new Common::MemoryReadStream(_compBuffer, realSize, DisposeAfterUse::YES);
return _compStream;
}
} // End of namespace Trecision

View 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 TRECISION_FASTFILE_H
#define TRECISION_FASTFILE_H
#include "common/archive.h"
#include "common/array.h"
#include "common/stream.h"
namespace Trecision {
class TrecisionEngine;
struct FileEntry {
Common::Path name;
uint32 offset;
};
class FastFile : public Common::Archive {
public:
FastFile();
~FastFile() override;
bool open(TrecisionEngine *vm, const Common::Path &filename);
void close();
bool isOpen() const { return _stream != 0; }
Common::SeekableReadStream *createReadStreamForCompressedMember(const Common::Path &name);
// Common::Archive API implementation
bool hasFile(const Common::Path &path) const override;
int listMembers(Common::ArchiveMemberList &list) const override;
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
Common::SeekableReadStreamEndian *_stream;
Common::SeekableReadStream *_compStream;
Common::Array<FileEntry> _fileEntries;
uint8 *_compBuffer;
const FileEntry *getEntry(const Common::Path &name) const;
void decompress(const uint8 *src, uint32 srcSize, uint8 *dst, uint32 decompSize);
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,840 @@
/* 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/file.h"
#include "common/system.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/graphics.h"
#include "trecision/pathfinding3d.h"
#include "trecision/renderer3d.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
GraphicsManager::GraphicsManager(TrecisionEngine *vm) : _vm(vm), _rgb555Format(2, 5, 5, 5, 0, 10, 5, 0, 0) {
for (int i = 0; i < 3; ++i)
_bitMask[i] = 0;
for (int i = 0; i < 256; ++i) {
_fonts[i]._width = 0;
_fonts[i]._data = nullptr;
}
}
GraphicsManager::~GraphicsManager() {
_screenBuffer.free();
_background.free();
_smkBackground.free();
_leftInventoryArrow.free();
_rightInventoryArrow.free();
_inventoryIcons.free();
_saveSlotThumbnails.free();
_textureMat.free();
for (int i = 0; i < 256; ++i)
delete[] _fonts[i]._data;
}
bool GraphicsManager::init() {
// Find a suitable 16-bit format, currently we don't support other color depths
Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
for (Common::List<Graphics::PixelFormat>::iterator it = formats.begin(); it != formats.end(); ++it) {
if (it->bytesPerPixel != 2 || it->aBits()) {
it = formats.reverse_erase(it);
} else if (*it == _rgb555Format) {
formats.clear();
formats.push_back(_rgb555Format);
break;
}
}
if (formats.empty())
return false;
initGraphics(MAXX, MAXY, formats);
_screenFormat = g_system->getScreenFormat();
if (_screenFormat.bytesPerPixel != 2)
return false;
_bitMask[0] = _screenFormat.rMax() << _screenFormat.rShift;
_bitMask[1] = _screenFormat.gMax() << _screenFormat.gShift;
_bitMask[2] = _screenFormat.bMax() << _screenFormat.bShift;
clearScreen();
_screenBuffer.create(MAXX, MAXY, _screenFormat);
_background.create(MAXX, MAXY, _screenFormat);
_smkBackground.create(MAXX, AREA, _screenFormat);
_saveSlotThumbnails.create(READICON * ICONDX, ICONDY, _screenFormat);
loadData();
initCursor();
hideCursor();
return true;
}
void GraphicsManager::addDirtyRect(Common::Rect rect, bool translateRect) {
if (translateRect)
rect.translate(0, TOP);
_dirtyRects.push_back(rect);
}
void GraphicsManager::drawObj(int index, bool mask, Common::Rect drawRect, Common::Rect drawObjRect, bool includeDirtyRect) {
if (drawObjRect.left > MAXX || drawObjRect.top > MAXY)
return;
// If we have a valid object, draw it, otherwise erase it
// by using the background buffer
const uint16 *buf = index >= 0 ? _vm->_objectGraphics[index].buf : (uint16 *)_smkBackground.getPixels();
if (mask && index >= 0) {
uint8 *maskPtr = _vm->_objectGraphics[index].mask;
for (uint16 y = drawRect.top; y < drawRect.bottom; ++y) {
uint16 x = 0;
bool copyBytes = false;
while (x < drawRect.width()) {
if (!copyBytes) { // jump
x += *maskPtr;
++maskPtr;
copyBytes = true;
} else { // copy
const uint16 maskOffset = *maskPtr;
if (maskOffset != 0 && y >= drawRect.top + drawObjRect.top && y < drawRect.top + drawObjRect.bottom) {
const void *src = (x >= drawObjRect.left) ? buf : buf + drawObjRect.left - x;
int offset = (x >= drawObjRect.left) ? x : drawObjRect.left;
void *dst = _screenBuffer.getBasePtr(offset + drawRect.left, y);
if (x >= drawObjRect.left && x + maskOffset < drawObjRect.right)
memcpy(dst, src, maskOffset * 2);
else if (x < drawObjRect.left && x + maskOffset < drawObjRect.right && x + maskOffset >= drawObjRect.left)
memcpy(dst, src, (maskOffset + x - drawObjRect.left) * 2);
else if (x >= drawObjRect.left && x + maskOffset >= drawObjRect.right && x < drawObjRect.right)
memcpy(dst, src, (drawObjRect.right - x) * 2);
else if (x < drawObjRect.left && x + maskOffset >= drawObjRect.right)
memcpy(dst, src, (drawObjRect.right - drawObjRect.left) * 2);
}
x += *maskPtr;
buf += *maskPtr++;
copyBytes = false;
}
}
}
} else {
const uint16 x = drawRect.left + drawObjRect.left;
if (x + drawObjRect.width() > MAXX || drawObjRect.top + drawObjRect.height() > MAXY) {
warning("drawObj: Invalid surface, skipping");
return;
}
for (uint16 y = drawObjRect.top; y < drawObjRect.bottom; ++y) {
memcpy(_screenBuffer.getBasePtr(x, drawRect.top + y),
buf + (y * drawRect.width()) + drawObjRect.left, drawObjRect.width() * 2);
}
}
if (includeDirtyRect)
addDirtyRect(drawObjRect, true);
}
void GraphicsManager::eraseObj(Common::Rect drawObjRect) {
Common::Rect eraseRect = drawObjRect;
eraseRect.translate(0, TOP);
if (eraseRect.isValidRect())
_screenBuffer.fillRect(eraseRect, 0);
}
void GraphicsManager::clearScreen() {
g_system->fillScreen(0);
}
void GraphicsManager::copyToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette) {
Graphics::Surface *surface16 = surface->convertTo(_screenFormat, palette);
copyToScreenBufferInner(surface16, x, y);
surface16->free();
delete surface16;
}
void GraphicsManager::copyToScreenBufferInner(const Graphics::Surface *surface, int x, int y) {
if (x + surface->w > MAXX || y + surface->h > MAXY) {
warning("copyToScreenBufferInner: Invalid surface, skipping");
return;
}
for (int curY = 0; curY < surface->h; ++curY) {
// NOTE: We use surface width for the pitch so that memcpy works
// correctly with surfaces from getSubArea()
memcpy(_screenBuffer.getBasePtr(x, y + curY), surface->getBasePtr(0, curY), surface->w * 2);
}
}
void GraphicsManager::blitToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette, bool useSmkBg) {
if (x + surface->w > MAXX || y + surface->h > MAXY) {
warning("blitToScreenBuffer: Invalid surface, skipping");
return;
}
const uint16 mask = (uint16)_screenFormat.RGBToColor(palette[0], palette[1], palette[2]);
Graphics::Surface *surface16 = surface->convertTo(_screenFormat, palette);
for (int curY = 0; curY < surface16->h; ++curY) {
for (int curX = 0; curX < surface16->w; ++curX) {
const int destX = x + curX;
const int destY = y + curY;
const uint16 pixel = (uint16)surface16->getPixel(curX, curY);
if (pixel != mask) {
_screenBuffer.setPixel(destX, destY, pixel);
if (useSmkBg)
_smkBackground.setPixel(destX, destY - TOP, pixel);
} else if (useSmkBg) {
const uint16 bgPixel = _background.getPixel(destX, destY - TOP);
_screenBuffer.setPixel(destX, destY, bgPixel);
_smkBackground.setPixel(destX, destY - TOP, bgPixel);
}
}
}
surface16->free();
delete surface16;
}
void GraphicsManager::copyToScreen(int x, int y, int w, int h) {
g_system->copyRectToScreen(
_screenBuffer.getBasePtr(x, y),
MAXX * 2, x, y, w, h
);
}
void GraphicsManager::readSurface(Common::SeekableReadStream *stream, Graphics::Surface *surface, uint16 width, uint16 height, uint16 count) {
surface->create(width * count, height, _rgb555Format);
for (uint16 i = 0; i < count; ++i) {
for (uint16 y = 0; y < height; ++y) {
for (uint16 x = 0; x < width; ++x) {
surface->setPixel(width * i + x, y, stream->readUint16LE());
}
}
}
surface->convertToInPlace(_screenFormat);
}
void GraphicsManager::readTexture(Common::SeekableReadStream *stream) {
readSurface(stream, &_textureMat, 91, 256);
}
void GraphicsManager::drawTexturePixel(uint16 textureX, uint16 textureY, uint16 screenX, uint16 screenY) {
const uint16 texturePixel = (uint16)_textureMat.getPixel(textureX, textureY);
_screenBuffer.setPixel(screenX, screenY, texturePixel);
}
void GraphicsManager::loadBackground(Common::SeekableReadStream *stream) {
SObject bgInfo;
bgInfo.readRect(stream);
readSurface(stream, &_background, bgInfo._rect.width(), bgInfo._rect.height());
_smkBackground.copyFrom(_background);
memcpy(_screenBuffer.getBasePtr(0, TOP), _background.getPixels(), _background.pitch * _background.h);
}
void GraphicsManager::loadData() {
Common::SeekableReadStream *arrowsDataFile = _vm->_dataFile.createReadStreamForMember("frecc.bm");
// The data file contains images for deactivated arrows, which aren't used. Skip them.
arrowsDataFile->skip(ICONMARGDX * ICONDY * 2 * 3);
readSurface(arrowsDataFile, &_leftInventoryArrow, ICONMARGSX, ICONDY);
readSurface(arrowsDataFile, &_rightInventoryArrow, ICONMARGSX, ICONDY);
delete arrowsDataFile;
Common::SeekableReadStream *iconsDataFile = _vm->_dataFile.createReadStreamForMember("icone.bm");
readSurface(iconsDataFile, &_inventoryIcons, ICONDX, ICONDY, READICON);
delete iconsDataFile;
loadFont();
}
void GraphicsManager::setSaveSlotThumbnail(byte iconSlot, const Graphics::Surface *thumbnail) {
Graphics::Surface *scaled = thumbnail->scale(ICONDX, ICONDY);
scaled->convertToInPlace(_screenFormat);
for (uint16 y = 0; y < ICONDY; ++y) {
memcpy(_saveSlotThumbnails.getBasePtr(ICONDX * iconSlot, y), scaled->getBasePtr(0, y), ICONDX * 2);
}
scaled->free();
delete scaled;
}
void GraphicsManager::drawLeftInventoryArrow(byte startLine) {
Graphics::Surface arrow = _leftInventoryArrow.getSubArea(Common::Rect(
0, startLine, _leftInventoryArrow.w, _leftInventoryArrow.h
));
copyToScreenBufferInner(&arrow, 0, FIRSTLINE);
}
void GraphicsManager::drawRightInventoryArrow(byte startLine) {
Graphics::Surface arrow = _rightInventoryArrow.getSubArea(Common::Rect(
0, startLine, _rightInventoryArrow.w, _rightInventoryArrow.h
));
copyToScreenBufferInner(&arrow, MAXX - ICONMARGDX, FIRSTLINE);
}
void GraphicsManager::drawInventoryIcon(byte iconIndex, byte iconSlot, byte startLine) {
Graphics::Surface icon = _inventoryIcons.getSubArea(Common::Rect(
iconIndex * ICONDX,
startLine,
iconIndex * ICONDX + ICONDX,
_inventoryIcons.h
));
copyToScreenBufferInner(&icon, iconSlot * ICONDX + ICONMARGSX, FIRSTLINE);
}
void GraphicsManager::drawSaveSlotThumbnail(byte iconIndex, byte iconSlot, byte startLine) {
Graphics::Surface icon = _saveSlotThumbnails.getSubArea(Common::Rect(
iconIndex * ICONDX,
startLine,
iconIndex * ICONDX + ICONDX,
_saveSlotThumbnails.h
));
copyToScreenBufferInner(&icon, iconSlot * ICONDX + ICONMARGSX, FIRSTLINE);
}
void GraphicsManager::clearScreenBuffer() {
_screenBuffer.fillRect(Common::Rect(0, 0, MAXX, MAXY), 0);
}
void GraphicsManager::clearScreenBufferTop() {
// Clears lines 0 - 60
_screenBuffer.fillRect(Common::Rect(0, 0, MAXX, TOP), 0);
}
void GraphicsManager::clearScreenBufferInventory() {
// Clears lines 420 - 480
_screenBuffer.fillRect(Common::Rect(0, FIRSTLINE, MAXX, MAXY), 0);
}
void GraphicsManager::clearScreenBufferSaveSlotDescriptions() {
// Clears lines 470 - 480
_screenBuffer.fillRect(Common::Rect(0, FIRSTLINE + ICONDY + 10, MAXX, MAXY), 0);
}
uint16 GraphicsManager::convertToScreenFormat(uint16 color) const {
uint8 r, g, b;
_rgb555Format.colorToRGB(color, r, g, b);
return (uint16)_screenFormat.RGBToColor(r, g, b);
}
/**
* Shadow Pixel
* (dark) 0..8 (light)
*/
void GraphicsManager::shadow(uint16 x, uint16 y, uint8 num) {
if (x > MAXX || y > MAXY) {
warning("shadow: Invalid pixel, skipping");
return;
}
const uint16 val = (uint16)_screenBuffer.getPixel(x, y);
const uint16 shadowVal =
((((val & _bitMask[2]) * num >> 7) & _bitMask[2]) |
(((val & _bitMask[1]) * num >> 7) & _bitMask[1]) |
(((val & _bitMask[0]) * num >> 7) & _bitMask[0]));
_screenBuffer.setPixel(x, y, shadowVal);
}
void GraphicsManager::pixelAliasing(uint16 x, uint16 y) {
if (x > MAXX || y > MAXY) {
warning("pixelAliasing: Invalid pixel, skipping");
return;
}
int px1 = _screenBuffer.getPixel(x - 1, y);
int px2 = _screenBuffer.getPixel(x, y);
_screenBuffer.setPixel(x - 1, y, aliasing(px1, px2, 6)); // 75% 25%
_screenBuffer.setPixel(x, y, aliasing(px1, px2, 2)); // 25% 75%
}
/**
* Aliasing Pixel
*/
uint16 GraphicsManager::aliasing(uint32 val1, uint32 val2, uint8 num) {
// 0: 0% val1 100% val2
// 1: 12% val1 87% val2
// 2: 25% val1 75% val2
// 3: 37% val1 62% val2
// 4: 50% val1 50% val2
// 5: 62% val1 37% val2
// 6: 75% val1 25% val2
// 7: 87% val1 12% val2
// 8: 100% val1 0% val2
return (((((val1 & _bitMask[2]) * num + (val2 & _bitMask[2]) * (8 - num)) >> 3) & _bitMask[2]) |
((((val1 & _bitMask[1]) * num + (val2 & _bitMask[1]) * (8 - num)) >> 3) & _bitMask[1]) |
((((val1 & _bitMask[0]) * num + (val2 & _bitMask[0]) * (8 - num)) >> 3) & _bitMask[0]));
}
void GraphicsManager::dissolve() {
const uint16 val = 30;
uint16 centerX = MAXX / 2;
uint16 centerY = MAXY / 2;
int lastv = 9000;
uint32 sv = _vm->readTime();
uint32 cv = _vm->readTime();
while (sv + val > cv) {
_vm->checkSystem();
if (lastv + cv < sv + val) {
cv = _vm->readTime();
continue;
}
lastv = (sv - cv) + val;
const float a = (float)(((centerX + 200) / val) * lastv);
const float b = (float)((centerY / val) * lastv);
float x = 0.0f;
float y = b;
if (centerY - (int)y > TOP)
memset(_screenBuffer.getBasePtr(0, TOP), 0, (centerY - (int)y - TOP) * MAXX * 2);
if (AREA + TOP > centerY + (int)y)
memset(_screenBuffer.getBasePtr(0, centerY + (int)y), 0, (AREA + TOP - (centerY + (int)y)) * MAXX * 2);
float d1 = b * b - a * a * b + a * a / 4.0f;
while (_vm->floatComp(a * a * (y - 0.5f), b * b * (x + 1.0f)) == 1) {
if (_vm->floatComp(d1, 0.0f) == -1)
d1 += b * b * (2.0f * x + 3.0f);
else {
d1 += b * b * (2.0f * x + 3.0f) + a * a * (-2.0f * y + 2.0f);
y -= 1.0f;
}
x += 1.0f;
int rightX = centerX + (int)x;
int maxY = centerY + (int)y;
int minY = centerY - (int)y;
if (rightX < MAXX) {
if (maxY < MAXY)
memset(_screenBuffer.getBasePtr(rightX, maxY), 0, (MAXX - rightX) * 2);
if (minY >= 0)
memset(_screenBuffer.getBasePtr(rightX, minY), 0, (MAXX - rightX) * 2);
}
int leftX = centerX - (int)x;
if (leftX > 0) {
if (maxY < MAXY)
memset(_screenBuffer.getBasePtr(0, maxY), 0, leftX * 2);
if (minY >= 0)
memset(_screenBuffer.getBasePtr(0, minY), 0, leftX * 2);
}
}
float d2 = b * b * (x + 0.5f) * (x + 0.5f) + a * a * (y - 1.0f) * (y - 1.0f) - a * a * b * b;
while (_vm->floatComp(y, 0.0f) == 1) {
if (_vm->floatComp(d2, 0.0f) == -1) {
d2 += b * b * (2.0f * x + 2.0f) + a * a * (-2.0f * y + 3.0f);
x += 1.0f;
} else
d2 += a * a * (-2.0f * y + 3.0f);
y -= 1.0f;
int rightX = centerX + (int)x;
int maxY = centerY + (int)y;
int minY = centerY - (int)y;
if (rightX < MAXX) {
if (maxY < MAXY)
memset(_screenBuffer.getBasePtr(rightX, maxY), 0, (MAXX - rightX) * 2);
if (minY >= 0)
memset(_screenBuffer.getBasePtr(rightX, minY), 0, (MAXX - rightX) * 2);
}
int leftX = centerX - (int)x;
if (leftX > 0) {
if (maxY < MAXY)
memset(_screenBuffer.getBasePtr(0, maxY), 0, leftX * 2);
if (minY >= 0)
memset(_screenBuffer.getBasePtr(0, minY), 0, leftX * 2);
}
}
copyToScreen(0, 0, MAXX, MAXY);
cv = _vm->readTime();
}
clearScreen();
}
void GraphicsManager::paintScreen(bool flag) {
_vm->_animTypeMgr->next();
_dirtyRects.clear();
_vm->_flagPaintCharacter = true; // always redraws the character
// erase character
if (_vm->_flagShowCharacter && _vm->_actor->actorRectIsValid()) { // if a description exists
Common::Rect actorRect = _vm->_actor->getActorRect();
actorRect.translate(0, -TOP);
drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), actorRect);
} else if (_vm->_animMgr->_animRect.left != MAXX) {
drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), _vm->_animMgr->_animRect);
}
// If there's text to remove
if (_vm->_textStatus & TEXT_DEL) {
// remove text
Common::Rect drawObjRect = _vm->_textMgr->getOldTextRect();
drawObjRect.translate(0, -TOP);
if (drawObjRect.top >= 0 && drawObjRect.bottom < AREA) {
drawObj(-1, false, Common::Rect(0, TOP, MAXX, MAXY + TOP), drawObjRect);
} else {
eraseObj(drawObjRect);
}
_vm->_textMgr->clearOldText();
if (!(_vm->_textStatus & TEXT_DRAW)) // if there's no new text
_vm->_textStatus = TEXT_OFF; // stop updating text
}
// Suppress all the objects you removed
for (Common::List<SSortTable>::iterator it = _vm->_sortTable.begin(); it != _vm->_sortTable.end(); ++it) {
if (it->_remove) {
drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), _vm->_obj[it->_objectId]._rect);
}
}
// Find the position of the character
_vm->_pathFind->actorOrder();
// For every box from the horizon forward...
// Copy per level
for (int liv = _vm->_pathFind->_numSortPanel; liv >= 0; --liv) {
uint16 curBox = _vm->_pathFind->_sortPan[liv]._num;
// draws all objects and animations that intersect the boundaries and refer to the current box
paintObjAnm(curBox);
}
if (_vm->_textStatus & TEXT_DRAW) {
_vm->_textMgr->drawCurString();
_vm->_textStatus = TEXT_DRAW; // Activate text update
}
_vm->_actor->updateStepSound();
if (!flag && !_vm->_flagDialogActive) {
copyToScreen(0, 0, MAXX, MAXY);
}
_vm->_sortTable.clear();
_vm->_flagPaintCharacter = false;
_vm->_flagWaitRegen = false;
// Handle papaverine delayed action
if (_vm->_curRoom == kRoom4A && _vm->_obj[oCHOCOLATES4A].isFlagExtra()) {
if (_vm->_animMgr->smkCurFrame(kSmackerBackground) > 480) {
_vm->playScript(s4AHELLEN);
_vm->_obj[oCHOCOLATES4A].setFlagExtra(false);
}
}
//
}
/**
* Draw all objects and animations that intersect
* boundaries belonging to curbox
*/
void GraphicsManager::paintObjAnm(uint16 curBox) {
_vm->_animMgr->refreshAnim(curBox);
// draws new cards belonging to the current box
for (Common::List<SSortTable>::iterator it = _vm->_sortTable.begin(); it != _vm->_sortTable.end(); ++it) {
if (!it->_remove && _vm->_obj[it->_objectId]._nbox == curBox) {
// the bitmap object at the desired level
SObject obj = _vm->_obj[it->_objectId];
Common::Rect drawRect = obj._rect;
drawRect.translate(0, TOP);
drawObj(_vm->getRoomObjectIndex(it->_objectId), obj.isModeMask(), drawRect, Common::Rect(drawRect.width(), drawRect.height()), false);
_dirtyRects.push_back(drawRect);
}
}
for (DirtyRectsIterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
for (int i = 0; i < MAXOBJINROOM; ++i) {
const uint16 curObject = _vm->_room[_vm->_curRoom]._object[i];
if (!curObject)
break;
SObject obj = _vm->_obj[curObject];
if ((obj.isModeFull() || obj.isModeMask()) && _vm->isObjectVisible(curObject) && (obj._nbox == curBox)) {
Common::Rect r = *it;
Common::Rect r2 = obj._rect;
r2.translate(0, TOP);
// Include the bottom right of the rect in the intersects() check
++r2.bottom;
++r2.right;
if (r.intersects(r2)) {
Common::Rect drawRect = obj._rect;
drawRect.translate(0, TOP);
// Restore the bottom right of the rect
--r2.bottom;
--r2.right;
// TODO: Simplify this?
const int16 xr1 = (r2.left > r.left) ? 0 : r.left - r2.left;
const int16 yr1 = (r2.top > r.top) ? 0 : r.top - r2.top;
const int16 xr2 = MIN<int16>(r.right, r2.right) - r2.left;
const int16 yr2 = MIN<int16>(r.bottom, r2.bottom) - r2.top;
drawObj(i, obj.isModeMask(), drawRect, Common::Rect(xr1, yr1, xr2, yr2), false);
}
}
}
}
if (_vm->_pathFind->getActorPos() == curBox && _vm->_flagShowCharacter) {
_vm->_renderer->drawCharacter(CALCPOINTS);
if (_vm->_actor->actorRectIsValid()) {
const Common::Rect actorRect = _vm->_actor->getActorRect();
// enlarge the last dirty rectangle with the actor's rectangle
if (!_dirtyRects.empty())
_dirtyRects.back().extend(actorRect);
_vm->_renderer->resetZBuffer(actorRect);
}
_vm->_renderer->drawCharacter(DRAWFACES);
} else if (_vm->_pathFind->getActorPos() == curBox && !_vm->_flagDialogActive) {
_vm->_animMgr->refreshActionAnimation();
}
}
uint16 GraphicsManager::getCharWidth(byte character) {
return _fonts[character]._width;
}
void GraphicsManager::drawChar(byte curChar, uint16 textColor, uint16 line, Common::Rect rect, Common::Rect subtitleRect, uint16 inc, Graphics::Surface *externalSurface) {
uint16 fontDataOffset = 0;
const uint16 charWidth = getCharWidth(curChar);
for (uint16 y = line * CARHEI; y < (line + 1) * CARHEI; ++y) {
uint16 curPos = 0;
uint16 curColor = MASKCOL;
while (curPos <= charWidth - 1) {
if (y >= subtitleRect.top && y < subtitleRect.bottom) {
if (curColor != MASKCOL && _fonts[curChar]._data[fontDataOffset]) {
const uint16 charLeft = inc + curPos;
const uint16 charRight = charLeft + _fonts[curChar]._data[fontDataOffset];
drawCharPixel(
y,
charLeft,
charRight,
rect,
subtitleRect,
curColor,
externalSurface
);
}
}
curPos += _fonts[curChar]._data[fontDataOffset];
++fontDataOffset;
if (curColor == MASKCOL)
curColor = 0;
else if (curColor == 0)
curColor = textColor;
else if (curColor == textColor)
curColor = MASKCOL;
}
}
}
void GraphicsManager::drawCharPixel(uint16 y, uint16 charLeft, uint16 charRight, Common::Rect rect, Common::Rect subtitleRect, uint16 color, Graphics::Surface *externalSurface) {
Graphics::Surface *surface = externalSurface ? externalSurface : &_screenBuffer;
uint16 *dst1 = (uint16 *)surface->getBasePtr(rect.left + charLeft, rect.top + y);
uint16 *dst2 = (uint16 *)surface->getBasePtr(rect.left + subtitleRect.left, rect.top + y);
uint16 *dst = nullptr;
uint16 size = 0;
if (charLeft >= subtitleRect.left && charRight < subtitleRect.right) {
dst = dst1;
size = charRight - charLeft;
} else if (charLeft < subtitleRect.left && charRight < subtitleRect.right && charRight > subtitleRect.left) {
dst = dst2;
size = charRight - subtitleRect.left;
} else if (charLeft >= subtitleRect.left && charRight >= subtitleRect.right && subtitleRect.right > charLeft) {
dst = dst1;
size = subtitleRect.right - charLeft;
} else if (charLeft < subtitleRect.left && charRight >= subtitleRect.right && subtitleRect.right > charLeft) {
dst = dst2;
size = subtitleRect.right - subtitleRect.left;
}
if (dst && size > 0) {
uint16 *d = dst;
for (uint32 i = 0; i < size; ++i)
*d++ = color;
}
}
void GraphicsManager::initCursor() {
const int cw = 21, ch = 21;
const int cx = 10, cy = 10;
uint16 cursor[cw * ch];
memset(cursor, 0, ARRAYSIZE(cursor) * 2);
const uint16 cursorColor = (uint16)_screenFormat.RGBToColor(255, 255, 255);
for (int i = 0; i < cw; ++i) {
if (i >= 8 && i <= 12 && i != 10)
continue;
cursor[cx * cw + i] = cursorColor; // horizontal
cursor[cx + cw * i] = cursorColor; // vertical
}
CursorMan.pushCursor(cursor, cw, ch, cx, cy, 0, false, &_screenFormat);
}
void GraphicsManager::showCursor() {
CursorMan.showMouse(true);
}
void GraphicsManager::hideCursor() {
CursorMan.showMouse(false);
}
void GraphicsManager::loadFont() {
const char *fileName = "nlfont.fnt";
Common::SeekableReadStream *fontStream = _vm->_dataFile.createReadStreamForMember(fileName);
if (fontStream == nullptr)
error("readData(): File %s not found", fileName);
uint16 fontDataOffset = 768;
for (int i = 0; i < 256; ++i) {
uint16 offset = fontStream->readSint16LE();
_fonts[i]._width = fontStream->readByte();
int tmpPos = fontStream->pos();
fontStream->seek(offset + fontDataOffset);
int cpt = 0;
for (uint16 y = 0; y < CARHEI; ++y) {
uint16 curPos = 0;
while (curPos <= _fonts[i]._width - 1) {
curPos += fontStream->readByte();
++cpt;
}
}
fontStream->seek(offset + fontDataOffset);
_fonts[i]._data = new int8[cpt];
fontStream->read(_fonts[i]._data, cpt);
fontStream->seek(tmpPos);
}
// Fix o+e ligature character (lowercase and uppercase). Ticket #12623
// Format is :
// - Each line represents a line of pixels
// - colors are in this order : none, shadow, text. Colors are looping until the total number of pixels corresponds to the character width
// - each number correspond to a number of pixels of the corresponding color
// So, 1, 6, 0, 2 means : 1 pixel unchanged, 6 pixels shadow, 0 pixel in text color, 2 pixels unchanged
static const int8 fix140[67] = {
1, 8,
0, 2, 2, 0, 1, 3, 0, 1,
0, 1, 1, 0, 2, 2, 0, 3,
0, 1, 1, 0, 3, 1, 0, 2, 0, 1,
0, 1, 1, 0, 3, 2, 0, 1, 0, 1,
0, 1, 1, 0, 3, 1, 0, 2, 0, 1,
0, 1, 1, 0, 2, 2, 0, 3,
0, 2, 2, 0, 1, 3, 0, 1,
1, 8,
9
};
static const int8 fix156[54] = {
9,
9,
1, 6, 0, 2,
0, 2, 2, 0, 1, 2, 0, 1, 0, 1,
0, 1, 1, 0, 2, 1, 0, 2, 1, 0, 1,
0, 1, 1, 0, 2, 4, 0, 1,
0, 1, 1, 0, 2, 1, 0, 4,
0, 2, 2, 0, 1, 3, 0, 1,
1, 8,
9
};
delete _fonts[140]._data;
delete _fonts[156]._data;
_fonts[140]._width = _fonts[156]._width = 9;
_fonts[140]._data = new int8[67];
_fonts[156]._data = new int8[54];
memcpy(_fonts[140]._data, fix140, 67);
memcpy(_fonts[156]._data, fix156, 54);
}
bool GraphicsManager::isCursorVisible() {
return CursorMan.isVisible();
}
void GraphicsManager::showDemoPic() {
Common::File file;
if (file.open("EndPic.bm")) {
readSurface(&file, &_screenBuffer, MAXX, MAXY);
copyToScreen(0, 0, MAXX, MAXY);
g_system->updateScreen();
_vm->freeKey();
_vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
_vm->waitKey();
}
}
} // End of namespace Trecision

View File

@@ -0,0 +1,116 @@
/* 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 TRECISION_GRAPHICS_H
#define TRECISION_GRAPHICS_H
#include "common/rect.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
namespace Common {
class SeekableReadStream;
}
namespace Trecision {
class TrecisionEngine;
struct Font {
int8 *_data;
uint16 _width;
};
class GraphicsManager {
TrecisionEngine *_vm;
Graphics::Surface _screenBuffer;
Graphics::Surface _background;
Graphics::Surface _smkBackground;
Graphics::Surface _leftInventoryArrow;
Graphics::Surface _rightInventoryArrow;
Graphics::Surface _inventoryIcons;
Graphics::Surface _saveSlotThumbnails;
Graphics::Surface _textureMat;
Graphics::PixelFormat _screenFormat;
uint16 _bitMask[3];
Font _fonts[256];
Common::List<Common::Rect> _dirtyRects;
const Graphics::PixelFormat _rgb555Format;
uint16 aliasing(uint32 val1, uint32 val2, uint8 num);
void drawCharPixel(uint16 y, uint16 charLeft, uint16 charRight, Common::Rect rect, Common::Rect subtitleRect, uint16 color, Graphics::Surface *externalSurface = nullptr);
void initCursor();
void copyToScreenBufferInner(const Graphics::Surface *surface, int x, int y);
void paintObjAnm(uint16 curBox);
void drawObj(int index, bool mask, Common::Rect drawRect, Common::Rect drawObjRect, bool includeDirtyRect = true);
void eraseObj(Common::Rect drawObjRect);
public:
GraphicsManager(TrecisionEngine *vm);
~GraphicsManager();
bool init();
void clearScreen();
void copyToScreen(int x, int y, int w, int h);
void copyToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette);
void blitToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette, bool useSmkBg);
void paintScreen(bool flag);
void loadBackground(Common::SeekableReadStream *stream);
void clearScreenBuffer();
void clearScreenBufferTop();
void clearScreenBufferInventory();
void clearScreenBufferSaveSlotDescriptions();
void drawLeftInventoryArrow(byte startLine);
void drawRightInventoryArrow(byte startLine);
void drawInventoryIcon(byte iconIndex, byte iconSlot, byte startLine);
void drawSaveSlotThumbnail(byte iconIndex, byte iconSlot, byte startLine);
void setSaveSlotThumbnail(byte iconSlot, const Graphics::Surface *thumbnail);
void readSurface(Common::SeekableReadStream *stream, Graphics::Surface *surface, uint16 width, uint16 height, uint16 count = 1);
void readTexture(Common::SeekableReadStream *stream);
void drawTexturePixel(uint16 textureX, uint16 textureY, uint16 screenX, uint16 screenY);
uint16 convertToScreenFormat(uint16 color) const;
void shadow(uint16 x, uint16 y, uint8 num);
void pixelAliasing(uint16 x, uint16 y);
void dissolve();
void addDirtyRect(Common::Rect rect, bool translateRect);
uint16 getCharWidth(byte character);
void drawChar(byte curChar, uint16 textColor, uint16 line, Common::Rect rect, Common::Rect subtitleRect, uint16 inc, Graphics::Surface *externalSurface);
bool isCursorVisible();
void showCursor();
void hideCursor();
void loadFont();
void loadData();
void showDemoPic();
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,377 @@
/* 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 "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/graphics.h"
#include "trecision/logic.h"
#include "trecision/pathfinding3d.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
void TrecisionEngine::refreshInventory(uint8 startIcon, uint8 startLine) {
if (startLine > ICONDY)
return;
_graphicsMgr->clearScreenBufferInventory();
for (uint8 iconSlot = 0; iconSlot < ICONSHOWN; iconSlot++) {
uint8 i = iconSlot + startIcon;
if (i >= _inventory.size())
break;
const byte iconIndex = _inventory[i];
if (iconIndex == _lightIcon)
continue;
if (iconIndex <= EMPTYSLOT)
_graphicsMgr->drawInventoryIcon(iconIndex - 1, iconSlot, startLine);
else
_graphicsMgr->drawSaveSlotThumbnail(iconIndex - EMPTYSLOT - 1, iconSlot, startLine);
}
if (startIcon != 0)
_graphicsMgr->drawLeftInventoryArrow(startLine);
if (startIcon + ICONSHOWN < (int)_inventory.size())
_graphicsMgr->drawRightInventoryArrow(startLine);
_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, ICONDY);
}
void TrecisionEngine::setInventoryStart(uint8 startIcon, uint8 startLine) {
_inventoryRefreshStartIcon = startIcon;
_inventoryRefreshStartLine = startLine;
}
void TrecisionEngine::moveInventoryLeft() {
if (_iconBase < _inventory.size() - ICONSHOWN)
++_iconBase;
setInventoryStart(_iconBase, INVENTORY_SHOW);
}
void TrecisionEngine::moveInventoryRight() {
if (_iconBase > 0)
--_iconBase;
setInventoryStart(_iconBase, INVENTORY_SHOW);
}
void TrecisionEngine::showIconName() {
if (isIconArea(_mousePos)) {
if (_inventoryStatus != INV_ON)
openInventory();
_curInventory = whatIcon(_mousePos);
showInventoryName(_curInventory, true);
if (!_flagUseWithStarted && !_flagSomeoneSpeaks) {
setInventoryStart(_iconBase, INVENTORY_SHOW);
}
} else if (isInventoryArea(_mousePos)) {
showInventoryName(NO_OBJECTS, true);
if (!_flagUseWithStarted) {
_lightIcon = 0xFF;
setInventoryStart(_iconBase, INVENTORY_SHOW);
}
}
}
void TrecisionEngine::openInventory() {
if (!_flagInventoryLocked && (_inventoryStatus == INV_OFF) && !_flagDialogActive) {
_inventoryCounter = INVENTORY_HIDE;
_inventorySpeedIndex = 0;
_inventoryStatus = INV_PAINT;
}
}
void TrecisionEngine::closeInventory() {
if (!_flagInventoryLocked && (_inventoryStatus == INV_INACTION) && !_flagDialogActive) {
_inventoryCounter = INVENTORY_SHOW;
_inventorySpeedIndex = 0;
_inventoryStatus = INV_DEPAINT;
_lightIcon = 0xFF;
}
}
void TrecisionEngine::closeInventoryImmediately() {
_inventoryStatus = INV_OFF;
_lightIcon = 0xFF;
_flagInventoryLocked = false;
_inventoryRefreshStartLine = INVENTORY_HIDE;
_inventoryCounter = INVENTORY_HIDE;
setInventoryStart(_inventoryRefreshStartIcon, INVENTORY_HIDE);
refreshInventory(_inventoryRefreshStartIcon, _inventoryRefreshStartLine);
}
void TrecisionEngine::examineItem() {
_curInventory = whatIcon(_mousePos);
_actor->actorStop();
_pathFind->nextStep();
if (_flagUseWithStarted) {
endUseWith();
} else
doInvExamine();
}
void TrecisionEngine::useItem() {
_curInventory = whatIcon(_mousePos);
if (_curInventory == 0)
return;
if (_flagUseWithStarted) {
endUseWith();
} else if (_inventoryObj[_curInventory].isUseWith()) {
if (_curInventory == kItemFlare && _curRoom == kRoom29) {
_textMgr->characterSay(kSentenceOnlyGotOne);
return;
}
_animMgr->startSmkAnim(_inventoryObj[_curInventory]._anim);
_lightIcon = _curInventory;
setInventoryStart(_iconBase, INVENTORY_SHOW);
_flagInventoryLocked = true;
_flagUseWithStarted = true;
_useWith[USED] = _curInventory;
_useWithInv[USED] = true;
showInventoryName(_curInventory, true);
} else
doInvOperate();
}
void TrecisionEngine::endUseWith() {
_flagInventoryLocked = false;
_flagUseWithStarted = false;
_useWith[WITH] = _curInventory;
_useWithInv[WITH] = true;
_lightIcon = 0xFF;
if (_useWith[USED] != _curInventory) {
doUseWith();
} else {
_animMgr->smkStop(kSmackerIcon);
showInventoryName(_curInventory, true);
}
}
void TrecisionEngine::clearUseWith() {
if (_flagUseWithStarted) {
if (_useWithInv[USED]) {
_lightIcon = 0xFF;
_animMgr->smkStop(kSmackerIcon);
setInventoryStart(_inventoryRefreshStartIcon, INVENTORY_HIDE);
_flagInventoryLocked = false;
}
_useWith[USED] = 0;
_useWith[WITH] = 0;
_useWithInv[USED] = false;
_useWithInv[WITH] = false;
_flagUseWithStarted = false;
_textMgr->clearLastText();
}
}
uint8 TrecisionEngine::whatIcon(Common::Point pos) {
if (pos.x < ICONMARGSX || pos.x > MAXX - ICONMARGDX)
return 0;
int index = _iconBase + ((pos.x - ICONMARGSX) / (ICONDX));
return index < (int)_inventory.size() ? _inventory[index] : 0;
}
int8 TrecisionEngine::iconPos(uint8 icon) {
for (uint8 i = 0; i < _inventory.size(); i++) {
if (_inventory[i] == icon)
return i;
}
return -1;
}
void TrecisionEngine::showInventoryName(uint16 obj, bool showhide) {
if (_logicMgr->isCloseupOrControlRoom() || _flagSomeoneSpeaks)
return;
if (_lastObj) {
_textMgr->clearLastText();
_lastObj = 0;
}
if (_flagUseWithStarted) {
if (!showhide) {
_textMgr->clearLastText();
_lastInv = 0;
return;
}
if ((obj | 0x8000) == _lastInv)
return;
Common::String desc = _sysText[kMessageUse];
if (_useWithInv[USED]) {
desc += _objName[_inventoryObj[_useWith[USED]]._name];
desc += _sysText[kMessageWith];
if (obj && (_inventoryObj[_useWith[USED]]._name != _inventoryObj[obj]._name))
desc += _objName[_inventoryObj[obj]._name];
} else {
if (_obj[_useWith[USED]].isModeHidden())
desc += "?"; // dunno
else
desc += _objName[_obj[_useWith[USED]]._name];
desc += _sysText[kMessageWith];
if (obj && (_obj[_useWith[USED]]._name != _inventoryObj[obj]._name))
desc += _objName[_inventoryObj[obj]._name];
}
const uint16 lenText = textLength(desc);
Common::Point pos(CLIP(320 - (lenText / 2), 2, MAXX - 2 - lenText), MAXY - CARHEI);
_lastInv = (obj | 0x8000);
if (_lastInv)
_textMgr->clearLastText();
_textMgr->addText(pos, desc.c_str(), COLOR_INVENTORY);
} else {
if (obj == _lastInv)
return;
if (!obj || !showhide) {
_textMgr->clearLastText();
_lastInv = 0;
return;
}
const uint16 lenText = textLength(_objName[_inventoryObj[obj]._name]);
uint16 posX = ICONMARGSX + ((iconPos(_curInventory) - _iconBase) * (ICONDX)) + ICONDX / 2;
posX = CLIP(posX - (lenText / 2), 2, MAXX - 2 - lenText);
Common::Point pos(posX, MAXY - CARHEI);
_lastInv = obj;
if (_lastInv)
_textMgr->clearLastText();
if (_inventoryObj[obj]._name)
_textMgr->addText(pos, _objName[_inventoryObj[obj]._name], COLOR_INVENTORY);
}
}
void TrecisionEngine::removeIcon(uint8 icon) {
const int8 pos = iconPos(icon);
if (pos == -1)
return;
_inventory.remove_at(pos);
_iconBase = _inventory.size() <= ICONSHOWN ? 0 : _inventory.size() - ICONSHOWN;
_textMgr->redrawString();
}
void TrecisionEngine::addIcon(uint8 icon) {
if (iconPos(icon) != -1)
return;
_inventory.push_back(icon);
_iconBase = _inventory.size() <= ICONSHOWN ? 0 : _inventory.size() - ICONSHOWN;
// To show the icon that enters the inventory
// doEvent(MC_INVENTORY,ME_OPEN,MP_DEFAULT,0,0,0,0);
// FlagForceRegenInventory = true;
_textMgr->redrawString();
}
void TrecisionEngine::replaceIcon(uint8 oldIcon, uint8 newIcon) {
int8 pos = iconPos(oldIcon);
if (pos >= 0)
_inventory[pos] = newIcon;
}
void TrecisionEngine::rollInventory(uint8 status) {
static const int16 inventorySpeed[8] = { 20, 10, 5, 3, 2, 0, 0, 0 };
if (status == INV_PAINT) {
_inventoryCounter -= inventorySpeed[_inventorySpeedIndex++];
if (_inventoryCounter <= INVENTORY_SHOW || _inventorySpeedIndex > 5) {
_inventorySpeedIndex = 0;
setInventoryStart(_iconBase, INVENTORY_SHOW);
_inventoryStatus = INV_INACTION;
_inventoryCounter = INVENTORY_SHOW;
if (!isInventoryArea(_mousePos))
closeInventory();
_textMgr->redrawString();
return;
}
} else if (status == INV_DEPAINT) {
_inventoryCounter += inventorySpeed[_inventorySpeedIndex++];
if (_inventoryCounter > INVENTORY_HIDE || _inventorySpeedIndex > 5) {
_inventorySpeedIndex = 0;
setInventoryStart(_iconBase, INVENTORY_HIDE);
_inventoryStatus = INV_OFF;
_inventoryCounter = INVENTORY_HIDE;
if (isInventoryArea(_mousePos) && !(_flagDialogActive || _flagDialogMenuActive))
openInventory();
else
_textMgr->redrawString();
return;
}
}
setInventoryStart(_iconBase, _inventoryCounter);
}
void TrecisionEngine::doScrollInventory(Common::Point pos) {
if (_inventoryStatus != INV_INACTION)
return;
if (pos.x <= ICONMARGSX && _iconBase)
moveInventoryRight();
else if (isBetween(MAXX - ICONMARGDX, pos.x, MAXX) && (_iconBase + ICONSHOWN < (int)_inventory.size()))
moveInventoryLeft();
}
void TrecisionEngine::syncInventory(Common::Serializer &ser) {
if (ser.isLoading()) {
_inventory.clear();
_cyberInventory.clear();
}
for (uint which = 0; which <= 1; which++) {
for (uint i = 0; i < MAXICON; i++) {
byte val = 0;
if (ser.isSaving()) {
if (which == 0)
val = i < _inventory.size() ? _inventory[i] : 0;
else
val = i < _cyberInventory.size() ? _cyberInventory[i] : 0;
ser.syncAsByte(val);
} else {
ser.syncAsByte(val);
if (val != kItemNull) {
if (which == 0)
_inventory.push_back(val);
else
_cyberInventory.push_back(val);
}
}
}
}
}
} // End of namespace Trecision

4123
engines/trecision/logic.cpp Normal file

File diff suppressed because it is too large Load Diff

103
engines/trecision/logic.h Normal file
View File

@@ -0,0 +1,103 @@
/* 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 TRECISION_LOGIC_H
#define TRECISION_LOGIC_H
#include "common/scummsys.h"
#include "common/serializer.h"
namespace Trecision {
class TrecisionEngine;
class LogicManager {
TrecisionEngine *_vm;
// panel puzzle 35
uint16 _comb35[7];
uint16 _count35;
// sundial puzzle 49
uint16 _comb49[4];
// sundial puzzle 4CT
uint16 _comb4CT[6];
// keyboard puzzle 58
uint16 _comb58[6];
uint16 _count58;
// SlotMachine41
uint16 _slotMachine41Counter;
// special management
uint16 _wheel;
uint16 _wheelPos[3];
void initInventory();
public:
LogicManager(TrecisionEngine *vm);
~LogicManager();
void syncGameStream(Common::Serializer &ser);
void setupAltRoom(uint16 room, bool altRoomFl);
void endChangeRoom();
void useInventoryWithInventory();
void useInventoryWithScreen();
bool useScreenWithScreen();
void roomOut(uint16 curObj, uint16 *action, uint16 *pos);
bool mouseExamine(uint16 curObj);
bool mouseOperate(uint16 curObj);
bool mouseTake(uint16 curObj);
bool mouseTalk(uint16 curObj);
bool mouseClick(uint16 curObj);
bool operateInventory();
void doMouseGame();
bool doMouseInventory();
void doMouseLeftRight();
void doSystemChangeRoom(uint16 room);
bool isCloseupOrControlRoom() const;
private:
void startCharacterAnimations();
bool startPlayDialog();
void initControlPanel();
void handleClickControlPanel(uint16 curObj);
void handleClickSphinxPuzzle();
void handleClickPositioner();
void handleClickSnakeEscape();
void handleClickCloseup();
void handleClickGameArea();
void handleClickInventoryArea();
void handleChangeRoomObjects();
};
// end of class
} // End of namespace Trecision
#endif

View 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/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "graphics/surface.h"
#include "common/system.h"
#include "common/savefile.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "trecision/trecision.h"
#include "trecision/detection.h"
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_ORIGINAL_SAVELOAD,
{
_s("Use original save/load screens"),
_s("Use the original save/load screens instead of the ScummVM ones"),
"originalsaveload",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class TrecisionMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
const char *getName() const override {
return "trecision";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
void getSavegameThumbnail(Graphics::Surface &thumb) override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
Common::Error TrecisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new Trecision::TrecisionEngine(syst, desc);
return Common::kNoError;
}
void TrecisionMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
// We are referencing g_engine here, but this should be safe, as this
// method is only used while the engine is running.
// TODO: Is there a better way to do this?
Trecision::TrecisionEngine *engine = (Trecision::TrecisionEngine *)g_engine;
if (engine->_controlPanelSave)
thumb.copyFrom(engine->_thumbnail);
else
MetaEngine::getSavegameThumbnail(thumb);
}
SaveStateDescriptor TrecisionMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::ScopedPtr<Common::InSaveFile> saveFile(g_system->getSavefileManager()->openForLoading(
getSavegameFile(slot, target)));
if (saveFile) {
const byte version = saveFile->readByte();
if (version >= SAVE_VERSION_ORIGINAL_MIN && version <= SAVE_VERSION_ORIGINAL_MAX) {
// Original saved game, convert
Common::String saveName = saveFile->readString(0, 40);
SaveStateDescriptor desc(this, slot, saveName);
// This is freed inside SaveStateDescriptor
const Graphics::PixelFormat kImageFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
Graphics::Surface *thumbnail = new Graphics::Surface();
thumbnail->create(ICONDX, ICONDY, kImageFormat);
saveFile->read(thumbnail->getPixels(), ICONDX * ICONDY * kImageFormat.bytesPerPixel);
desc.setThumbnail(thumbnail);
return desc;
} else if (version >= SAVE_VERSION_SCUMMVM_MIN) {
saveFile->seek(0);
return MetaEngine::querySaveMetaInfos(target, slot);
}
}
return SaveStateDescriptor();
}
Common::KeymapArray TrecisionMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Trecision;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "trecision-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game 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("SKIP", _("Skip video"));
act->setCustomEngineActionEvent(kActionSkipVideo);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
gameKeyMap->addAction(act);
// I18N: Toggles walking speed of actor
act = new Action("FASTWALK", _("Toggle fast walk"));
act->setCustomEngineActionEvent(kActionFastWalk);
act->addDefaultInputMapping("CAPSLOCK");
act->addDefaultInputMapping("JOY_CENTER");
gameKeyMap->addAction(act);
act = new Action("PAUSE", _("Pause game"));
act->setCustomEngineActionEvent(kActionPause);
act->addDefaultInputMapping("p");
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("QUIT", _("Quit game"));
act->setCustomEngineActionEvent(kActionQuit);
act->addDefaultInputMapping("q");
act->addDefaultInputMapping("Q");
act->addDefaultInputMapping("JOY_LEFT_STICK");
gameKeyMap->addAction(act);
act = new Action("SYSMENU", _("Open system menu"));
act->setCustomEngineActionEvent(kActionSystemMenu);
act->addDefaultInputMapping("F1");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
act = new Action("SAVEGAME", _("Save game"));
act->setCustomEngineActionEvent(kActionSave);
act->addDefaultInputMapping("F2");
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
gameKeyMap->addAction(act);
act = new Action("LOADGAME", _("Load game"));
act->setCustomEngineActionEvent(kActionLoad);
act->addDefaultInputMapping("F3");
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
gameKeyMap->addAction(act);
act = new Action("YESKEY", _("Press \"Yes\" key"));
act->setCustomEngineActionEvent(kActionYes);
act->addDefaultInputMapping("y");
act->addDefaultInputMapping("j");
act->addDefaultInputMapping("JOY_RIGHT_STICK");
gameKeyMap->addAction(act);
KeymapArray keymaps(2);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
return keymaps;
}
bool Trecision::TrecisionEngine::hasFeature(EngineFeature f) const {
return (f == kSupportsSubtitleOptions) ||
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsChangingOptionsDuringRuntime);
}
#if PLUGIN_ENABLED_DYNAMIC(TRECISION)
REGISTER_PLUGIN_DYNAMIC(TRECISION, PLUGIN_TYPE_ENGINE, TrecisionMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TRECISION, PLUGIN_TYPE_ENGINE, TrecisionMetaEngine);
#endif

View File

@@ -0,0 +1,36 @@
MODULE := engines/trecision
MODULE_OBJS = \
console.o \
actor.o \
animmanager.o \
animtype.o \
dialog.o \
fastfile.o \
graphics.o \
inventory.o \
logic.o \
metaengine.o \
pathfinding3d.o \
renderer3d.o \
resource.o \
sound.o \
saveload.o \
scheduler.o \
script.o \
struct.o \
text.o \
trecision.o \
utils.o \
video.o
# This module can be built as a plugin
ifeq ($(ENABLE_TRECISION), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,149 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 TRECISION_PATHFINDING_H
#define TRECISION_PATHFINDING_H
#include "trecision/struct.h"
#include "common/serializer.h"
namespace Trecision {
struct SSortPan {
int _num;
float _min;
};
struct SPathNode {
float _x, _z;
float _dist;
int16 _oldPanel;
int16 _curPanel;
void clear() {
_x = _z = 0.0f;
_dist = 0.0f;
_oldPanel = 0;
_curPanel = 0;
}
};
struct SPan {
float _x1, _z1;
float _x2, _z2;
float _h;
int _flags;
int16 _nearPanel1;
int16 _nearPanel2;
int8 _col1;
int8 _col2;
void clear() {
_x1 = _z1 = 0.0f;
_x2 = _z2 = 0.0f;
_h = 0.0f;
_flags = 0;
_nearPanel1 = _nearPanel2 = 0;
_col1 = _col2 = 0;
}
};
struct SStep {
float _px, _pz;
float _dx, _dz;
float _theta;
int _curAction;
int _curFrame;
int16 _curPanel;
void clear() {
_px = _pz = 0.0f;
_dx = _dz = 0.0f;
_theta = 0.0f;
_curAction = 0;
_curFrame = 0;
_curPanel = 0;
}
};
class TrecisionEngine;
class PathFinding3D {
TrecisionEngine *_vm;
SPathNode _pathNode[MAXPATHNODES];
float _invP[3][3];
int _numPathNodes;
float _x3d, _y3d, _z3d;
float _curX, _curZ;
float _lookX, _lookZ;
int32 _panelNum;
int16 _oldPanel;
int _actorPos;
int _forcedActorPos;
bool pointInside(int pan, float x, float z) const;
void sortPanel();
void pointOut();
void invPointProject(int x, int y);
bool intersectLinePanel(SPan *p, float x, float y, float z);
bool intersectLineFloor(float x, float y, float z);
bool intersectLineLine(float xa, float ya, float xb, float yb, float xc, float yc, float xd, float yd);
void findShortPath();
float evalPath(int a, float destX, float destZ, int nearP);
void lookAt(float x, float z);
void buildFramelist();
void displayPath();
bool findAttachedPanel(int16 srcPanel, int16 destPanel);
void sortPath();
public:
PathFinding3D(TrecisionEngine *vm);
~PathFinding3D();
int _curStep;
int _lastStep;
int16 _curPanel;
int _numSortPanel;
int8 _characterGoToPosition;
bool _characterInMovement;
SSortPan _sortPan[32];
SStep _step[MAXSTEP];
SPan _panel[MAXPANELSINROOM];
void findPath();
void setPosition(int num);
void goToPosition(int num);
int nextStep();
void initSortPan();
void read3D(Common::SeekableReadStreamEndian *ff);
void reset(uint16 idx, float px, float pz, float theta);
void whereIs(int px, int py);
void actorOrder();
void syncGameStream(Common::Serializer &ser);
int getActorPos() const { return _actorPos; }
void setForcedActorPos(int actorPos) { _forcedActorPos = actorPos; }
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,828 @@
/* 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 "trecision/actor.h"
#include "trecision/animtype.h"
#include "trecision/graphics.h"
#include "trecision/renderer3d.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
#define SHADOWVERTSNUM 42
static const int16 _shadowVerts[SHADOWVERTSNUM] = {
6, 15, 23,
24, 32, 78,
80, 81, 83,
86, 90, 99,
107, 108, 116,
155, 157, 158,
160, 164, 168,
169, 173, 174,
187, 188, 192,
193, 213, 215,
227, 229, 235,
238, 249, 250,
252, 253, 299,
306, 330, 336
};
#define SHADOWFACESNUM 48
const int16 _shadowFaces[SHADOWFACESNUM][3] = {
{22, 21, 5}, {7, 5, 22},
{7, 19, 5}, {5, 2, 19},
{27, 24, 16}, {27, 16, 18},
{18, 16, 9}, {18, 13, 9},
{13, 9, 2}, {3, 19, 12},
{25, 26, 17}, {17, 15, 25},
{17, 19, 15}, {15, 12, 19},
{20, 23, 8}, {8, 6, 20},
{6, 9, 3}, {3, 8, 6},
{12, 3, 4}, {4, 11, 12},
{35, 4, 11}, {13, 2, 1},
{1, 14, 13}, {14, 37, 1},
{1, 34, 37}, {31, 36, 37},
{37, 30, 31}, {29, 34, 35},
{35, 29, 28}, {36, 11, 31},
{30, 37, 14}, {29, 1, 34},
{28, 4, 35}, {36, 10, 35},
{35, 32, 10}, {37, 0, 34},
{37, 33, 0}, {0, 33, 39},
{39, 40, 0}, {10, 38, 32},
{32, 41, 38}, {36, 35, 34},
{36, 37, 35}, {11, 36, 35},
{38, 40, 41}, {41, 38, 39},
{2, 19, 13}, {3, 9, 12}
};
Renderer3D::Renderer3D(TrecisionEngine *vm) : _vm(vm) {
_zBuffer = new int16[ZBUFFERSIZE / 2];
_minXClip = 0;
_minYClip = 0;
_maxXClip = 0;
_maxYClip = 0;
_zBufStartX = 0;
_zBufStartY = 0;
_zBufWid = 0;
_shadowLightNum = 0;
_totalShadowVerts = 0;
// data for the triangle routines
for (int i = 0; i < 480; ++i) {
_lEdge[i] = 0;
_rEdge[i] = 0;
_lColor[i] = 0;
_rColor[i] = 0;
_lZ[i] = 0;
_rZ[i] = 0;
_lTextX[i] = 0;
_rTextX[i] = 0;
_lTextY[i] = 0;
_rTextY[i] = 0;
}
for (int i = 0; i < 10; ++i)
_shadowIntens[i] = 0;
for (int i = 0; i < MAXVERTEX; ++i) {
_vVertex[i].clear();
_shVertex[i].clear();
}
}
Renderer3D::~Renderer3D() {
delete[] _zBuffer;
}
void Renderer3D::textureTriangle(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1,
int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2,
int32 x3, int32 y3, int32 z3, int32 c3, int32 tx3, int32 ty3,
const STexture *t) {
if (y1 > _maxYClip)
y1 = _maxYClip;
if (y1 < _minYClip)
y1 = _minYClip;
int16 yBottom = y1;
int16 yTop = y1;
const uint8 *texture = t->_texture;
if (yBottom > y2) {
if (y2 < _minYClip)
y2 = _minYClip;
yBottom = y2;
}
if (yTop < y2) {
if (y2 > _maxYClip)
y2 = _maxYClip;
yTop = y2;
}
if (yBottom > y3) {
if (y3 < _minYClip)
y3 = _minYClip;
yBottom = y3;
}
if (yTop < y3) {
if (y3 > _maxYClip)
y3 = _maxYClip;
yTop = y3;
}
for (int16 y = yBottom; y < yTop; ++y) {
_lEdge[y] = _maxXClip;
_rEdge[y] = _minXClip;
}
// scan the edges of the triangle
textureScanEdge(x1, y1, z1, c1, tx1, ty1, x2, y2, z2, c2, tx2, ty2);
textureScanEdge(x2, y2, z2, c2, tx2, ty2, x3, y3, z3, c3, tx3, ty3);
textureScanEdge(x3, y3, z3, c3, tx3, ty3, x1, y1, z1, c1, tx1, ty1);
// Gouraud fill the horizontal scanlines
for (int16 y = yBottom; y < yTop; ++y) {
int32 el = _lEdge[y];
if (el < _minXClip)
el = _minXClip;
int32 er = _rEdge[y];
if (er > _maxXClip)
er = _maxXClip;
// edge right - edge left
int16 dx = er - el;
if (dx > 0) {
// color of left edge of horizontal scanline
int32 cl = _lColor[y];
// slope dc/_dx
int32 mc = ((int16)(_rColor[y] - cl) << 8) / dx;
// zbuffer of left edge of horizontal scanline
int32 zl = _lZ[y];
// slope _dz/_dx
int32 mz = ((int32)(_rZ[y] - zl) << 16) / dx;
// texture x of left edge of horizontal scanline
int32 olx = _lTextX[y];
// slope dty/_dx
int32 mtx = ((int32)(_rTextX[y] - olx) << 16) / dx;
// texture y of left edge of horizontal scanline
int32 oly = _lTextY[y];
// slope dty/_dx
int32 mty = ((int32)(_rTextY[y] - oly) << 16) / dx;
// pointer to zbuffer
int16 *z = _zBuffer + (y - _zBufStartY) * _zBufWid + (el - _zBufStartX);
uint16 x = el;
zl <<= 16;
cl <<= 8;
olx <<= 16;
oly <<= 16;
// loop through every pixel in horizontal scanline
while (dx) {
const int32 screenOffset = zl >> 16;
if (*z > screenOffset) {
const uint16 textureX = (uint16)(cl >> 9);
const uint16 textureY = texture[(olx >> 16) + t->_dx * (oly >> 16)];
_vm->_graphicsMgr->drawTexturePixel(textureX, textureY, x, y);
*z = (int16)screenOffset;
}
++x; // increase screen x
++z; // increase zbuffer
zl += mz; // increase the zbuffer by _dz/_dx
cl += mc; // increase the color by dc/_dx
olx += mtx;
oly += mty;
--dx; // pixel to do --
}
}
}
}
void Renderer3D::textureScanEdge(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1, int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2) {
// make sure that edge goes from top to bottom
int16 dy = y2 - y1;
if (dy < 0) {
SWAP(y1, y2);
SWAP(x1, x2);
SWAP(c1, c2);
SWAP(z1, z2);
SWAP(tx1, tx2);
SWAP(ty1, ty2);
dy = -dy;
}
if (dy == 0)
dy = 1;
// initialize for stepping
int32 mx = ((x2 - x1) << 16) / dy; // dx/dy
int32 mz = ((z2 - z1) << 16) / dy; // dz/dy
int32 mc = ((c2 - c1) << 8) / dy; // dc/dy
int32 mtx = ((tx2 - tx1) << 16) / dy;
int32 mty = ((ty2 - ty1) << 16) / dy;
x1 <<= 16; // starting x coordinate
z1 <<= 16; // starting z coordinate
c1 <<= 8; // starting c color
tx1 <<= 16;
ty1 <<= 16;
// step through edge and record color values along the way
for (int32 count = y1; count < y2; ++count) {
int16 x = (uint16)(x1 >> 16);
if (x < _lEdge[count]) {
_lEdge[count] = x;
_lZ[count] = (int16)(z1 >> 16);
_lTextX[count] = (uint16)(tx1 >> 16);
_lTextY[count] = (uint16)(ty1 >> 16);
_lColor[count] = (uint8)(c1 >> 8);
}
if (x > _rEdge[count]) {
_rEdge[count] = x;
_rZ[count] = (int16)(z1 >> 16);
_rTextX[count] = (uint16)(tx1 >> 16);
_rTextY[count] = (uint16)(ty1 >> 16);
_rColor[count] = (uint8)(c1 >> 8);
}
x1 += mx; // x = x + dx/dy
c1 += mc; // c = c + dc/dy
z1 += mz; // z = z + dz/dy
tx1 += mtx;
ty1 += mty;
}
}
void Renderer3D::shadowTriangle(int32 x1, int32 y1, int32 x2, int32 y2,
int32 x3, int32 y3, uint8 cv, int32 zv) {
if (y1 > _maxYClip)
y1 = _maxYClip;
if (y1 < _minYClip)
y1 = _minYClip;
int16 yBottom = y1;
int16 yTop = y1;
if (yBottom > y2) {
if (y2 < _minYClip)
y2 = _minYClip;
yBottom = y2;
}
if (yTop < y2) {
if (y2 > _maxYClip)
y2 = _maxYClip;
yTop = y2;
}
if (yBottom > y3) {
if (y3 < _minYClip)
y3 = _minYClip;
yBottom = y3;
}
if (yTop < y3) {
if (y3 > _maxYClip)
y3 = _maxYClip;
yTop = y3;
}
for (int16 y = yBottom; y < yTop; ++y) {
_lEdge[y] = _maxXClip;
_rEdge[y] = _minXClip;
}
// scan the edges of the triangle
shadowScanEdge(x1, y1, x2, y2);
shadowScanEdge(x2, y2, x3, y3);
shadowScanEdge(x3, y3, x1, y1);
// gouraud fill the horizontal scanlines
for (int16 y = yBottom; y < yTop; ++y) {
// coordinate of left edge of horizontal scanline
int32 el = _lEdge[y];
if (el < _minXClip)
el = _minXClip;
// coordinate of right edge of horizontal scanline
int32 er = _rEdge[y];
if (er > _maxXClip)
er = _maxXClip;
// edge right - edge left
int16 dx = er - el;
if (dx > 0) {
// screen offset
int16 x = el;
int16 *zBufferPtr = _zBuffer + (y - _zBufStartY) * _zBufWid + (el - _zBufStartX);
// loop through every pixel in horizontal scanline
while (dx) {
if (*zBufferPtr != zv) {
_vm->_graphicsMgr->shadow(x, y, cv);
*zBufferPtr = zv;
}
++x; // increase screen x
++zBufferPtr; // increase zbuffer
--dx; // pixel to do --
}
}
}
}
void Renderer3D::shadowScanEdge(int32 x1, int32 y1, int32 x2, int32 y2) {
// make sure that edge goes from top to bottom
int16 dy = y2 - y1;
if (dy < 0) {
SWAP(y1, y2);
SWAP(x1, x2);
dy = -dy;
}
if (dy == 0)
dy = 1;
// initialize for stepping
int32 mx = ((x2 - x1) << 16) / dy; // slope dx/dy
x1 <<= 16; // starting x coordinate
// step through edge and record color values along the way
for (int32 count = y1; count < y2; ++count) {
int16 x = (int16)(x1 >> 16);
if (x < _lEdge[count])
_lEdge[count] = x;
if (x > _rEdge[count])
_rEdge[count] = x;
x1 += mx; // x = x + dx/dy
}
}
/**
* Initialize a 3D Room
*/
void Renderer3D::init3DRoom() {
_vm->_cx = (MAXX - 1) / 2;
_vm->_cy = (MAXY - 1) / 2;
for (int c = 0; c < ZBUFFERSIZE / 2; ++c)
_zBuffer[c] = 0x7FFF;
}
void Renderer3D::resetZBuffer(Common::Rect area) {
if (!area.isValidRect())
return;
int size = area.width() * area.height();
if (size * 2 > ZBUFFERSIZE)
warning("Warning: _zBuffer size %d!\n", size * 2);
int16 *d = _zBuffer;
for (int i = 0; i < size; ++i)
*d++ = 0x7FFF;
}
/**
* Change the clipping area
*/
void Renderer3D::setClipping(int16 x1, int16 y1, int16 x2, int16 y2) {
_minXClip = x1;
_minYClip = y1;
_maxXClip = x2;
_maxYClip = y2;
}
void Renderer3D::setZBufferRegion(int16 sx, int16 sy, int16 dx) {
_zBufStartX = sx;
_zBufStartY = sy;
_zBufWid = dx;
}
/**
* Determines whether a triangle has clockwise
* or counterclockwise vertices
*/
int8 Renderer3D::clockWise(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3) {
x2 -= x1;
y2 -= y1;
x3 -= x1;
y3 -= y1;
int32 a1 = ((int32)x2) * y3;
int32 a2 = ((int32)y2) * x3;
if (a1 > a2)
return 1; // clockwise
if (a1 < a2)
return -1; // counterclockwise
a1 = ((int32)x2) * x3;
a2 = ((int32)y2) * y3;
if (a1 < 0 || a2 < 0)
return -1;
a1 = ((int32)x2) * x2 + ((int32)y2) * y2;
a2 = ((int32)x3) * x3 + ((int32)y3) * y3;
if (a1 < a2)
return 1;
return 0;
}
void Renderer3D::calcCharacterPoints() {
Actor *actor = _vm->_actor;
SCamera *camera = actor->_camera;
SLight *light = actor->_light;
int vertexNum = actor->_vertexNum;
if (actor->_curAction > hLAST)
error("Error in drawCharacter() - _curAction > hLAST");
int cfp = 0;
int cur = 0;
while (cur < actor->_curAction)
cfp += defActionLen[cur++];
if (actor->_curAction == hWALKOUT)
cfp = 1;
cfp += actor->_curFrame;
if (actor->_curAction == hLAST)
cfp = 0;
actor->_vertex = &actor->_characterArea[cfp * actor->_vertexNum];
_shadowLightNum = 0;
_totalShadowVerts = 0;
// camera matrix
float e10 = camera->_e1[0];
float e11 = camera->_e1[1];
float e12 = camera->_e1[2];
float e20 = camera->_e2[0];
float e21 = camera->_e2[1];
float e22 = camera->_e2[2];
float e30 = camera->_e3[0];
float e31 = camera->_e3[1];
float e32 = camera->_e3[2];
// Light directions
float l0 = 0.0f;
float l1 = 0.0f;
float l2 = 0.0f;
actor->_area[0] = 32000;
actor->_area[1] = -32000;
actor->_area[2] = 32000;
actor->_area[3] = -32000;
actor->_area[4] = 32000;
actor->_area[5] = -32000;
float t = (actor->_theta * M_PI * 2) / 360.0;
float cost = cos(t);
float sint = sin(t);
// Put all vertices in dark color
for (int i = 0; i < MAXVERTEX; ++i)
_vVertex[i]._angle = 180;
float dist;
float tx = 0.0f;
float ty = 0.0f;
float tz = 0.0f;
float pa0, pa1, pa2;
for (uint32 i = 0; i < actor->_lightNum; ++i) {
// if off lint == 0
// if it has a shadow lint & 0x80
int lint = light->_inten & 0x7F;
if (lint) { // if it's not turned off
tx = light->_x - actor->_px - actor->_dx; // computes direction vector
tz = light->_z - actor->_pz - actor->_dz; // between light and actor
ty = light->_y;
if (light->_position) { // if it's attenuated
dist = sqrt(tx * tx + ty * ty + tz * tz); // Distance light <--> actor
// adjust light intensity due to the distance
if (_vm->floatComp(dist, light->_outr) == 1) // if it's out of range it's off
lint = 0;
else if (_vm->floatComp(dist, light->_inr) == 1) // if it's inside the circle it's decreased
lint = (int)((float)lint * (light->_outr - dist) / (light->_outr - light->_inr));
}
}
if (lint) { // If it's still on
// Light rotates around the actor
l0 = tx * cost - tz * sint;
l2 = tx * sint + tz * cost;
l1 = ty;
t = sqrt(l0 * l0 + l1 * l1 + l2 * l2);
l0 /= t;
l1 /= t;
l2 /= t;
// Adjust light intensity according to the spot
tx = (float)light->_fallOff;
if (light->_fallOff) { // for light spot only
ty = (float)light->_hotspot;
pa0 = light->_dx * cost - light->_dz * sint;
pa1 = light->_dy;
pa2 = light->_dx * sint + light->_dz * cost;
t = sqrt(pa0 * pa0 + pa1 * pa1 + pa2 * pa2);
pa0 /= t;
pa1 /= t;
pa2 /= t;
tz = acos((pa0 * l0) + (pa1 * l1) + (pa2 * l2)) * 360.0 / (M_PI * 2);
tz = CLIP(tz, 0.f, 180.f);
// tx falloff
// ty hotspot
// tz current angle
_shadowIntens[_shadowLightNum] = SHADOWAMBIENT;
if (_vm->floatComp(tz, tx) == 1) { // tz > tx - if it's out of the falloff
lint = 0;
_shadowIntens[_shadowLightNum] = 0;
} else if (_vm->floatComp(tz, ty) == 1) { // tz > ty - if it's between the falloff and the hotspot
lint = (int)((float)lint * (tx - tz) / (tx - ty));
_shadowIntens[_shadowLightNum] = (int)((float)_shadowIntens[_shadowLightNum] * (tx - tz) / (tx - ty));
}
}
}
if ((light->_inten & 0x80) && lint) { // if it's shadowed and still on
// casts shadow vertices
for (int j = 0; j < SHADOWVERTSNUM; ++j) {
pa0 = actor->_vertex[_shadowVerts[j]]._x;
pa1 = actor->_vertex[_shadowVerts[j]]._y;
pa2 = actor->_vertex[_shadowVerts[j]]._z;
_shVertex[vertexNum + _totalShadowVerts + j]._x = pa0 - (pa1 * l0);
_shVertex[vertexNum + _totalShadowVerts + j]._z = pa2 - (pa1 * l2);
_shVertex[vertexNum + _totalShadowVerts + j]._y = 0;
}
// per default all shadows are equally faint
// _shadowIntens[_shadowLightNum] = SHADOWAMBIENT;
++_shadowLightNum;
_totalShadowVerts += SHADOWVERTSNUM;
}
if (lint) { // if still on
// adapts the light vector o its intensity
t = (float)(lint) / 127.0;
l0 = l0 * t;
l1 = l1 * t;
l2 = l2 * t;
SVertex *curVertex = actor->_vertex;
for (int j = 0; j < vertexNum; ++j) {
pa0 = curVertex->_nx;
pa1 = curVertex->_ny;
pa2 = curVertex->_nz;
lint = (int)((acos(pa0 * l0 + pa1 * l1 + pa2 * l2) * 360.0) / M_PI);
lint = CLIP(lint, 0, 180);
_vVertex[j]._angle -= (180 - lint);
++curVertex;
}
}
++light;
}
// rearranged light values so they can be viewed
for (int i = 0; i < vertexNum; ++i)
_vVertex[i]._angle = CLIP<int32>(_vVertex[i]._angle, 0, 180);
// Calculate the distance of the character from the room
tx = camera->_ex - actor->_px;
ty = camera->_ey;
tz = camera->_ez - actor->_pz;
dist = tx * e30 + ty * e31 + tz * e32;
SVertex *curVertex = actor->_vertex;
for (int i = 0; i < vertexNum + _totalShadowVerts; ++i) {
if (i < vertexNum) {
l0 = curVertex->_x;
l1 = curVertex->_z;
pa1 = ty - curVertex->_y;
} else {
l0 = _shVertex[i]._x;
l1 = _shVertex[i]._z;
pa1 = ty - _shVertex[i]._y;
}
pa0 = tx - (l0 * cost + l1 * sint); // rotate _curVertex
pa2 = tz - (-l0 * sint + l1 * cost);
l0 = pa0 * e10 + pa1 * e11 + pa2 * e12; // project _curVertex
l1 = pa0 * e20 + pa1 * e21 + pa2 * e22;
l2 = pa0 * e30 + pa1 * e31 + pa2 * e32;
int x2d = _vm->_cx + (int)((l0 * camera->_fovX) / l2);
int y2d = _vm->_cy + (int)((l1 * camera->_fovY) / l2);
_vVertex[i]._x = x2d;
_vVertex[i]._y = y2d;
_vVertex[i]._z = (int32)((dist - l2) * 128.0);
actor->_area[0] = MIN(x2d, actor->_area[0]);
actor->_area[1] = MAX(x2d, actor->_area[1]);
actor->_area[2] = MIN(y2d, actor->_area[2]);
actor->_area[3] = MAX(y2d, actor->_area[3]);
actor->_area[4] = MIN<int32>(_vVertex[i]._z, actor->_area[4]);
actor->_area[5] = MAX<int32>(_vVertex[i]._z, actor->_area[5]);
++curVertex;
}
actor->_area[4] = (int)dist;
actor->_area[5] = (int)dist;
// vertex clipping
if (actor->_area[0] <= _minXClip + 1) {
actor->_area[0] = _minXClip;
} else {
--actor->_area[0];
}
if (actor->_area[1] >= _maxXClip - 1) {
actor->_area[1] = _maxXClip;
} else {
++actor->_area[1];
}
if (actor->_area[2] <= _minYClip + 1) {
actor->_area[2] = _minYClip;
} else {
--actor->_area[2];
}
if (actor->_area[3] >= _maxYClip - 1) {
actor->_area[3] = _maxYClip;
} else {
++actor->_area[3];
}
if (actor->_curAction == hLAST) // exit displacer
actor->_area[2] = actor->_area[3] - (((actor->_area[3] - actor->_area[2]) * actor->_curFrame) / defActionLen[hLAST]);
// set zbuffer vars
setZBufferRegion(actor->_area[0], actor->_area[2], actor->_area[1] - actor->_area[0]);
}
void Renderer3D::drawCharacterFaces() {
Actor *actor = _vm->_actor;
STexture *textures = actor->_textures;
SFace *face = actor->_face;
int vertexNum = actor->_vertexNum;
if (actor->_curAction == hLAST)
setClipping(0, actor->_area[2], MAXX, actor->_area[3]);
for (int i = 0; i < _shadowLightNum; ++i) {
for (int j = 0; j < SHADOWFACESNUM; ++j) {
int p0 = _shadowFaces[j][0] + vertexNum + i * SHADOWVERTSNUM;
int p1 = _shadowFaces[j][1] + vertexNum + i * SHADOWVERTSNUM;
int p2 = _shadowFaces[j][2] + vertexNum + i * SHADOWVERTSNUM;
int px0 = _vVertex[p0]._x;
int py0 = _vVertex[p0]._y;
int px1 = _vVertex[p1]._x;
int py1 = _vVertex[p1]._y;
int px2 = _vVertex[p2]._x;
int py2 = _vVertex[p2]._y;
shadowTriangle(px0, py0, px1, py1, px2, py2, 127 - _shadowIntens[i], (int16)(0x7FF0 + i));
}
}
for (uint i = 0; i < actor->_faceNum; ++i) {
int p0 = face->_a;
int p1 = face->_b;
int p2 = face->_c;
int px0 = _vVertex[p0]._x;
int py0 = _vVertex[p0]._y;
int px1 = _vVertex[p1]._x;
int py1 = _vVertex[p1]._y;
int px2 = _vVertex[p2]._x;
int py2 = _vVertex[p2]._y;
if (clockWise(px0, py0, px1, py1, px2, py2) > 0) {
uint16 textureId = face->_mat;
if (textureId < MAXMAT && textures[textureId].isActive()) {
textureTriangle(px0, py0, _vVertex[p0]._z, _vVertex[p0]._angle, actor->_textureCoord[i][0][0], actor->_textureCoord[i][0][1],
px1, py1, _vVertex[p1]._z, _vVertex[p1]._angle, actor->_textureCoord[i][1][0], actor->_textureCoord[i][1][1],
px2, py2, _vVertex[p2]._z, _vVertex[p2]._angle, actor->_textureCoord[i][2][0], actor->_textureCoord[i][2][1],
&textures[textureId]);
}
}
++face;
}
int p0 = 0;
for (int i = _zBufStartY; i < actor->_area[3]; ++i) {
for (int j = 1; j < _zBufWid; ++j) {
int py1 = (_zBuffer[p0] >= 0x7FF0) * 0x8000;
int py2 = (_zBuffer[p0 + 1] >= 0x7FF0) * 0x8000;
int p1 = _zBuffer[p0] < 0x7FFF;
int p2 = _zBuffer[p0 + 1] < 0x7FFF;
if (p1 != p2) {
_vm->_graphicsMgr->pixelAliasing(j + _zBufStartX, i);
// if the first is the character
if (p1)
_zBuffer[p0] = 0x00BF | py1;
else
_zBuffer[p0] = 0x003F | py2;
if (j + 1 < _zBufWid) {
++p0;
++j;
// if the second is the character
if (p2)
_zBuffer[p0] = 0x00BF | py2;
else
_zBuffer[p0] = 0x003F | py1;
}
} else {
// set value alpha max
if (p1)
_zBuffer[p0] = 0x00FF | py1;
else
_zBuffer[p0] = 0x0000 | py1;
}
++p0;
// if it's the last of the line
if (j == _zBufWid - 1) {
if (p2)
_zBuffer[p0] = 0x00FF | py2;
else
_zBuffer[p0] = 0x0000 | py2;
}
}
++p0;
}
if (actor->_curAction == hLAST)
setClipping(0, TOP, MAXX, AREA + TOP);
}
/**
* Draw the character
*/
void Renderer3D::drawCharacter(uint8 flag) {
if (!_vm->_flagShowCharacter)
return;
// Compute pointer to frame
if (flag & CALCPOINTS)
calcCharacterPoints();
if (flag & DRAWFACES)
drawCharacterFaces();
}
} // End of namespace Trecision

View 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/>.
*
*/
#ifndef TRECISION_RENDERER3D_H
#define TRECISION_RENDERER3D_H
#include "trecision/struct.h"
namespace Trecision {
struct SVVertex {
int32 _x, _y, _z;
int32 _angle;
void clear() {
_x = _y = _z = 0;
_angle = 0;
}
};
class TrecisionEngine;
class Renderer3D {
TrecisionEngine *_vm;
int16 _minXClip;
int16 _minYClip;
int16 _maxXClip;
int16 _maxYClip;
int16 *_zBuffer;
int16 _zBufStartX;
int16 _zBufStartY;
int16 _zBufWid;
int16 _shadowLightNum;
int16 _totalShadowVerts;
uint8 _shadowIntens[10];
SVVertex _vVertex[MAXVERTEX];
SVertex _shVertex[MAXVERTEX];
// data for the triangle routines
int16 _lEdge[480];
int16 _rEdge[480];
uint8 _lColor[480];
uint8 _rColor[480];
int16 _lZ[480];
int16 _rZ[480];
uint16 _lTextX[480];
uint16 _rTextX[480];
uint16 _lTextY[480];
uint16 _rTextY[480];
void setZBufferRegion(int16 sx, int16 sy, int16 dx);
int8 clockWise(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3);
void textureTriangle(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1, int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2, int32 x3, int32 y3, int32 z3, int32 c3, int32 tx3, int32 ty3, const STexture *t);
void textureScanEdge(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1, int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2);
void shadowTriangle(int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, uint8 cv, int32 zv);
void shadowScanEdge(int32 x1, int32 y1, int32 x2, int32 y2);
void calcCharacterPoints();
void drawCharacterFaces();
public:
Renderer3D(TrecisionEngine *vm);
~Renderer3D();
void init3DRoom();
void resetZBuffer(Common::Rect area);
void setClipping(int16 x1, int16 y1, int16 x2, int16 y2);
void drawCharacter(uint8 flag);
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,227 @@
/* 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/scummsys.h"
#include "common/system.h"
#include "common/file.h"
#include "common/savefile.h"
#include "common/str.h"
#include "common/substream.h"
#include "gui/saveload.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/pathfinding3d.h"
#include "trecision/renderer3d.h"
#include "trecision/trecision.h"
#include "trecision/sound.h"
#include "trecision/video.h"
namespace Trecision {
Common::SeekableReadStreamEndian *TrecisionEngine::readEndian(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose) {
return new Common::SeekableReadStreamEndianWrapper(stream, isAmiga(), dispose);
}
void TrecisionEngine::loadAll() {
Common::File dataNl;
if (!dataNl.open("DATA.NL"))
error("loadAll : Couldn't open DATA.NL");
Common::SeekableReadStreamEndian *data = readEndian(&dataNl, DisposeAfterUse::NO);
for (int i = 0; i < MAXROOMS; ++i)
_room[i].loadRoom(data);
for (int i = 0; i < MAXOBJ; ++i)
_obj[i].loadObj(data);
for (int i = 0; i < MAXINVENTORY; ++i)
_inventoryObj[i].loadObj(data);
_soundMgr->loadSamples(data);
// TODO: Unused Amiga data?
if (isAmiga()) {
data->skip(12 * 12 + 1);
}
for (int i = 0; i < MAXSCRIPTFRAME; ++i) {
_scriptFrame[i]._class = data->readByte();
_scriptFrame[i]._event = data->readByte();
_scriptFrame[i]._u8Param = data->readByte();
data->readByte(); // Padding
_scriptFrame[i]._u16Param1 = data->readUint16();
_scriptFrame[i]._u16Param2 = data->readUint16();
_scriptFrame[i]._u32Param = data->readUint16();
_scriptFrame[i]._noWait = !(data->readSint16() == 0);
}
for (int i = 0; i < MAXSCRIPT; ++i) {
_scriptFirstFrame[i] = data->readUint16();
data->readByte(); // unused field
data->readByte(); // Padding
}
_animMgr->loadAnimTab(data);
_dialogMgr->loadData(data);
data->skip(620); // actions (unused)
int numFileRef = data->readSint32();
data->skip(numFileRef * (12 + 4)); // fileRef name + offset
_textArea = new char[MAXTEXTAREA];
data->read(_textArea, MAXTEXTAREA);
_textPtr = _textArea;
for (int i = 0; i < MAXOBJNAME; i++)
_objName[i] = getNextSentence();
for (int i = 0; i < MAXSENTENCE; i++)
_sentence[i] = getNextSentence();
for (int i = 0; i < MAXSYSTEXT; i++)
_sysText[i] = getNextSentence();
delete data;
dataNl.close();
}
byte *TrecisionEngine::readData(const Common::Path &fileName) {
Common::SeekableReadStream *stream = _dataFile.createReadStreamForMember(fileName);
if (stream == nullptr)
error("readData(): File %s not found", fileName.toString().c_str());
byte *buf = new byte[stream->size()];
stream->read(buf, stream->size());
delete stream;
return buf;
}
void TrecisionEngine::read3D(const Common::Path &filename) {
Common::SeekableReadStreamEndian *ff = readEndian(_dataFile.createReadStreamForMember(filename));
if (ff == nullptr)
error("read3D: Can't open 3D file %s", filename.toString().c_str());
_actor->read3D(ff);
_pathFind->read3D(ff);
delete ff;
_cx = 320;
_cy = 240;
_pathFind->initSortPan();
_renderer->init3DRoom();
_renderer->setClipping(0, TOP, MAXX, AREA + TOP);
}
void TrecisionEngine::readObject(Common::SeekableReadStream *stream, uint16 objIndex, uint16 objectId) {
SObject *obj = &_obj[objectId];
if (obj->isModeFull()) {
obj->readRect(stream);
uint32 size = obj->_rect.width() * obj->_rect.height();
delete[] _objectGraphics[objIndex].buf;
_objectGraphics[objIndex].buf = new uint16[size];
for (uint32 i = 0; i < size; ++i)
_objectGraphics[objIndex].buf[i] = _graphicsMgr->convertToScreenFormat(stream->readUint16LE());
}
if (obj->isModeMask()) {
obj->readRect(stream);
uint32 size = stream->readUint32LE();
delete[] _objectGraphics[objIndex].buf;
_objectGraphics[objIndex].buf = new uint16[size];
for (uint32 i = 0; i < size; ++i)
_objectGraphics[objIndex].buf[i] = _graphicsMgr->convertToScreenFormat(stream->readUint16LE());
size = stream->readUint32LE();
delete[] _objectGraphics[objIndex].mask;
_objectGraphics[objIndex].mask = new uint8[size];
for (uint32 i = 0; i < size; ++i)
_objectGraphics[objIndex].mask[i] = (uint8)stream->readByte();
}
refreshObject(objectId);
}
void TrecisionEngine::readObj(Common::SeekableReadStream *stream) {
if (!_room[_curRoom]._object[0])
return;
for (uint16 objIndex = 0; objIndex < MAXOBJINROOM; objIndex++) {
const uint16 objectId = _room[_curRoom]._object[objIndex];
if (!objectId)
break;
if (_curRoom == kRoom41D && objIndex == PATCHOBJ_ROOM41D)
break;
if (_curRoom == kRoom2C && objIndex == PATCHOBJ_ROOM2C)
break;
readObject(stream, objIndex, objectId);
}
}
void TrecisionEngine::readExtraObj2C() {
if (!_room[_curRoom]._object[32])
return;
Common::SeekableReadStream *ff = _dataFile.createReadStreamForMember("2c2.bm");
for (uint16 objIndex = PATCHOBJ_ROOM2C; objIndex < MAXOBJINROOM; objIndex++) {
const uint16 objectId = _room[_curRoom]._object[objIndex];
if (!objectId)
break;
readObject(ff, objIndex, objectId);
}
delete ff;
}
void TrecisionEngine::readPositionerSnapshots() {
if (!_room[_curRoom]._object[32])
return;
Common::SeekableReadStream *ff = _dataFile.createReadStreamForMember("41d2.bm");
for (uint16 objIndex = PATCHOBJ_ROOM41D; objIndex < MAXOBJINROOM; objIndex++) {
const uint16 objectId = _room[_curRoom]._object[objIndex];
if (!objectId)
break;
readObject(ff, objIndex, objectId);
}
delete ff;
}
} // End of namespace Trecision

View File

@@ -0,0 +1,467 @@
/* 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 "gui/saveload.h"
#include "common/config-manager.h"
#include "common/savefile.h"
#include "common/translation.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/logic.h"
#include "trecision/pathfinding3d.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
void TrecisionEngine::loadSaveSlots(Common::StringArray &saveNames) {
for (uint i = 0; i < ICONSHOWN; ++i) {
SaveStateDescriptor saveState = getMetaEngine()->querySaveMetaInfos(_targetName.c_str(), i + 1);
if (saveState.getSaveSlot() == -1) {
saveNames.push_back(_sysText[kMessageEmptySpot]);
_inventory.push_back(EMPTYSLOT);
} else {
saveNames.push_back(saveState.getDescription());
_inventory.push_back(EMPTYSLOT + i + 1);
_graphicsMgr->setSaveSlotThumbnail(i, saveState.getThumbnail());
}
}
refreshInventory(0, 0);
}
bool TrecisionEngine::dataSave() {
const Common::Array<byte> savedInventory = _inventory;
const uint8 savedIconBase = _iconBase;
Common::StringArray saveNames;
saveNames.reserve(MAXSAVEFILE);
uint16 posx, LenText;
bool ret = true;
_actor->actorStop();
_pathFind->nextStep();
if (!ConfMan.getBool("originalsaveload")) {
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
int saveSlot = dialog->runModalWithCurrentTarget();
Common::String saveName = dialog->getResultString();
bool skipSave = saveSlot == -1;
delete dialog;
// Remove the mouse click event from the save/load dialog
eventLoop();
_mouseLeftBtn = _mouseRightBtn = false;
if (!skipSave)
saveGameState(saveSlot, saveName);
return skipSave;
}
_graphicsMgr->clearScreenBufferTop();
SDText drawText;
drawText.set(
Common::Rect(0, TOP - 20, MAXX, CARHEI + (TOP - 20)),
Common::Rect(0, 0, MAXX, CARHEI),
MOUSECOL,
_sysText[kMessageSavePosition]);
drawText.draw(this);
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
_graphicsMgr->clearScreenBufferInventory();
_graphicsMgr->copyToScreen(0, TOP + AREA, MAXX, TOP);
_scheduler->resetQueues();
freeKey();
// Reset the inventory and turn it into save slots
_inventory.clear();
_iconBase = 0;
insave:
int8 CurPos = -1;
int8 OldPos = -1;
bool skipSave = false;
loadSaveSlots(saveNames);
for (;;) {
checkSystem();
getKey();
int16 mx = _mousePos.x;
int16 my = _mousePos.y;
if (my >= FIRSTLINE &&
my < FIRSTLINE + ICONDY &&
mx >= ICONMARGSX &&
mx < MAXX - ICONMARGDX) {
OldPos = CurPos;
CurPos = ((mx - ICONMARGSX) / ICONDX);
if (OldPos != CurPos) {
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
posx = ICONMARGSX + ((CurPos) * (ICONDX)) + ICONDX / 2;
LenText = textLength(saveNames[CurPos]);
posx = CLIP(posx - (LenText / 2), 2, MAXX - 2 - LenText);
drawText.set(
Common::Rect(posx, FIRSTLINE + ICONDY + 10, LenText + posx, CARHEI + (FIRSTLINE + ICONDY + 10)),
Common::Rect(0, 0, LenText, CARHEI),
MOUSECOL,
saveNames[CurPos].c_str());
drawText.draw(this);
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
}
if (_mouseLeftBtn) {
_mouseLeftBtn = false;
break;
}
} else {
if (OldPos != -1) {
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
}
OldPos = -1;
CurPos = -1;
if (_mouseLeftBtn || _mouseRightBtn) {
_mouseLeftBtn = _mouseRightBtn = false;
skipSave = true;
break;
}
}
}
if (!skipSave) {
if (_inventory[CurPos] == EMPTYSLOT) {
saveNames[CurPos].clear();
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
}
for (;;) {
_keybInput = true;
checkSystem();
uint16 ch = getKey();
freeKey();
_keybInput = false;
if (ch == 0x1B) {
ch = 0;
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
goto insave;
}
if (ch == 8) // Backspace
saveNames[CurPos].deleteLastChar();
else if (ch == 13) // Enter
break;
else if (saveNames[CurPos].size() < 39 && Common::isPrint(ch))
saveNames[CurPos] += ch;
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
saveNames[CurPos] += '_'; // add blinking cursor
posx = ICONMARGSX + ((CurPos) * (ICONDX)) + ICONDX / 2;
LenText = textLength(saveNames[CurPos]);
posx = CLIP(posx - (LenText / 2), 2, MAXX - 2 - LenText);
drawText.set(
Common::Rect(posx, FIRSTLINE + ICONDY + 10, LenText + posx, CARHEI + (FIRSTLINE + ICONDY + 10)),
Common::Rect(0, 0, LenText, CARHEI),
MOUSECOL,
saveNames[CurPos].c_str());
const bool hideLastChar = (readTime() / 8) & 1;
drawText.draw(this, hideLastChar);
saveNames[CurPos].deleteLastChar(); // remove blinking cursor
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
}
_graphicsMgr->clearScreenBufferInventory();
ret = false;
// Restore the inventory
_inventory = savedInventory;
_curInventory = 0;
_iconBase = savedIconBase;
saveGameState(CurPos + 1, saveNames[CurPos]);
}
_graphicsMgr->clearScreenBufferInventory();
_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, TOP);
_graphicsMgr->clearScreenBufferTop();
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
// Restore the inventory
_inventory = savedInventory;
_curInventory = 0;
_iconBase = savedIconBase;
return ret;
}
bool TrecisionEngine::dataLoad() {
const Common::Array<byte> savedInventory = _inventory;
const uint8 savedIconBase = _iconBase;
Common::StringArray saveNames;
saveNames.reserve(MAXSAVEFILE);
bool retval = true;
if (!ConfMan.getBool("originalsaveload")) {
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
int saveSlot = dialog->runModalWithCurrentTarget();
bool skipLoad = saveSlot == -1;
delete dialog;
// Remove the mouse click event from the save/load dialog
eventLoop();
_mouseLeftBtn = _mouseRightBtn = false;
if (!skipLoad)
loadGameState(saveSlot);
return !skipLoad;
}
_graphicsMgr->clearScreenBufferTop();
_graphicsMgr->showCursor();
SDText drawText;
drawText.set(
Common::Rect(0, TOP - 20, MAXX, CARHEI + (TOP - 20)),
Common::Rect(0, 0, MAXX, CARHEI),
MOUSECOL,
_sysText[kMessageLoadPosition]);
drawText.draw(this);
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
_graphicsMgr->clearScreenBufferInventory();
_graphicsMgr->copyToScreen(0, TOP + AREA, MAXX, TOP);
_scheduler->resetQueues();
freeKey();
// Reset the inventory and turn it into save slots
_inventory.clear();
_iconBase = 0;
loadSaveSlots(saveNames);
bool skipLoad = false;
int8 curPos = -1;
int8 oldPos = -1;
for (;;) {
checkSystem();
getKey();
if (_mousePos.y >= FIRSTLINE &&
_mousePos.y < (FIRSTLINE + ICONDY) &&
_mousePos.x >= ICONMARGSX &&
(_mousePos.x < (MAXX - ICONMARGDX))) {
oldPos = curPos;
curPos = (_mousePos.x - ICONMARGSX) / ICONDX;
if (oldPos != curPos) {
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
uint16 posX = ICONMARGSX + ((curPos) * (ICONDX)) + ICONDX / 2;
uint16 lenText = textLength(saveNames[curPos]);
if (posX - (lenText / 2) < 2)
posX = 2;
else
posX = posX - (lenText / 2);
if (posX + lenText > MAXX - 2)
posX = MAXX - 2 - lenText;
drawText.set(
Common::Rect(posX, FIRSTLINE + ICONDY + 10, lenText + posX, CARHEI + (FIRSTLINE + ICONDY + 10)),
Common::Rect(0, 0, lenText, CARHEI),
MOUSECOL,
saveNames[curPos].c_str());
drawText.draw(this);
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
}
if (_mouseLeftBtn && (_inventory[curPos] != EMPTYSLOT)) {
_mouseLeftBtn = false;
break;
}
} else {
if (oldPos != -1) {
_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
}
oldPos = -1;
curPos = -1;
if (_mouseLeftBtn || _mouseRightBtn) {
_mouseLeftBtn = _mouseRightBtn = false;
retval = false;
skipLoad = true;
break;
}
}
}
if (!skipLoad) {
loadGameState(curPos + 1);
} else {
_actor->actorStop();
_pathFind->nextStep();
checkSystem();
_graphicsMgr->clearScreenBufferInventory();
_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, TOP);
_graphicsMgr->clearScreenBufferTop();
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
if (_flagScriptActive) {
_graphicsMgr->hideCursor();
}
// Restore the inventory
_inventory = savedInventory;
_curInventory = 0;
_iconBase = savedIconBase;
}
return retval;
}
Common::Error TrecisionEngine::loadGameStream(Common::SeekableReadStream *stream) {
const byte version = stream->readByte();
Common::Serializer ser(stream, nullptr);
ser.setVersion(version);
syncGameStream(ser);
_graphicsMgr->clearScreenBufferInventory();
_flagNoPaintScreen = true;
_curStack = 0;
_flagScriptActive = false;
_oldRoom = _curRoom;
changeRoom(_curRoom);
_actor->actorStop();
_pathFind->nextStep();
checkSystem();
_graphicsMgr->clearScreenBufferInventory();
_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, TOP);
_graphicsMgr->clearScreenBufferTop();
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
if (_flagScriptActive) {
_graphicsMgr->hideCursor();
}
return Common::kNoError;
}
Common::Error TrecisionEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
const byte version = SAVE_VERSION_SCUMMVM;
Common::Serializer ser(nullptr, stream);
ser.setVersion(version);
stream->writeByte(version);
syncGameStream(ser);
return Common::kNoError;
}
bool TrecisionEngine::syncGameStream(Common::Serializer &ser) {
uint16 unused = 0;
if (ser.isLoading()) {
ser.skip(40, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // description
ser.skip(ICONDX * ICONDY * sizeof(uint16), SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // thumbnail
}
ser.syncAsUint16LE(_curRoom);
ser.syncAsByte(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _inventorySize
ser.syncAsByte(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _cyberInventorySize
ser.syncAsByte(_iconBase);
ser.syncAsSint16LE(_flagSkipTalk);
ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagSkipEnable
ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagMouseEnabled
ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagScreenRefreshed
ser.syncAsSint16LE(_flagPaintCharacter);
ser.syncAsSint16LE(_flagSomeoneSpeaks);
ser.syncAsSint16LE(_flagCharacterSpeak);
ser.syncAsSint16LE(_flagInventoryLocked);
ser.syncAsSint16LE(_flagUseWithStarted);
ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagMousePolling
ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagDialogSolitaire
ser.syncAsSint16LE(unused); // _flagCharacterExists
syncInventory(ser);
_actor->syncGameStream(ser);
_pathFind->syncGameStream(ser);
for (int i = 0; i < MAXROOMS; i++)
_room[i].syncGameStream(ser);
for (int i = 0; i < MAXOBJ; i++)
_obj[i].syncGameStream(ser);
for (int i = 0; i < MAXINVENTORY; i++)
_inventoryObj[i].syncGameStream(ser);
_animMgr->syncGameStream(ser);
ser.skip(NUMSAMPLES * 2, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // SoundManager::syncGameStream()
_dialogMgr->syncGameStream(ser);
_logicMgr->syncGameStream(ser);
return true;
}
} // End of namespace Trecision

View File

@@ -0,0 +1,164 @@
/* 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 "trecision/trecision.h"
#include "trecision/scheduler.h"
namespace Trecision {
Scheduler::Scheduler(TrecisionEngine *vm) : _vm(vm) {
_token = CLASS_CHAR;
_counter = 0;
Message msg = { MC_IDLE, 0, MP_DEFAULT, 0, 0, 0, 0 };
_idleMsg = _msg = msg;
}
Scheduler::~Scheduler() {
}
void Scheduler::process() {
bool retry = true;
while (retry) {
retry = false;
switch (_token) {
case CLASS_GAME:
if (_counter <= 30) {
++_counter;
_token = CLASS_CHAR;
if (!_gameQueue.empty()) {
_msg = _gameQueue.front();
_vm->_curMessage = &_msg;
_gameQueue.pop_front();
} else {
_vm->_curMessage = &_idleMsg;
}
} else {
_counter = 0;
_vm->_curMessage = &_idleMsg;
}
break;
case CLASS_CHAR:
_token = CLASS_GAME;
if (_vm->_flagPaintCharacter || _characterQueue.empty()) {
retry = true;
} else {
_msg = _characterQueue.front();
_vm->_curMessage = &_msg;
_characterQueue.pop_front();
}
break;
default:
break;
}
}
}
struct MessageComparator {
bool operator()(const Message &x, const Message &y) const {
return x._priority < y._priority;
}
};
void Scheduler::doEvent(uint8 cls, uint8 event, uint8 priority,
uint16 u16Param1, uint16 u16Param2,
uint8 u8Param, uint32 u32Param) {
Message m;
m._class = cls;
m._event = event;
m._priority = priority;
m._u16Param1 = u16Param1;
m._u16Param2 = u16Param2;
m._u8Param = u8Param;
m._u32Param = u32Param;
if (cls <= CLASS_GAME) {
_gameQueue.push_back(m);
Common::sort(_gameQueue.begin(), _gameQueue.end(), MessageComparator());
} else {
_characterQueue.push_back(m);
Common::sort(_characterQueue.begin(), _characterQueue.end(), MessageComparator());
}
}
void Scheduler::leftClick(uint16 x, uint16 y) {
doEvent(MC_MOUSE, ME_MLEFT, MP_DEFAULT, x, y, 0, 0);
}
void Scheduler::rightClick(uint16 x, uint16 y) {
doEvent(MC_MOUSE, ME_MRIGHT, MP_DEFAULT, x, y, 0, 0);
}
void Scheduler::mouseExamine(uint16 object) {
doEvent(MC_ACTION, ME_MOUSEEXAMINE, MP_DEFAULT, 0, 0, 0, object);
}
void Scheduler::mouseOperate(uint16 object) {
doEvent(MC_ACTION, ME_MOUSEOPERATE, MP_DEFAULT, 0, 0, 0, object);
}
void Scheduler::init() {
resetQueues();
_vm->_curMessage = &_idleMsg;
}
void Scheduler::resetQueues() {
_gameQueue.clear();
_characterQueue.clear();
}
void Scheduler::initCharacterQueue() {
_characterQueue.clear();
}
bool Scheduler::testEmptyQueues() {
bool onlyDialogEventsInGameQueue = true;
bool noActionInProgress = true;
for (Common::List<Message>::iterator it = _gameQueue.begin(); it != _gameQueue.end(); ++it) {
if (it->_class != MC_DIALOG) {
onlyDialogEventsInGameQueue = false;
break;
}
}
for (Common::List<Message>::iterator it = _characterQueue.begin(); it != _characterQueue.end(); ++it) {
if (it->_class == MC_CHARACTER) {
if (it->_event == ME_CHARACTERACTION ||
it->_event == ME_CHARACTERGOTO ||
it->_event == ME_CHARACTERGOTOACTION ||
it->_event == ME_CHARACTERGOTOEXAMINE ||
it->_event == ME_CHARACTERCONTINUEACTION) {
noActionInProgress = false;
break;
}
}
}
return noActionInProgress && onlyDialogEventsInGameQueue;
}
} // End of namespace Trecision

View 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 TRECISION_SCHEDULER_H
#define TRECISION_SCHEDULER_H
#include "common/scummsys.h"
#include "common/list.h"
#define MAXMESSAGE 128
namespace Trecision {
class TrecisionEngine;
struct Message {
uint8 _class; // message class
uint8 _event; // message name
uint8 _priority; // message priority
uint8 _u8Param;
uint16 _u16Param1; // byte parameter 1
uint16 _u16Param2; // byte parameter 2
uint32 _u32Param; // int parameter
void set(Message *src) {
_class = src->_class;
_event = src->_event;
_priority = src->_priority;
_u8Param = src->_u8Param;
_u16Param1 = src->_u16Param1;
_u16Param2 = src->_u16Param2;
_u32Param = src->_u32Param;
}
};
class Scheduler {
TrecisionEngine *_vm;
uint8 _token;
uint8 _counter;
// Message system
Message _idleMsg;
Message _msg;
Common::List<Message> _gameQueue;
Common::List<Message> _characterQueue;
public:
Scheduler(TrecisionEngine *vm);
~Scheduler();
void process();
void doEvent(uint8 cls, uint8 event, uint8 priority, uint16 u16Param1, uint16 u16Param2, uint8 u8Param, uint32 u32Param);
void leftClick(uint16 x, uint16 y);
void rightClick(uint16 x, uint16 y);
void mouseExamine(uint16 object);
void mouseOperate(uint16 object);
void init();
void resetQueues();
void initCharacterQueue();
bool testEmptyQueues();
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,665 @@
/* 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/scummsys.h"
#include "graphics/scaler.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/logic.h"
#include "trecision/pathfinding3d.h"
#include "trecision/renderer3d.h"
#include "trecision/scheduler.h"
#include "trecision/trecision.h"
#include "trecision/text.h"
#include "trecision/video.h"
namespace Trecision {
void SScriptFrame::clear() {
_class = MC_IDLE;
_event = ME_MOUSEOPERATE;
_u8Param = 0;
_u16Param1 = _u16Param2 = 0;
_u32Param = 0;
_noWait = false;
}
void SScriptFrame::sendFrame(Scheduler *scheduler) {
scheduler->doEvent(_class, _event, MP_DEFAULT, _u16Param1, _u16Param2, _u8Param, _u32Param);
}
void TrecisionEngine::endScript() {
--_curStack;
if (_curStack == 0) {
_flagScriptActive = false;
_graphicsMgr->showCursor();
_textMgr->redrawString();
}
}
void TrecisionEngine::playScript(uint16 id) {
++_curStack;
_flagScriptActive = true;
_graphicsMgr->hideCursor();
_curScriptFrame[_curStack] = _scriptFirstFrame[id];
processScriptFrame();
}
void TrecisionEngine::evalScript() {
if (_scheduler->testEmptyQueues()) {
++_curScriptFrame[_curStack];
_graphicsMgr->hideCursor();
processScriptFrame();
}
}
void TrecisionEngine::processScriptFrame() {
SScriptFrame *curFrame = &_scriptFrame[_curScriptFrame[_curStack]];
// If the event is empty, terminate the script
if (curFrame->isEmptyEvent()) {
endScript();
return;
}
bool loop = true;
while (loop) {
loop = false;
curFrame = &_scriptFrame[_curScriptFrame[_curStack]];
SScriptFrame *nextFrame = &_scriptFrame[_curScriptFrame[_curStack] + 1];
curFrame->sendFrame(_scheduler);
if (curFrame->_noWait && !nextFrame->isEmptyEvent()) {
++_curScriptFrame[_curStack];
loop = true;
}
}
}
bool TrecisionEngine::quitPrompt() {
_graphicsMgr->clearScreenBufferTop();
SDText drawText;
drawText.set(
Common::Rect(0, TOP - 20, MAXX, CARHEI + (TOP - 20)),
Common::Rect(0, 0, MAXX, CARHEI),
MOUSECOL,
_sysText[kMessageConfirmExit]
);
drawText.draw(this);
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
freeKey();
checkSystem();
_graphicsMgr->clearScreenBufferTop();
waitKey();
Common::CustomEventType customType = _curAction;
_curAction = kActionNone;
return (customType == kActionYes); // German confirmation is J, English and French use 'Y'
}
void TrecisionEngine::demoOver() {
_graphicsMgr->clearScreenBufferTop();
SDText drawText;
drawText.set(
Common::Rect(0, TOP - 20, MAXX, CARHEI + (TOP - 20)),
Common::Rect(0, 0, MAXX, CARHEI),
MOUSECOL,
_sysText[kMessageDemoOver]
);
drawText.draw(this);
_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
freeKey();
waitKey();
quitGame();
}
void TrecisionEngine::doAction() {
if (_curMessage->_event == ME_MOUSEOPERATE || _curMessage->_event == ME_MOUSEEXAMINE) {
// Action in the game area
_curObj = _curMessage->_u32Param;
if (_curObj == oLASTLEV5)
_textMgr->characterSay(kSentencePutHimOutOfAction);
if (!_curObj || !isObjectVisible(_curObj))
return;
if (_obj[_curObj].isModeHidden())
_obj[_curObj].setModeHidden(false);
if (_flagUseWithStarted) {
if ((_obj[_curObj].isFlagRoomOut() || _obj[_curObj].isFlagRoomIn()) && !_obj[_curObj].isFlagExamine())
return;
_flagUseWithStarted = false;
_flagInventoryLocked = false;
_useWith[WITH] = _curObj;
_useWithInv[WITH] = false;
_lightIcon = 0xFF;
if (!_useWithInv[USED] && _curObj == _useWith[USED]) {
_useWith[USED] = 0;
_useWith[WITH] = 0;
_useWithInv[USED] = false;
_useWithInv[WITH] = false;
_flagUseWithStarted = false;
_textMgr->clearLastText();
} else
doUseWith();
_curObj = 0;
return;
}
if (_curMessage->_event == ME_MOUSEOPERATE && _obj[_curObj].isFlagUseWith()) {
_flagUseWithStarted = true;
_flagInventoryLocked = true;
_useWith[USED] = _curObj;
_useWith[WITH] = 0;
_useWithInv[USED] = false;
_useWithInv[WITH] = false;
_textMgr->showObjName(_curObj, true);
return;
}
}
switch (_curMessage->_event) {
case ME_MOUSEOPERATE:
if (_obj[_curObj].isFlagRoomIn())
doRoomIn(_curObj);
else if (_obj[_curObj].isFlagPerson())
doMouseTalk(_curObj);
else if (_obj[_curObj].isFlagRoomOut())
doRoomOut(_curObj);
else if (_obj[_curObj].isFlagTake())
doMouseTake(_curObj);
else
doMouseOperate(_curObj);
break;
case ME_MOUSEEXAMINE:
if (_obj[_curObj].isFlagExamine())
doMouseExamine(_curObj);
else if (_obj[_curObj].isFlagRoomIn())
doRoomIn(_curObj);
else if (_obj[_curObj].isFlagPerson())
doMouseExamine(_curObj);
else if (_obj[_curObj].isFlagRoomOut())
doRoomOut(_curObj);
else
doMouseExamine(_curObj);
break;
default:
break;
}
}
void TrecisionEngine::processMouseMovement() {
if (isGameArea(_mousePos)) {
// Game area
if (_flagSomeoneSpeaks || _flagDialogMenuActive || _flagDialogActive)
return;
checkMask(_mousePos);
_logicMgr->doMouseGame();
} else if (isInventoryArea(_mousePos)) {
if (_logicMgr->doMouseInventory())
return;
if ((_flagSomeoneSpeaks && !_flagCharacterSpeak) || _flagDialogMenuActive || _flagDialogActive)
return;
if (_animMgr->isActionActive())
return;
if (_inventoryStatus == INV_OFF)
openInventory();
else if (_inventoryStatus == INV_INACTION)
showIconName();
} else {
// Up area
if (_curRoom == kRoomControlPanel)
return;
_curObj = 0;
_textMgr->showObjName(_curObj, true);
if (_flagDialogMenuActive)
_dialogMgr->updateChoices(_mousePos.x, _mousePos.y);
}
}
void TrecisionEngine::doMouse() {
switch (_curMessage->_event) {
case ME_MRIGHT:
case ME_MLEFT:
if (_flagSomeoneSpeaks) {
_flagSkipTalk = true;
break;
}
if (_actor->_curAction > hWALKIN)
break;
if (_flagDialogActive && _flagDialogMenuActive) {
_dialogMgr->selectChoice(_mousePos.x, _mousePos.y);
break;
}
_logicMgr->doMouseLeftRight();
break;
default:
break;
}
}
void TrecisionEngine::doCharacter() {
switch (_curMessage->_event) {
case ME_CHARACTERDOACTION:
case ME_CHARACTERGOTOACTION:
case ME_CHARACTERGOTOEXAMINE:
case ME_CHARACTERGOTOEXIT:
case ME_CHARACTERGOTO:
if (_pathFind->nextStep()) {
_pathFind->_characterInMovement = false;
_pathFind->_characterGoToPosition = -1;
_flagWaitRegen = true;
} else
_pathFind->_characterInMovement = true;
if (_fastWalk) {
if (_pathFind->nextStep()) {
_pathFind->_characterInMovement = false;
_pathFind->_characterGoToPosition = -1;
_flagWaitRegen = true;
} else
_pathFind->_characterInMovement = true;
}
_flagPaintCharacter = true;
if (_pathFind->_characterInMovement)
reEvent();
else {
_graphicsMgr->showCursor();
if (_curMessage->_event == ME_CHARACTERGOTOACTION)
_scheduler->mouseOperate((uint16)_curMessage->_u32Param);
else if (_curMessage->_event == ME_CHARACTERGOTOEXAMINE)
_scheduler->mouseExamine((uint16)_curMessage->_u32Param);
else if (_curMessage->_event == ME_CHARACTERGOTOEXIT) {
_flagShowCharacter = false;
changeRoom(_curMessage->_u16Param1, _curMessage->_u16Param2, _curMessage->_u8Param);
} else if (_curMessage->_event == ME_CHARACTERDOACTION) {
_lastObj = 0;
_textMgr->showObjName(_curObj, true);
refreshInventory(_inventoryRefreshStartIcon, _inventoryRefreshStartLine);
}
}
break;
case ME_CHARACTERACTION:
if (_flagWaitRegen)
reEvent();
_scheduler->initCharacterQueue();
_inventoryRefreshStartLine = INVENTORY_HIDE;
refreshInventory(_inventoryRefreshStartIcon, INVENTORY_HIDE);
_inventoryStatus = INV_OFF;
if (_curMessage->_u16Param1 > hLAST) {
_animMgr->startSmkAnim(_curMessage->_u16Param1);
_animTypeMgr->init(_curMessage->_u16Param1, _curMessage->_u32Param);
_graphicsMgr->hideCursor();
_scheduler->doEvent(MC_CHARACTER, ME_CHARACTERCONTINUEACTION, _curMessage->_priority, _curMessage->_u16Param1, _curMessage->_u16Param2, _curMessage->_u8Param, _curMessage->_u32Param);
} else
_actor->actorDoAction(_curMessage->_u16Param1);
_textMgr->clearLastText();
break;
case ME_CHARACTERCONTINUEACTION:
_flagShowCharacter = false;
_animTypeMgr->handler(kAnimTypeCharacter);
// If the animation is over
if (!_animMgr->isActionActive()) {
_graphicsMgr->showCursor();
_flagShowCharacter = true;
_pathFind->_characterInMovement = false;
_scheduler->initCharacterQueue();
_animTypeMgr->end(kAnimTypeCharacter);
_flagWaitRegen = true;
_lastObj = 0;
_textMgr->showObjName(_curObj, true);
// If the room changes at the end
if (_curMessage->_u16Param2) {
_flagShowCharacter = false;
changeRoom(_curMessage->_u16Param2, 0, _curMessage->_u8Param);
} else if (_curMessage->_u8Param)
_pathFind->setPosition(_curMessage->_u8Param);
if ((_curMessage->_u16Param1 == _obj[oCANCELLATA1B]._anim) && !isObjectVisible(oBOTTIGLIA1D) && !isObjectVisible(oRETE17)) {
_dialogMgr->playDialog(dF181);
_pathFind->setPosition(1);
}
} else
reEvent();
break;
default:
break;
}
}
void TrecisionEngine::changeRoom(uint16 room, uint16 action, byte position) {
if (_curRoom == 0)
return;
// if regen still has to occur
if (_flagWaitRegen)
reEvent();
_logicMgr->doSystemChangeRoom(room);
_pathFind->setPosition(position);
_actor->actorStop();
if (action)
startCharacterAction(action, 0, 0, 0);
_logicMgr->endChangeRoom();
// WORKAROUND: Set position *again*. Fixes entering some rooms
// (e.g. kRoom23A, kRoom2D, kRoom28).
// The first two checks have been duplicated from endChangeRoom()
if (_curRoom == kRoom31 && !_room[kRoom31].isDone())
_pathFind->setPosition(14);
else if (_oldRoom == kRoom41D && _inventoryObj[kItemPositioner].isFlagExtra())
_pathFind->setPosition(30);
else
_pathFind->setPosition(position);
_room[_curRoom].setDone(true); // Visited
_renderer->drawCharacter(CALCPOINTS); // for right _actorPos entrance
}
void TrecisionEngine::doIdle() {
uint16 a = getAction();
switch (a) {
case kActionQuit:
if (!_flagDialogActive && !_flagDialogMenuActive) {
if (quitPrompt())
quitGame();
}
break;
case kActionSkipVideo:
if (canPlayerInteract()) {
::createThumbnailFromScreen(&_thumbnail);
_actor->actorStop();
_pathFind->nextStep();
_graphicsMgr->showCursor();
_obj[o00EXIT]._goRoom = _curRoom;
changeRoom(kRoomControlPanel);
_flagShowCharacter = false;
}
break;
case kActionSystemMenu:
if (canPlayerInteract()) {
::createThumbnailFromScreen(&_thumbnail);
_actor->actorStop();
_pathFind->nextStep();
_graphicsMgr->showCursor();
_obj[o00EXIT]._goRoom = _curRoom;
changeRoom(kRoomControlPanel);
_flagShowCharacter = false;
}
break;
case kActionSave:
if (canPlayerInteract()) {
::createThumbnailFromScreen(&_thumbnail);
dataSave();
showInventoryName(NO_OBJECTS, false);
showIconName();
refreshInventory(_inventoryRefreshStartIcon, _inventoryRefreshStartLine);
}
break;
case kActionLoad:
if (canPlayerInteract()) {
::createThumbnailFromScreen(&_thumbnail);
if (!dataLoad()) {
showInventoryName(NO_OBJECTS, false);
showIconName();
refreshInventory(_inventoryRefreshStartIcon, _inventoryRefreshStartLine);
}
}
break;
default:
break;
}
if (isGameArea(_mousePos) && ((_inventoryStatus == INV_ON) || (_inventoryStatus == INV_INACTION)))
closeInventory();
if (_inventoryScrollTime > _curTime)
_inventoryScrollTime = _curTime;
if (isInventoryArea(_mousePos) && (_curTime > (INVSCROLLSP + _inventoryScrollTime))) {
doScrollInventory(_mousePos);
_inventoryScrollTime = _curTime;
}
}
void TrecisionEngine::doRoomIn(uint16 curObj) {
_graphicsMgr->hideCursor();
uint16 curAction = _obj[curObj]._anim;
uint16 curPos = _obj[curObj]._ninv;
changeRoom(_obj[curObj]._goRoom, curAction, curPos);
_obj[curObj].setFlagDone(true);
}
void TrecisionEngine::doRoomOut(uint16 curObj) {
_graphicsMgr->hideCursor();
uint16 curAction, curPos;
_logicMgr->roomOut(curObj, &curAction, &curPos);
if (curAction)
_scheduler->doEvent(MC_CHARACTER, ME_CHARACTERACTION, MP_DEFAULT, curAction, _obj[curObj]._goRoom, curPos, curObj);
_obj[curObj].setFlagDone(true);
}
void TrecisionEngine::doMouseExamine(uint16 curObj) {
if (!curObj)
warning("doMouseExamine - curObj not set");
bool printSentence = _logicMgr->mouseExamine(curObj);
if (printSentence && _obj[curObj]._examine)
_textMgr->characterSay(_obj[curObj]._examine);
}
void TrecisionEngine::doMouseOperate(uint16 curObj) {
if (!curObj)
warning("doMouseOperate - curObj not set");
bool printSentence = _logicMgr->mouseOperate(curObj);
if (printSentence && _obj[curObj]._action)
_textMgr->characterSay(_obj[curObj]._action);
}
void TrecisionEngine::doMouseTake(uint16 curObj) {
if (!curObj)
warning("doMouseTake - curObj not set");
bool del = _logicMgr->mouseTake(curObj);
uint16 curAction = _obj[curObj]._anim;
if (curAction)
_scheduler->doEvent(MC_CHARACTER, ME_CHARACTERACTION, MP_DEFAULT, curAction, 0, 0, curObj);
// Remove object being taken
if (del) {
if (curAction) {
for (uint16 j = 0; j < MAXATFRAME; ++j) {
SAtFrame *frame = &_animMgr->_animTab[curAction]._atFrame[j];
if (frame->_type == ATFCLR && frame->_index == curObj)
break;
if (frame->_type == ATFNONE) {
frame->_area = 0;
frame->_numFrame = 1;
frame->_type = ATFCLR;
frame->_index = curObj;
break;
}
}
} else {
setObjectVisible(curObj, false);
}
}
addIcon(_obj[_curObj]._ninv);
}
void TrecisionEngine::doMouseTalk(uint16 curObj) {
if (!curObj)
warning("doMouseTalk - curObj not set");
bool printSentence = _logicMgr->mouseTalk(curObj);
if (printSentence)
_dialogMgr->playDialog(_obj[curObj]._goRoom);
}
void TrecisionEngine::doUseWith() {
_textMgr->showObjName(0, false);
if (_useWithInv[USED]) {
if (_useWithInv[WITH])
_logicMgr->useInventoryWithInventory();
else
_logicMgr->useInventoryWithScreen();
} else
doScreenUseWithScreen();
_useWith[USED] = 0;
_useWith[WITH] = 0;
_useWithInv[USED] = false;
_useWithInv[WITH] = false;
_flagUseWithStarted = false;
}
void TrecisionEngine::doScreenUseWithScreen() {
if (!_useWith[USED] || !_useWith[WITH])
warning("doScreenUseWithScreen - _useWith not set properly");
if (_pathFind->_characterInMovement)
return;
bool printSentence = _logicMgr->useScreenWithScreen();
if (printSentence)
_textMgr->characterSay(_obj[_useWith[USED]]._action);
}
void TrecisionEngine::doInvExamine() {
if (!_curInventory)
warning("doInvExamine - _curInventory not set properly");
if (_inventoryObj[_curInventory]._examine)
_textMgr->characterSay(_inventoryObj[_curInventory]._examine);
}
void TrecisionEngine::doInvOperate() {
if (!_curInventory)
warning("doInvOperate - _curInventory not set properly");
const bool printSentence = _logicMgr->operateInventory();
if (_inventoryObj[_curInventory]._action && printSentence)
_textMgr->characterSay(_inventoryObj[_curInventory]._action);
}
void TrecisionEngine::doScript() {
Message *message = _curMessage;
const uint16 index = message->_u16Param1;
const uint16 index2 = message->_u16Param2;
const uint32 value = message->_u32Param;
switch (message->_event) {
case ME_CHANGER:
changeRoom(index, index2, value);
break;
default:
break;
}
}
void TrecisionEngine::processCurrentMessage() {
switch (_curMessage->_class) {
case MC_CHARACTER:
doCharacter();
break;
case MC_IDLE:
doIdle();
break;
case MC_MOUSE:
doMouse();
break;
case MC_ACTION:
doAction();
break;
case MC_STRING:
_textMgr->doString();
break;
case MC_DIALOG:
_dialogMgr->doDialog();
break;
case MC_SCRIPT:
doScript();
break;
default:
break;
}
}
} // End of namespace Trecision

289
engines/trecision/sound.cpp Normal file
View File

@@ -0,0 +1,289 @@
/* 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 "audio/audiostream.h"
#include "audio/mixer.h"
#include "audio/decoders/wave.h"
#include "audio/decoders/raw.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
#include "trecision/defines.h"
namespace Trecision {
SoundManager::SoundManager(TrecisionEngine *vm) : _vm(vm) {
if (!_speechFile.open(_vm, "nlspeech.cd0"))
warning("SoundManager - nlspeech.cd0 is missing - skipping");
_stepLeftStream = nullptr;
_stepRightStream = nullptr;
}
SoundManager::~SoundManager() {
g_system->getMixer()->stopAll();
_speechFile.close();
stopAll();
}
Audio::SeekableAudioStream *SoundManager::loadWAV(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
if (_vm->isAmiga()) {
return Audio::makeRawStream(stream, 11025, 0, disposeAfterUse);
} else {
return Audio::makeWAVStream(stream, disposeAfterUse);
}
}
void SoundManager::play(int soundId) {
SRoom *curRoom = &_vm->_room[_vm->_curRoom];
for (uint16 soundSlot = 0; soundSlot < MAXSOUNDSINROOM; soundSlot++) {
if (curRoom->_sounds[soundSlot] == 0)
return;
if (curRoom->_sounds[soundSlot] == soundId) {
const SoundType soundType = (_gSample[soundId]._flag & kSoundFlagBgMusic) ? kSoundTypeMusic : kSoundTypeSfx;
Common::SeekableReadStream *soundFileStream = _vm->_dataFile.createReadStreamForMember(Common::Path(_gSample[soundId]._name));
if (!soundFileStream)
continue;
// We need to copy this WAV to memory since it will be streamed
Common::SeekableReadStream *memStream = soundFileStream->readStream(soundFileStream->size());
delete soundFileStream;
stopSoundType(soundType);
Audio::Mixer::SoundType type =
(_gSample[soundId]._flag & kSoundFlagBgMusic) ?
Audio::Mixer::kMusicSoundType :
Audio::Mixer::kSFXSoundType;
int volume = VOLUME(_gSample[soundId]._volume);
// FIXME: This looks wrong - it makes the room music silent
/*if (_gSample[soundId]._flag & kSoundFlagSoundOn) {
volume = 0;
}*/
Audio::AudioStream *stream = nullptr;
if (_gSample[soundId]._flag & kSoundFlagSoundLoop)
stream = Audio::makeLoopingAudioStream(loadWAV(memStream), 0);
else
stream = loadWAV(memStream);
g_system->getMixer()->playStream(
type,
&_soundHandles[soundType],
stream,
-1,
volume,
0,
DisposeAfterUse::YES
);
}
}
}
void SoundManager::stopAll() {
for (int i = 0; i < MAXSOUNDS; i++) {
g_system->getMixer()->stopHandle(_soundHandles[i]);
}
delete _stepLeftStream;
_stepLeftStream = nullptr;
delete _stepRightStream;
_stepRightStream = nullptr;
}
void SoundManager::stopAllExceptMusic() {
for (int i = 0; i < MAXSOUNDS; i++) {
if (i != kSoundTypeMusic) {
g_system->getMixer()->stopHandle(_soundHandles[i]);
}
}
delete _stepLeftStream;
_stepLeftStream = nullptr;
delete _stepRightStream;
_stepRightStream = nullptr;
}
void SoundManager::stopSoundType(SoundType type) {
if (g_system->getMixer()->isSoundHandleActive(_soundHandles[type])) {
g_system->getMixer()->stopHandle(_soundHandles[type]);
}
}
void SoundManager::soundStep(int midx, int midz, int act, int frame) {
SRoom *curRoom = &_vm->_room[_vm->_curRoom];
bool stepRight = false;
bool stepLeft = false;
switch (act) {
case hWALK:
if (frame == 3)
stepLeft = true;
else if (frame == 8)
stepRight = true;
break;
case hWALKIN:
if (frame == 3)
stepLeft = true;
else if (frame == 9)
stepRight = true;
break;
case hWALKOUT:
if (frame == 5)
stepLeft = true;
else if (frame == 10)
stepRight = true;
break;
case hSTOP0:
case hSTOP1:
case hSTOP2:
case hSTOP3:
case hSTOP9:
if (frame >= defActionLen[act] - 1)
stepLeft = true;
break;
case hSTOP4:
case hSTOP5:
case hSTOP6:
case hSTOP7:
case hSTOP8:
if (frame >= defActionLen[act] - 1)
stepRight = true;
break;
default:
break;
}
if (!stepRight && !stepLeft)
return;
int soundId;
for (int soundSlot = 0; soundSlot < MAXSOUNDSINROOM; soundSlot++) {
soundId = curRoom->_sounds[soundSlot];
if (stepRight && (_gSample[soundId]._flag & kSoundFlagStepRight)) {
if (!_stepRightStream) {
Common::SeekableReadStream *soundFileStream = _vm->_dataFile.createReadStreamForMember(Common::Path(_gSample[soundId]._name));
_stepRightStream = loadWAV(soundFileStream);
}
break;
}
if (stepLeft && (_gSample[soundId]._flag & kSoundFlagStepLeft)) {
if (!_stepLeftStream) {
Common::SeekableReadStream *soundFileStream = _vm->_dataFile.createReadStreamForMember(Common::Path(_gSample[soundId]._name));
_stepLeftStream = loadWAV(soundFileStream);
}
break;
}
if (soundId == 0)
return;
}
midz = ((int)(_gSample[soundId]._volume) * 1000) / ABS(midz);
if (midz > 255)
midz = 255;
g_system->getMixer()->stopHandle(_soundHandles[kSoundTypeStep]);
Audio::SeekableAudioStream *stream = stepLeft ? _stepLeftStream : _stepRightStream;
stream->rewind();
const int panpos = ((midx - 320) * 127 / 320) / 2;
g_system->getMixer()->playStream(
Audio::Mixer::kSFXSoundType,
&_soundHandles[kSoundTypeStep],
stream,
-1,
VOLUME(midz),
panpos,
DisposeAfterUse::NO
);
}
int32 SoundManager::talkStart(const Common::Path &name) {
if (!_speechFile.isOpen())
return 0;
stopSoundType(kSoundTypeSpeech);
Common::SeekableReadStream *stream = _speechFile.createReadStreamForMember(name);
if (!stream)
return 0;
Audio::SeekableAudioStream *audioStream = loadWAV(stream);
g_system->getMixer()->playStream(
Audio::Mixer::kSpeechSoundType,
&_soundHandles[kSoundTypeSpeech],
audioStream,
-1,
Audio::Mixer::kMaxChannelVolume, // TODO
0,
DisposeAfterUse::YES
);
_vm->_characterSpeakTime = _vm->readTime();
return TIME(audioStream->getLength().msecs());
}
void SoundManager::loadRoomSounds() {
SRoom *curRoom = &_vm->_room[_vm->_curRoom];
stopAll();
for (uint16 soundSlot = 0; soundSlot < MAXSOUNDSINROOM; soundSlot++) {
const uint16 soundId = curRoom->_sounds[soundSlot];
if (soundId == 0)
break;
if (_gSample[soundId]._name.equalsIgnoreCase("RUOTE2C.WAV"))
break;
if ((_gSample[soundId]._flag & kSoundFlagBgMusic) || (_gSample[soundId]._flag & kSoundFlagSoundOn))
play(soundId);
}
}
void SoundManager::loadSamples(Common::SeekableReadStreamEndian *stream) {
for (int i = 0; i < NUMSAMPLES; ++i) {
_gSample[i]._name = stream->readString(0, 14);
_gSample[i]._volume = stream->readByte();
_gSample[i]._flag = stream->readByte();
_gSample[i]._panning = stream->readSByte();
}
}
} // End of namespace Trecision

89
engines/trecision/sound.h Normal file
View File

@@ -0,0 +1,89 @@
/* 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 TRECISION_SOUND_H
#define TRECISION_SOUND_H
#include "trecision/fastfile.h"
#include "common/stream.h"
#include "audio/mixer.h"
#include "audio/audiostream.h"
namespace Trecision {
#define SOUND_OFF 0
#define SOUND_ON 1
#define MAXSOUNDS 4
#define SAMPLEVOICES 6
#define NUMSAMPLES 145 // Maximum number of samples in the game
#define VOLUME(a) ( (a * 255) / 127 )
#define TIME(a) ( (a * 3) / 50 )
struct SSound {
Common::String _name;
uint8 _volume;
uint8 _flag;
int8 _panning;
};
enum SoundType {
kSoundTypeMusic = 0,
kSoundTypeSpeech = 1,
kSoundTypeSfx = 2,
kSoundTypeStep = 3
};
class TrecisionEngine;
class SoundManager {
public:
SoundManager(TrecisionEngine *vm);
~SoundManager();
private:
TrecisionEngine *_vm;
FastFile _speechFile; // nlspeech.cd0
Audio::SoundHandle _soundHandles[MAXSOUNDS];
SSound _gSample[NUMSAMPLES];
Audio::SeekableAudioStream *_stepLeftStream;
Audio::SeekableAudioStream *_stepRightStream;
public:
Audio::SeekableAudioStream *loadWAV(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
void play(int soundId);
void stopSoundType(SoundType type);
void stopAll();
void stopAllExceptMusic();
void soundStep(int midx, int midz, int act, int frame);
int32 talkStart(const Common::Path &name);
void loadRoomSounds();
void loadSamples(Common::SeekableReadStreamEndian *stream);
};
} // End of namespace Trecision
#endif

View File

@@ -0,0 +1,164 @@
/* 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 "trecision/struct.h"
namespace Trecision {
void SRoom::loadRoom(Common::SeekableReadStreamEndian *stream) {
stream->read(&_baseName, 4);
_flag = stream->readByte();
stream->readByte(); // Padding
_bkgAnim = stream->readUint16();
for (int j = 0; j < MAXOBJINROOM; ++j)
_object[j] = stream->readUint16();
for (int j = 0; j < MAXSOUNDSINROOM; ++j)
_sounds[j] = stream->readUint16();
for (int j = 0; j < MAXACTIONINROOM; ++j)
_actions[j] = stream->readUint16();
}
void SRoom::syncGameStream(Common::Serializer &ser) {
ser.syncBytes((byte *)_baseName, 4);
for (int i = 0; i < MAXACTIONINROOM; i++)
ser.syncAsUint16LE(_actions[i]);
ser.syncAsByte(_flag);
ser.syncAsUint16LE(_bkgAnim);
}
/********************************************************************/
void SObject::readRect(Common::SeekableReadStream *stream) {
_rect.left = stream->readUint16LE();
_rect.top = stream->readUint16LE();
_rect.setWidth(stream->readUint16LE());
_rect.setHeight(stream->readUint16LE());
}
void SObject::syncGameStream(Common::Serializer &ser) {
ser.syncAsUint16LE(_area.left);
ser.syncAsUint16LE(_area.top);
ser.syncAsUint16LE(_area.right);
ser.syncAsUint16LE(_area.bottom);
ser.syncAsUint16LE(_name);
ser.syncAsUint16LE(_examine);
ser.syncAsUint16LE(_action);
ser.syncAsUint16LE(_anim);
ser.syncAsByte(_mode);
ser.syncAsByte(_flag);
ser.syncAsByte(_goRoom);
ser.syncAsByte(_nbox);
ser.syncAsByte(_ninv);
ser.syncAsSByte(_position);
}
void SObject::loadObj(Common::SeekableReadStreamEndian *stream) {
uint16 w = stream->readUint16();
uint16 h = stream->readUint16();
_rect.left = stream->readUint16();
_rect.top = stream->readUint16();
_rect.setWidth(w);
_rect.setHeight(h);
_area.left = stream->readUint16();
_area.top = stream->readUint16();
_area.right = stream->readUint16();
_area.bottom = stream->readUint16();
_position = stream->readSByte();
stream->readByte(); // Padding
_name = stream->readUint16();
_examine = stream->readUint16();
_action = stream->readUint16();
_goRoom = stream->readByte();
_nbox = stream->readByte();
_ninv = stream->readByte();
_mode = stream->readByte();
_flag = stream->readByte();
stream->readByte(); // Padding
_anim = stream->readUint16();
}
/********************************************************************/
void SInvObject::syncGameStream(Common::Serializer &ser) {
ser.syncAsUint16LE(_name);
ser.syncAsUint16LE(_examine);
ser.syncAsUint16LE(_action);
ser.syncAsUint16LE(_anim);
ser.syncAsByte(_flag);
}
void SInvObject::loadObj(Common::SeekableReadStreamEndian *stream) {
_name = stream->readUint16();
_examine = stream->readUint16();
_action = stream->readUint16();
_flag = stream->readByte();
stream->readByte(); // Padding
_anim = stream->readUint16();
}
/********************************************************************/
void STexture::clear() {
_dx = _dy = _angle = 0;
_texture = nullptr;
_active = false;
}
void STexture::set(int16 x, int16 y, uint8 *buffer) {
_dx = x;
_dy = y;
_angle = 0;
_active = true;
_texture = buffer;
}
/********************************************************************/
void SVertex::clear() {
_x = _y = _z = 0.0f;
_nx = _ny = _nz = 0.0f;
}
/********************************************************************/
void SLight::clear() {
_x = _y = _z = 0.0f;
_dx = _dy = _dz = 0.0f;
_inr = _outr = 0.0f;
_hotspot = 0;
_fallOff = 0;
_inten = 0;
_position = 0;
}
/********************************************************************/
void SCamera::clear() {
_ex = _ey = _ez = 0.0f;
_fovX = _fovY = 0.0f;
for (uint8 i = 0; i < 3; ++i)
_e1[i] = _e2[i] = _e3[i] = 0.0f;
}
} // namespace Trecision

250
engines/trecision/struct.h Normal file
View File

@@ -0,0 +1,250 @@
/* 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 TRECISION_STRUCT_H
#define TRECISION_STRUCT_H
#include "common/stream.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/serializer.h"
#include "graphics/surface.h"
#include "trecision/defines.h"
namespace Trecision {
struct SRoom {
char _baseName[4]; // Room name
uint16 _bkgAnim; // Background animation
uint16 _object[MAXOBJINROOM]; // Objects in the room
uint16 _sounds[MAXSOUNDSINROOM]; // Sounds of the room
uint16 _actions[MAXACTIONINROOM]; // Character actions in the room
bool hasExtra() { return _flag & kObjFlagExtra; }
bool isDone() { return _flag & kObjFlagDone; }
void setExtra(bool on) { if (on) _flag |= kObjFlagExtra; else _flag &= ~kObjFlagExtra; }
void setDone(bool on) { if (on) _flag |= kObjFlagDone; else _flag &= ~kObjFlagDone; }
void syncGameStream(Common::Serializer &ser);
void loadRoom(Common::SeekableReadStreamEndian *stream);
private:
uint8 _flag; // Room visited or not, extra or not
};
struct SObject {
Common::Rect _rect;
Common::Rect _area;
int8 _position; // -1 if no position
uint16 _name;
uint16 _examine;
uint16 _action;
uint8 _goRoom; // If direction room num - if person num dialog
uint8 _nbox; // Which 3d box the object is associated with
uint8 _ninv; // ptr inventory
uint16 _anim;
void readRect(Common::SeekableReadStream *stream);
void setFlagDone(bool on) { if (on) _flag |= kObjFlagDone; else _flag &= ~kObjFlagDone; }
void setFlagExamine(bool on) { if (on) _flag |= kObjFlagExamine; else _flag &= ~kObjFlagExamine; }
void setFlagExtra(bool on) { if (on) _flag |= kObjFlagExtra; else _flag &= ~kObjFlagExtra; }
void setFlagPerson(bool on) { if (on) _flag |= kObjFlagPerson; else _flag &= ~kObjFlagPerson; }
void setFlagRoomOut(bool on) { if (on) _flag |= kObjFlagRoomOut; else _flag &= ~kObjFlagRoomOut; }
void setFlagRoomIn(bool on) { if (on) _flag |= kObjFlagRoomIn; else _flag &= ~kObjFlagRoomIn; }
void setFlagTake(bool on) { if (on) _flag |= kObjFlagTake; else _flag &= ~kObjFlagTake; }
bool isFlagDone() { return _flag & kObjFlagDone; }
bool isFlagExamine() { return _flag & kObjFlagExamine; }
bool isFlagExtra() { return _flag & kObjFlagExtra; }
bool isFlagPerson() { return _flag & kObjFlagPerson; }
bool isFlagRoomIn() { return _flag & kObjFlagRoomIn; }
bool isFlagRoomOut() { return _flag & kObjFlagRoomOut; }
bool isFlagTake() { return _flag & kObjFlagTake; }
bool isFlagUseWith() { return _flag & kObjFlagUseWith; }
bool isModeHidden() { return _mode & OBJMODE_HIDDEN; }
bool isModeFull() { return _mode & OBJMODE_FULL; }
bool isModeMask() { return _mode & OBJMODE_MASK; }
bool isModeLim() { return _mode & OBJMODE_LIM; }
bool isModeStatus() { return _mode & OBJMODE_OBJSTATUS; }
void setModeHidden(bool on) { if (on) _mode |= OBJMODE_HIDDEN; else _mode &= ~OBJMODE_HIDDEN; }
void setModeFull(bool on) { if (on) _mode |= OBJMODE_FULL; else _mode &= ~OBJMODE_FULL; }
void setModeMask(bool on) { if (on) _mode |= OBJMODE_MASK; else _mode &= ~OBJMODE_MASK; }
void setModeLim(bool on) { if (on) _mode |= OBJMODE_LIM; else _mode &= ~OBJMODE_LIM; }
void setModeStatus(bool on) { if (on) _mode |= OBJMODE_OBJSTATUS; else _mode &= ~OBJMODE_OBJSTATUS; }
void syncGameStream(Common::Serializer &ser);
void loadObj(Common::SeekableReadStreamEndian *stream);
private:
uint8 _flag;
uint8 _mode;
};
struct SInvObject {
uint16 _name; // Object name in the inventory
uint16 _examine; // Sentence if examined
uint16 _action;
uint16 _anim;
void setFlagExtra(bool on) { if (on) _flag |= kObjFlagExtra; else _flag &= ~kObjFlagExtra; }
bool isFlagExtra() { return _flag & kObjFlagExtra; }
bool isUseWith() { return _flag & kObjFlagUseWith; }
void syncGameStream(Common::Serializer &ser);
void loadObj(Common::SeekableReadStreamEndian *stream);
private:
uint8 _flag;
};
struct SAtFrame {
uint8 _type; //ATFTEXT, ATFSND, ATFEVENT
uint8 _area; // 0 1 2 3 4
uint16 _numFrame;
uint16 _index;
};
// Shifted left by 1 - 4, depending on the subarea
#define SMKANIM_OFF_BASE 16
struct SAnim {
char _name[14];
uint16 _flag; // 1- background 2- icon 3- action 4- active - 4bits per child
Common::Rect _area[MAXAREA];
uint8 _nbox;
SAtFrame _atFrame[MAXATFRAME];
/**
* Toggle the animation of a subarea
* @param area: 1 - 4
* @param show: show or hide the animation area
*/
void toggleAnimArea(uint8 area, bool show) {
assert(area >= 1 && area <= 4);
if (show)
_flag &= ~(SMKANIM_OFF_BASE << area);
else
_flag |= (SMKANIM_OFF_BASE << area);
}
/**
* Checks if an animation subarea is shown
* @param area: 1 - 4
* @return true if the subarea is shown
*/
bool isAnimAreaShown(uint8 area) {
assert(area >= 1 && area <= 4);
return !(_flag & (SMKANIM_OFF_BASE << area));
}
};
struct SSortTable {
uint16 _objectId; // Object ID
bool _remove; // Whether to copy or remove
};
class Scheduler;
struct SScriptFrame {
uint8 _class;
uint8 _event;
uint8 _u8Param;
uint16 _u16Param1;
uint16 _u16Param2;
uint16 _u32Param;
bool _noWait;
void clear();
void sendFrame(Scheduler *scheduler);
bool isEmptyEvent() const { return _class == 0 && _event == 0; }
};
class TrecisionEngine;
struct SDText {
Common::Rect _rect;
Common::Rect _subtitleRect;
uint16 _textColor;
Common::String _text;
Common::String _drawTextLines[MAXDTEXTLINES];
void set(SDText *org);
void set(Common::Rect rect, Common::Rect subtitleRect, uint16 textCol, const Common::String &text);
void draw(TrecisionEngine *vm, bool hideLastChar = false, Graphics::Surface *externalSurface = nullptr);
uint16 calcHeight(TrecisionEngine *vm);
};
struct STexture {
int16 _dx, _dy, _angle;
uint8 *_texture;
void clear();
void set(int16 x, int16 y, uint8 *buffer);
bool isActive() { return _active; };
private:
bool _active;
};
struct SVertex {
float _x, _y, _z;
float _nx, _ny, _nz;
void clear();
};
struct SFace {
uint16 _a, _b, _c;
uint16 _mat;
};
struct SLight {
float _x, _y, _z;
float _dx, _dy, _dz;
float _inr, _outr;
uint8 _hotspot;
uint8 _fallOff;
int8 _inten;
int8 _position;
void clear();
};
struct SCamera {
float _ex, _ey, _ez;
float _e1[3];
float _e2[3];
float _e3[3];
float _fovX, _fovY;
void clear();
};
} // End of namespace Trecision
#endif

481
engines/trecision/text.cpp Normal file
View File

@@ -0,0 +1,481 @@
/* 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/config-manager.h"
#include "common/str.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/graphics.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
#include "trecision/text.h"
#include "trecision/scheduler.h"
#include "trecision/video.h"
namespace Trecision {
TextManager::TextManager(TrecisionEngine *vm) : _vm(vm) {
_someoneSpeakTime = 0;
_subStringAgain = false;
_talkTime = 0;
for (int i = 0; i < MAXSUBSTRING; ++i) {
for (int j = 0; j < MAXLENSUBSTRING; ++j) {
_subString[i][j] = 0;
}
}
_subStringUsed = 0;
_subStringStart = 0;
_curSentenceId = 0;
_curSubString = 0;
_talkingPersonId = 0;
}
TextManager::~TextManager() {
}
Common::Point TextManager::positionString(uint16 x, uint16 y, const char *string, bool characterFl) {
uint16 lenText = _vm->textLength(string);
Common::Point pos;
if (lenText > 960)
lenText = (lenText * 2 / 5);
else if (lenText > 320)
lenText = (lenText * 3 / 5);
if (x > (lenText >> 1))
x -= (lenText >> 1);
else
x = 0;
pos.x = CLIP<uint16>(x, 5, MAXX - lenText - 5);
pos.y = characterFl ? 0 : VIDEOTOP;
pos.y += y - 1; //15
if (pos.y <= VIDEOTOP)
pos.y = VIDEOTOP + 1;
return pos;
}
void TextManager::formattingSuperString() {
_subStringUsed = 0;
_subStringAgain = true;
while (_subStringAgain) {
formattingOneString();
++_subStringUsed;
}
}
void TextManager::formattingOneString() {
uint16 i;
memset(_subString[_subStringUsed], '\0', MAXLENSUBSTRING);
const uint16 available = (_superString.size() - _subStringStart);
for (i = 0; i < available; ++i) {
switch (_superString[i + _subStringStart]) {
case '\0':
_subStringAgain = false;
return;
case '@':
_subStringAgain = true;
_subStringStart += (i + 1);
return;
default:
_subString[_subStringUsed][i] = _superString[i + _subStringStart];
break;
}
}
_subString[_subStringUsed][i] = '\0';
_subStringAgain = false;
}
void TextManager::characterTalk(Common::String s) {
_vm->_flagSomeoneSpeaks = true;
_vm->_flagCharacterSpeak = true;
_vm->_flagSkipTalk = false;
_superString = s;
_subStringStart = 0;
_curSubString = 0;
formattingSuperString();
characterContinueTalk();
_vm->_scheduler->initCharacterQueue();
_vm->_actor->actorStop();
}
void TextManager::characterContinueTalk() {
Common::Point pos;
_vm->_flagSkipTalk = false;
_vm->_characterSpeakTime = _vm->_curTime;
_subStringAgain = (_curSubString < (_subStringUsed - 1));
if (_vm->_flagShowCharacter || _vm->_animMgr->isActionActive())
pos = positionString(_vm->_actor->_area[0], _vm->_actor->_area[2], _subString[_curSubString], true);
else
pos = positionString(MAXX / 2, 30, _subString[_curSubString], false);
clearLastText();
if (ConfMan.getBool("subtitles"))
addText(pos, _subString[_curSubString], COLOR_OBJECT);
if (!_vm->_flagDialogActive) {
if (_curSubString)
_lastFilename = Common::Path(Common::String::format("s%04d%c.wav", _curSentenceId, _curSubString + 'a'));
else
_lastFilename = Common::Path(Common::String::format("s%04d.wav", _curSentenceId));
}
_talkTime = _vm->_soundMgr->talkStart(_lastFilename);
if (!_talkTime)
_talkTime = Common::String(_subString[_curSubString]).size() * 5 / 2 + 50;
++_curSubString;
_vm->_scheduler->doEvent(MC_STRING, ME_CHARACTERSPEAKING, MP_DEFAULT, 0, 0, 0, 0);
}
void TextManager::characterMute() {
_vm->_flagSomeoneSpeaks = false;
_vm->_flagCharacterSpeak = false;
_vm->_flagSkipTalk = false;
_vm->_characterSpeakTime = 0;
clearLastText();
_vm->_lastObj = 0;
_vm->_lastInv = 0;
redrawString();
_vm->_soundMgr->stopSoundType(kSoundTypeSpeech);
if ((_vm->_curRoom == kRoom12CU) || (_vm->_curRoom == kRoom13CU))
_vm->changeRoom(_vm->_oldRoom);
}
void TextManager::someoneContinueTalk() {
_someoneSpeakTime = _vm->_curTime;
_vm->_flagSkipTalk = false;
_subStringAgain = (_curSubString < (_subStringUsed - 1));
Common::Point pos;
if (_talkingPersonId)
pos = positionString(_vm->_obj[_talkingPersonId]._area.left, _vm->_obj[_talkingPersonId]._area.top, _subString[_curSubString], false);
else
pos = positionString(_vm->_actor->_area[0], _vm->_actor->_area[2], _subString[_curSubString], true);
clearLastText();
if (ConfMan.getBool("subtitles"))
addText(pos, _subString[_curSubString], HYELLOW);
if (_curSubString)
_lastFilename = Common::Path(Common::String::format("s%04d%c.wav", _curSentenceId, _curSubString + 'a'));
else
_lastFilename = Common::Path(Common::String::format("s%04d.wav", _curSentenceId));
_talkTime = _vm->_soundMgr->talkStart(_lastFilename);
if (!_talkTime)
_talkTime = Common::String(_subString[_curSubString]).size() * 5 / 2 + 50;
++_curSubString;
_vm->_scheduler->doEvent(MC_STRING, ME_SOMEONESPEAKING, MP_DEFAULT, 0, 0, 0, 0);
}
void TextManager::someoneMute() {
_vm->_flagCharacterSpeak = false;
_vm->_flagSkipTalk = false;
_vm->_flagSomeoneSpeaks = false;
_someoneSpeakTime = 0;
clearLastText();
_vm->_lastObj = 0;
_vm->_lastInv = 0;
redrawString();
_vm->_soundMgr->stopSoundType(kSoundTypeSpeech);
}
// ******************************************************* //
void TextManager::doString() {
switch (_vm->_curMessage->_event) {
case ME_CHARACTERSPEAKING:
if (_vm->_flagCharacterSpeak) {
if (_vm->_flagSkipTalk || (_vm->_curTime > _talkTime + _vm->_characterSpeakTime)) {
if (_subStringAgain)
characterContinueTalk();
else
characterMute();
} else
_vm->reEvent();
}
break;
case ME_SOMEONESPEAKING:
if (_vm->_flagSomeoneSpeaks) {
if (_vm->_flagSkipTalk || (_vm->_curTime >= (_talkTime + _someoneSpeakTime))) {
if (_subStringAgain)
someoneContinueTalk();
else {
someoneMute();
}
} else
_vm->reEvent();
}
break;
default:
break;
}
}
void TextManager::showObjName(uint16 obj, bool show) {
static const char *dunno = "?";
Common::String desc;
if (_vm->_flagSomeoneSpeaks)
return;
if (_vm->_lastInv) {
clearLastText();
_vm->_lastInv = 0;
}
if (_vm->_flagUseWithStarted) {
if (!show) {
clearLastText();
_vm->_lastObj = obj;
return;
}
if ((_vm->_obj[_vm->_curObj].isFlagRoomOut() || _vm->_obj[_vm->_curObj].isFlagRoomIn()) && !_vm->_obj[_vm->_curObj].isFlagExamine())
return;
desc = _vm->_sysText[kMessageUse];
if (_vm->_useWithInv[USED])
desc += _vm->_objName[_vm->_inventoryObj[_vm->_useWith[USED]]._name];
else if (_vm->_obj[_vm->_useWith[USED]].isModeHidden())
desc += dunno;
else
desc += _vm->_objName[_vm->_obj[_vm->_useWith[USED]]._name];
desc += _vm->_sysText[kMessageWith];
if (obj && (_vm->_useWithInv[USED] || (obj != _vm->_useWith[USED]))) {
if (_vm->_obj[obj].isModeHidden())
desc += dunno;
else
desc += _vm->_objName[_vm->_obj[obj]._name];
}
_vm->_lastObj = (obj | 0x8000);
const uint16 lenText = _vm->textLength(desc);
const Common::Point pos(CLIP(320 - (lenText / 2), 2, MAXX - 2 - lenText), MAXY - CARHEI);
if (_vm->_lastObj)
clearLastText();
addText(pos, desc.c_str(), COLOR_INVENTORY);
} else {
if (!obj || !show) {
clearLastText();
_vm->_lastObj = obj;
return;
}
if (obj == _vm->_lastObj)
return;
if (!_vm->_obj[obj].isFlagExamine()) {
if (_vm->_obj[obj].isFlagDone() || _vm->_room[_vm->_obj[obj]._goRoom].isDone()) {
desc = _vm->_sysText[kMessageGoto];
if (_vm->_obj[obj].isModeHidden())
desc += dunno;
else
desc += _vm->_objName[_vm->_obj[obj]._name];
} else
desc = _vm->_sysText[kMessageGoto2];
} else if (_vm->_obj[obj].isModeHidden())
desc = dunno;
else
desc = _vm->_objName[_vm->_obj[obj]._name];
const uint16 x = (_vm->_obj[obj]._area.left + _vm->_obj[obj]._area.right) / 2;
const uint16 y = (obj == oWHEELS2C) ? 187 : _vm->_obj[obj]._area.top;
Common::Point pos = positionString(x, y, desc.c_str(), false);
if (_vm->_lastObj)
clearLastText();
_vm->_lastObj = obj;
addText(pos, desc.c_str(), COLOR_OBJECT);
}
}
void TextManager::someoneSay(uint16 sentence, uint16 person) {
_talkingPersonId = person;
_vm->_flagSomeoneSpeaks = true;
_vm->_flagSkipTalk = false;
_curSentenceId = sentence;
_superString = _vm->_sentence[sentence];
_subStringStart = 0;
_curSubString = 0;
formattingSuperString();
someoneContinueTalk();
}
void TextManager::characterSay(uint16 i) {
_curSentenceId = i;
// if he took some action
if (_vm->_sentence[i][0] == '*' && !_vm->_animMgr->isActionActive())
_vm->startCharacterAction(hBOH, 0, 0, 0);
else
characterTalk(_vm->_sentence[i]);
}
void TextManager::characterSayInAction(uint16 ss) {
const char *s = _vm->_sentence[ss];
if (s[0] == '*')
return;
_curSentenceId = ss;
_vm->_flagSomeoneSpeaks = true;
_vm->_flagCharacterSpeak = true;
_vm->_flagSkipTalk = false;
_superString = s;
_subStringStart = 0;
_curSubString = 0;
formattingSuperString();
characterContinueTalk();
}
void TextManager::addText(Common::Point pos, const char *text, uint16 textCol) {
StackText t;
t._x = pos.x;
t._y = pos.y;
t._textColor = textCol;
t._clear = false;
t._text = text;
_textStack.push_back(t);
}
void TextManager::clearLastText() {
if (!_textStack.empty()) {
if (!_textStack.back()._clear)
// The last entry is a string to be shown, remove it
_textStack.pop_back();
} else {
StackText t;
t._clear = true;
_textStack.push_back(t);
}
}
void TextManager::drawText(StackText *text) {
_curString._rect.left = text->_x;
_curString._rect.top = text->_y;
_curString._rect.setWidth(_vm->textLength(text->_text));
int16 w = _curString._rect.width();
if (text->_y == MAXY - CARHEI && w > 600)
w = w * 3 / 5;
else if (text->_y != MAXY - CARHEI && w > 960)
w = w * 2 / 5;
else if (text->_y != MAXY - CARHEI && w > 320)
w = w * 3 / 5;
_curString._rect.setWidth(w);
_curString._text = text->_text;
uint16 height = _curString.calcHeight(_vm);
_curString._subtitleRect = Common::Rect(_curString._rect.width(), height);
_curString._rect.setHeight(height);
_curString._textColor = text->_textColor;
if (_curString._rect.top <= height)
_curString._rect.top += height;
else
_curString._rect.top -= height;
if (_curString._rect.top <= VIDEOTOP)
_curString._rect.top = VIDEOTOP + 1;
_vm->_textStatus |= TEXT_DRAW;
}
void TextManager::clearText() {
if (_oldString._text.empty() && !_curString._text.empty()) {
_oldString.set(&_curString);
_curString._text.clear();
_vm->_textStatus |= TEXT_DEL;
}
}
void TextManager::drawTexts() {
for (Common::List<StackText>::iterator it = _textStack.begin(); it != _textStack.end(); ++it) {
if (it->_clear)
clearText();
else
drawText(&*it);
}
}
void TextManager::redrawString() {
if (!_vm->_flagDialogActive && !_vm->_flagDialogMenuActive && !_vm->_flagSomeoneSpeaks && !_vm->_flagScriptActive && _vm->_graphicsMgr->isCursorVisible()) {
if (_vm->isInventoryArea(_vm->_mousePos))
_vm->showIconName();
else {
_vm->checkMask(_vm->_mousePos);
showObjName(_vm->_curObj, true);
}
}
}
void TextManager::clearTextStack() {
_textStack.clear();
}
void TextManager::drawCurString() {
_curString.draw(_vm);
_vm->_graphicsMgr->addDirtyRect(_curString._rect, false);
}
Common::Rect TextManager::getOldTextRect() const {
return _oldString._rect;
}
void TextManager::clearOldText() {
_oldString._text.clear();
}
} // End of namespace Trecision

96
engines/trecision/text.h Normal file
View File

@@ -0,0 +1,96 @@
/* 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 TRECISION_TEXT_H
#define TRECISION_TEXT_H
#define MAXLENSUBSTRING 128
#define MAXSUBSTRING 16
#include "common/scummsys.h"
#include "trecision/struct.h"
namespace Trecision {
class TrecisionEngine;
struct StackText {
uint16 _x = 0;
uint16 _y = 0;
uint16 _textColor = 0;
Common::String _text;
bool _clear = false;
};
class TextManager {
TrecisionEngine *_vm;
uint32 _someoneSpeakTime;
bool _subStringAgain;
uint32 _talkTime;
char _subString[MAXSUBSTRING][MAXLENSUBSTRING];
uint16 _subStringUsed;
Common::String _superString;
uint16 _subStringStart;
uint16 _curSentenceId;
uint16 _curSubString;
Common::Path _lastFilename;
uint16 _talkingPersonId;
SDText _curString;
SDText _oldString;
Common::List<StackText> _textStack;
Common::Point positionString(uint16 x, uint16 y, const char *string, bool characterFl);
void formattingSuperString();
void formattingOneString();
void characterTalk(Common::String s);
void characterContinueTalk();
void characterMute();
void someoneContinueTalk();
void someoneMute();
public:
TextManager(TrecisionEngine *vm);
~TextManager();
void doString();
void showObjName(uint16 obj, bool show);
void someoneSay(uint16 sentence, uint16 person);
void characterSay(uint16 i);
void characterSayInAction(uint16 ss);
void addText(Common::Point pos, const char *text, uint16 textCol);
void clearLastText();
void drawText(StackText *text);
void clearText();
void drawTexts();
void redrawString();
void clearTextStack();
Common::Rect getOldTextRect() const;
void clearOldText();
void drawCurString();
};
} // End of namespace Trecision
#endif

View 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/>.
*
*/
#include "common/scummsys.h"
#include "common/error.h"
#include "common/system.h"
#include "common/events.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/str.h"
#include "backends/keymapper/keymapper.h"
#include "trecision/animmanager.h"
#include "trecision/animtype.h"
#include "trecision/actor.h"
#include "trecision/console.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/pathfinding3d.h"
#include "trecision/renderer3d.h"
#include "trecision/logic.h"
#include "trecision/scheduler.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
#include "trecision/text.h"
#include "trecision/video.h"
namespace Common {
class File;
}
namespace Trecision {
TrecisionEngine::TrecisionEngine(OSystem *syst, const ADGameDescription *desc) : Engine(syst), _gameDescription(desc) {
_gameId = !strcmp(_gameDescription->gameId, "nl") ? GID_NightLong : GID_ArkOfTime;
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "AUTORUN");
SearchMan.addSubDirectoryMatching(gameDataDir, "DATA");
SearchMan.addSubDirectoryMatching(gameDataDir, "FMV");
// Amiga version loads data files from directories
if (isAmiga()) {
SearchMan.addSubDirectoryMatching(gameDataDir, "NLDATA.CD0");
SearchMan.addSubDirectoryMatching(gameDataDir, "NLSPEECH.CD0");
SearchMan.addSubDirectoryMatching(gameDataDir, "NLANIM.CDX");
}
_curRoom = 0;
_oldRoom = 0;
_curInventory = 0;
for (int i = 0; i < 10; ++i)
_curScriptFrame[i] = 0;
_iconBase = 0;
_inventoryRefreshStartIcon = 0;
_curObj = 1;
_inventoryRefreshStartLine = INVENTORY_HIDE;
_lightIcon = 0xFF;
_inventoryStatus = INV_OFF;
_inventoryCounter = INVENTORY_HIDE;
_flagInventoryLocked = false;
_inventorySpeedIndex = 0;
_inventoryScrollTime = 0;
_fastWalk = false;
// Use With
_useWith[0] = _useWith[1] = 0;
_useWithInv[0] = _useWithInv[1] = false;
// Messages
for (int i = 0; i < MAXOBJNAME; ++i)
_objName[i] = nullptr;
for (int i = 0; i < MAXSENTENCE; ++i)
_sentence[i] = nullptr;
for (int i = 0; i < MAXSYSTEXT; ++i)
_sysText[i] = nullptr;
_curMessage = nullptr;
_animMgr = nullptr;
_dialogMgr = nullptr;
_graphicsMgr = nullptr;
_logicMgr = nullptr;
_soundMgr = nullptr;
_renderer = nullptr;
_pathFind = nullptr;
_textMgr = nullptr;
_animTypeMgr = nullptr;
_nextRefresh = 0;
_curKey = Common::KEYCODE_INVALID;
_curAction = kActionNone;
_curAscii = 0;
_mousePos = Common::Point(0, 0);
_mouseMoved = _mouseLeftBtn = _mouseRightBtn = false;
_keybInput = false;
_gamePaused = false;
_textPtr = nullptr;
_lastInv = 0;
_lastObj = 0;
_curStack = 0;
_flagScriptActive = false;
_actor = nullptr;
_flagDialogActive = false;
_flagDialogMenuActive = false;
_flagSkipTalk = false;
_flagPaintCharacter = false;
_flagShowCharacter = true;
_flagSomeoneSpeaks = false;
_flagCharacterSpeak = false;
_flagUseWithStarted = false;
_flagNoPaintScreen = false;
_flagWaitRegen = false;
for (int i = 0; i < MAXOBJINROOM; ++i) {
_objectGraphics[i].buf = nullptr;
_objectGraphics[i].mask = nullptr;
}
_curTime = 0;
_characterSpeakTime = 0;
_pauseStartTime = 0;
_textStatus = TEXT_OFF;
_cx = _cy = 0;
_textArea = nullptr;
Message msg = { MC_IDLE, 0, MP_DEFAULT, 0, 0, 0, 0 };
_snake52 = msg;
for (int i = 0; i < 50; ++i)
_scriptFrame[i].clear();
_scheduler = nullptr;
}
TrecisionEngine::~TrecisionEngine() {
if (_animMgr)
_animMgr->stopAllSmkAnims();
_dataFile.close();
_thumbnail.free();
delete _animMgr;
delete _dialogMgr;
delete _graphicsMgr;
delete _logicMgr;
delete _soundMgr;
delete _renderer;
delete _pathFind;
delete _textMgr;
delete _scheduler;
delete _animTypeMgr;
delete _actor;
delete[] _textArea;
for (int i = 0; i < MAXOBJINROOM; ++i) {
delete[] _objectGraphics[i].buf;
delete[] _objectGraphics[i].mask;
}
}
Common::Error TrecisionEngine::run() {
syncSoundSettings();
if (!_dataFile.open(this, "nldata.cd0"))
error("Error opening nldata.cd0");
_graphicsMgr = new GraphicsManager(this);
if (!_graphicsMgr->init())
return Common::kUnsupportedColorMode;
_animMgr = new AnimManager(this);
_dialogMgr = new DialogManager(this);
_logicMgr = new LogicManager(this);
_soundMgr = new SoundManager(this);
_pathFind = new PathFinding3D(this);
_renderer = new Renderer3D(this);
_textMgr = new TextManager(this);
_scheduler = new Scheduler(this);
_animTypeMgr = new AnimTypeManager(this);
_actor = new Actor(this);
setDebugger(new Console(this));
initMain();
while (!shouldQuit()) {
eventLoop();
if (!_flagNoPaintScreen)
processTime();
processMouse();
_scheduler->process();
_animTypeMgr->handler(kAnimTypeBackground);
processCurrentMessage();
if (_flagScriptActive)
evalScript();
}
if (isDemo())
_graphicsMgr->showDemoPic();
return Common::kNoError;
}
void TrecisionEngine::eventLoop() {
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
Common::Keymapper *keymapper = _eventMan->getKeymapper();
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
_mouseMoved = true;
_mousePos = event.mouse;
break;
case Common::EVENT_LBUTTONUP:
_mouseLeftBtn = true;
break;
case Common::EVENT_RBUTTONUP:
_mouseRightBtn = true;
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
_curAction = event.customType;
switch (event.customType) {
case kActionFastWalk:
_fastWalk ^= true;
break;
case kActionPause:
if (!_gamePaused && !_keybInput) {
_curKey = Common::KEYCODE_INVALID;
_curAction = kActionNone;
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
_gamePaused = true;
waitKey();
}
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
_gamePaused = false;
break;
default:
break;
}
return;
case Common::EVENT_KEYUP:
_curKey = event.kbd.keycode;
_curAscii = event.kbd.ascii;
break;
case Common::EVENT_JOYBUTTON_UP:
_joyButtonUp = true;
break;
default:
break;
}
}
g_system->delayMillis(10);
g_system->updateScreen();
}
void TrecisionEngine::initMain() {
for (int c = 0; c < MAXOBJ; c++)
_obj[c]._position = -1;
_curRoom = kRoomIntro;
_scheduler->init();
loadAll();
processTime();
// Check if a saved game is to be loaded from the launcher
if (ConfMan.hasKey("save_slot"))
loadGameState(ConfMan.getInt("save_slot"));
else
changeRoom(_curRoom);
}
void TrecisionEngine::checkSystem() {
eventLoop();
}
void TrecisionEngine::startCharacterAction(uint16 action, uint16 newRoom, uint8 newPos, uint16 textId) {
_scheduler->initCharacterQueue();
_flagInventoryLocked = false;
if (action > hLAST) {
_animMgr->startSmkAnim(action);
_animTypeMgr->init(action, _curObj);
_graphicsMgr->hideCursor();
_flagShowCharacter = false;
_scheduler->doEvent(MC_CHARACTER, ME_CHARACTERCONTINUEACTION, MP_DEFAULT, action, newRoom, newPos, _curObj);
} else {
if ((action == aWALKIN) || (action == aWALKOUT))
_curObj = 0;
_graphicsMgr->hideCursor();
_actor->actorDoAction(action);
_pathFind->nextStep();
}
if (textId)
_textMgr->characterSayInAction(textId);
else
_textMgr->clearLastText();
}
bool TrecisionEngine::canPlayerInteract() {
return (!_flagSomeoneSpeaks &&
!_flagScriptActive &&
!_flagDialogActive &&
!_flagDialogMenuActive &&
(_actor->_curAction < hWALKIN) &&
!_flagUseWithStarted &&
_flagShowCharacter &&
!_animMgr->isActionActive());
}
void TrecisionEngine::setObjectVisible(uint16 objectId, bool visible) {
_obj[objectId].setModeStatus(visible);
refreshObject(objectId);
}
void TrecisionEngine::refreshObject(uint16 objectId) {
for (int i = 0; i < MAXOBJINROOM; ++i) {
if (!_room[_curRoom]._object[i])
return; // reached the end of the list, object not found
if (objectId == _room[_curRoom]._object[i]) {
break; // object found in room objects, continue
}
}
if (_obj[objectId].isModeMask() || _obj[objectId].isModeFull()) {
SSortTable entry;
entry._objectId = objectId;
entry._remove = !isObjectVisible(objectId);
_sortTable.push_back(entry);
// Remove previous instances
for (Common::List<SSortTable>::iterator it = _sortTableReplay.begin(); it != _sortTableReplay.end(); ++it) {
if (it->_objectId == objectId) {
_sortTableReplay.erase(it);
break;
}
}
_sortTableReplay.push_back(entry);
}
}
bool TrecisionEngine::isObjectVisible(uint16 objectId) {
return _obj[objectId].isModeStatus();
}
void TrecisionEngine::setObjectAnim(uint16 objectId, uint16 animId) {
_obj[objectId]._anim = animId;
}
void TrecisionEngine::reEvent() {
_scheduler->doEvent(_curMessage->_class, _curMessage->_event, _curMessage->_priority, _curMessage->_u16Param1, _curMessage->_u16Param2, _curMessage->_u8Param, _curMessage->_u32Param);
}
Common::SeekableReadStreamEndian *TrecisionEngine::getLocStream() {
Common::Path filename(_room[_curRoom]._baseName);
if (isAmiga()) {
filename.appendInPlace(".bm");
return readEndian(_dataFile.createReadStreamForMember(filename));
} else {
filename.appendInPlace(".cr");
return readEndian(_dataFile.createReadStreamForCompressedMember(filename));
}
}
void TrecisionEngine::readLoc() {
_soundMgr->stopAllExceptMusic();
_graphicsMgr->clearScreenBufferTop();
_sortTable.clear();
_sortTableReplay.clear();
Common::SeekableReadStreamEndian *picFile = getLocStream();
_graphicsMgr->loadBackground(picFile);
readObj(picFile);
_soundMgr->stopAll();
if (_room[_curRoom]._sounds[0] != 0)
_soundMgr->loadRoomSounds();
Common::Path fname(Common::String::format("%s.3d", _room[_curRoom]._baseName));
read3D(fname);
if (_room[_curRoom]._bkgAnim) {
_animMgr->startSmkAnim(_room[_curRoom]._bkgAnim);
} else
_animMgr->smkStop(kSmackerBackground);
_animTypeMgr->init(_room[_curRoom]._bkgAnim, 0);
}
void TrecisionEngine::redrawRoom() {
const uint16 curDialog = _dialogMgr->getCurDialog();
const uint16 curChoice = _dialogMgr->getCurChoice();
const uint16 bgAnim = _room[_curRoom]._bkgAnim;
static const ElevatorAction elevatorActions[6] = {
{dASCENSORE12, 3, a129PARLACOMPUTERESCENDE, kRoom13},
{dASCENSORE12, 4, a129PARLACOMPUTERESCENDE, kRoom16},
{dASCENSORE13, 17, a139CHIUDONOPORTESU, kRoom12},
{dASCENSORE13, 18, a1316CHIUDONOPORTEGIU, kRoom16},
{dASCENSORE16, 32, a1616SALECONASCENSORE, kRoom12},
{dASCENSORE16, 33, a1616SALECONASCENSORE, kRoom13},
};
_flagShowCharacter = _dialogMgr->showCharacterAfterDialog();
_flagPaintCharacter = true;
_textStatus = TEXT_OFF;
for (int i = 0; i < 6; ++i) {
if (curDialog == elevatorActions[i].dialog && curChoice == elevatorActions[i].choice) {
startCharacterAction(elevatorActions[i].action, elevatorActions[i].newRoom, 20, 0);
break;
}
}
Common::SeekableReadStreamEndian *picFile = getLocStream();
_graphicsMgr->loadBackground(picFile);
_sortTable.clear();
_sortTable = _sortTableReplay;
if (bgAnim)
_animMgr->startSmkAnim(bgAnim);
if (_curRoom == kRoom4P && curDialog == dF4PI)
_animMgr->smkGoto(kSmackerBackground, 21);
_graphicsMgr->paintScreen(true);
}
void TrecisionEngine::tendIn() {
_textStatus = TEXT_OFF;
_flagPaintCharacter = true;
_graphicsMgr->paintScreen(true);
_graphicsMgr->copyToScreen(0, 0, MAXX, MAXY);
}
} // End of namespace Trecision

View File

@@ -0,0 +1,382 @@
/* 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 TRECISION_TRECISION_H
#define TRECISION_TRECISION_H
#include "common/str-array.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/str.h"
#include "common/serializer.h"
#include "common/stream.h"
#include "engines/advancedDetector.h"
#include "engines/engine.h"
#include "graphics/surface.h"
#include "trecision/defines.h"
#include "trecision/fastfile.h"
#include "trecision/struct.h"
#include "trecision/scheduler.h"
namespace Trecision {
class AnimManager;
class DialogManager;
class GraphicsManager;
class LogicManager;
class SoundManager;
class Actor;
class Renderer3D;
class PathFinding3D;
class TextManager;
class Scheduler;
class AnimTypeManager;
// Saved game versions
// Version history:
// - 102: Original PC full version
// - 103: Original PC demo version
// - 110: First ScummVM version
#define SAVE_VERSION_ORIGINAL_MIN 102
#define SAVE_VERSION_ORIGINAL_MAX 109
#define SAVE_VERSION_SCUMMVM_MIN 110
#define SAVE_VERSION_SCUMMVM 110
#define MAXROOMS 100 // Game rooms
#define MAXOBJ 1400 // Game objects
#define MAXINVENTORY 150 // Inventory Items
#define MAXSAVEFILE 12
enum TrecisionGameId {
GID_ArkOfTime = 0,
GID_NightLong = 1
};
enum TrecisionMessageIds {
kMessageSavePosition = 9,
kMessageEmptySpot = 10,
kMessageLoadPosition = 11,
kMessageConfirmExit = 13,
kMessageDemoOver = 17,
kMessageError = 19,
kMessageUse = 23,
kMessageWith = 24,
kMessageGoto = 25,
kMessageGoto2 = 26
};
enum TRECISIONAction {
kActionNone,
kActionSkipVideo,
kActionFastWalk,
kActionPause,
kActionQuit,
kActionSystemMenu,
kActionSave,
kActionLoad,
kActionYes
};
typedef Common::List<Common::Rect>::iterator DirtyRectsIterator;
struct ElevatorAction {
uint16 dialog;
uint16 choice;
uint16 action;
uint16 newRoom;
};
struct ObjectGraphics {
uint16 *buf;
uint8 *mask;
};
class TrecisionEngine : public Engine {
void initMain();
void loadAll();
void loadSaveSlots(Common::StringArray &saveNames);
void eventLoop();
// Inventory
void refreshInventory(uint8 startIcon, uint8 startLine);
void moveInventoryLeft();
void moveInventoryRight();
void syncInventory(Common::Serializer &ser);
void rollInventory(uint8 status);
void doScrollInventory(Common::Point pos);
void endUseWith();
// Script
void endScript();
void evalScript();
void processScriptFrame();
void doAction();
void doMouse();
void processMouseMovement();
void doCharacter();
void doIdle();
void doRoomIn(uint16 curObj);
void doRoomOut(uint16 curObj);
void doMouseExamine(uint16 curObj);
void doMouseOperate(uint16 curObj);
void doMouseTake(uint16 curObj);
void doUseWith();
void doScreenUseWithScreen();
void doInvExamine();
void doInvOperate();
void doScript();
void processCurrentMessage();
// Utils
char *getNextSentence();
uint16 getKey();
uint16 getAction();
void processTime();
void processMouse();
static bool isBetween(int a, int x, int b);
// Others
bool canPlayerInteract();
// Objects
void readObj(Common::SeekableReadStream *stream);
void readObject(Common::SeekableReadStream *stream, uint16 objIndex, uint16 objectId);
TrecisionGameId _gameId;
char *_textArea;
uint16 _curScriptFrame[10];
char *_textPtr;
uint16 _curAscii;
bool _keybInput;
bool _gamePaused;
uint8 _curStack;
Common::List<SSortTable> _sortTableReplay;
public:
TrecisionEngine(OSystem *syst, const ADGameDescription *desc);
~TrecisionEngine() override;
// ScummVM
Common::Error run() override;
TrecisionGameId getGameId() const { return _gameId; }
bool isDemo() const { return _gameDescription->flags & ADGF_DEMO; }
bool isAmiga() const { return _gameDescription->platform == Common::kPlatformAmiga; }
bool hasFeature(EngineFeature f) const override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return canPlayerInteract() && _curRoom != kRoomIntro; }
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return canPlayerInteract() && _curRoom != kRoomIntro; }
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
bool syncGameStream(Common::Serializer &ser);
// Data files
Common::SeekableReadStreamEndian *readEndian(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES);
void read3D(const Common::Path &c);
// Inventory
void setInventoryStart(uint8 startIcon, uint8 startLine);
void showInventoryName(uint16 obj, bool showhide);
void showIconName();
uint8 whatIcon(Common::Point pos);
int8 iconPos(uint8 icon);
void removeIcon(uint8 icon);
void addIcon(uint8 icon);
void replaceIcon(uint8 oldIcon, uint8 newIcon);
void openInventory();
void closeInventory();
void closeInventoryImmediately();
void useItem();
void examineItem();
void clearUseWith();
// Script
void playScript(uint16 id);
bool quitPrompt();
void demoOver();
void startCharacterAction(uint16 action, uint16 newRoom, uint8 newPos, uint16 sent);
void doMouseTalk(uint16 curObj);
void changeRoom(uint16 room, uint16 action = 0, byte position = 0);
// Utils
uint16 textLength(const Common::String &text, uint16 begin = 0, uint16 end = 0);
Common::KeyCode waitKey();
void waitDelay(uint32 val);
void freeKey();
uint32 readTime();
bool checkMask(Common::Point pos);
float sinCosAngle(float sinus, float cosinus);
float dist2D(float x1, float y1, float x2, float y2);
float dist3D(float x1, float y1, float z1, float x2, float y2, float z2);
static bool isGameArea(Common::Point pos);
static bool isInventoryArea(Common::Point pos);
static bool isIconArea(Common::Point pos);
int getRoomObjectIndex(uint16 objectId);
int floatComp(float f1, float f2) const;
// Others
void checkSystem();
bool dataSave();
bool dataLoad();
void reEvent();
// Objects
void setObjectVisible(uint16 objectId, bool visible);
void refreshObject(uint16 objectId);
bool isObjectVisible(uint16 objectId);
void setObjectAnim(uint16 objectId, uint16 animId);
void redrawRoom();
void readLoc();
Common::SeekableReadStreamEndian *getLocStream();
void tendIn();
void readExtraObj2C();
void readPositionerSnapshots();
// Data files
byte *readData(const Common::Path &fileName);
const ADGameDescription *_gameDescription;
Graphics::Surface _thumbnail;
bool _controlPanelSave = false;
uint16 _curRoom;
uint16 _oldRoom;
SRoom _room[MAXROOMS];
Common::List<SSortTable> _sortTable;
uint16 _curObj;
SObject _obj[MAXOBJ];
SDText _drawText;
// Inventory
uint16 _curInventory;
SInvObject _inventoryObj[MAXINVENTORY];
Common::Array<byte> _inventory;
Common::Array<byte> _cyberInventory;
uint8 _iconBase;
uint8 _inventoryStatus;
uint8 _lightIcon;
uint8 _inventoryRefreshStartIcon;
uint8 _inventoryRefreshStartLine;
int16 _inventoryCounter;
bool _flagInventoryLocked;
uint8 _inventorySpeedIndex;
uint32 _inventoryScrollTime;
uint16 _lastInv;
uint16 _lastObj;
bool _fastWalk;
// Use With
uint16 _useWith[2];
bool _useWithInv[2];
// Messages
const char *_objName[MAXOBJNAME];
const char *_sentence[MAXSENTENCE];
const char *_sysText[MAXSYSTEXT];
// Message system
Message *_curMessage;
// Snake management
Message _snake52;
uint32 _nextRefresh;
Common::Point _mousePos;
bool _mouseMoved, _mouseLeftBtn, _mouseRightBtn;
Common::KeyCode _curKey;
Common::CustomEventType _curAction;
bool _joyButtonUp = false;
bool _flagScriptActive;
SScriptFrame _scriptFrame[MAXSCRIPTFRAME];
uint16 _scriptFirstFrame[MAXSCRIPT];
AnimManager *_animMgr;
GraphicsManager *_graphicsMgr;
DialogManager *_dialogMgr;
LogicManager *_logicMgr;
SoundManager *_soundMgr;
Renderer3D *_renderer;
PathFinding3D *_pathFind;
TextManager *_textMgr;
Scheduler *_scheduler;
AnimTypeManager *_animTypeMgr;
Actor *_actor;
// Data files
FastFile _dataFile; // nldata.cd0
bool _flagDialogActive;
bool _flagDialogMenuActive;
bool _flagSkipTalk;
bool _flagPaintCharacter;
bool _flagShowCharacter;
bool _flagSomeoneSpeaks;
bool _flagCharacterSpeak;
bool _flagUseWithStarted;
bool _flagNoPaintScreen;
bool _flagWaitRegen;
ObjectGraphics _objectGraphics[MAXOBJINROOM];
uint32 _curTime;
uint32 _characterSpeakTime;
int _cx, _cy;
uint8 _textStatus;
uint32 _pauseStartTime;
};
uint8 static const defActionLen[hLAST + 1] = {
/* STAND */ 1,
/* PARTE */ 1,
/* WALK */ 10,
/* END */ 1,
/* STOP0 */ 3,
/* STOP1 */ 4,
/* STOP2 */ 3,
/* STOP3 */ 2,
/* STOP4 */ 3,
/* STOP5 */ 4,
/* STOP6 */ 3,
/* STOP7 */ 3,
/* STOP8 */ 2,
/* STOP9 */ 3,
/* WALKI */ 12,
/* BOH */ 9,
/* UGG */ 41,
/* UTT */ 35,
/* WALKO */ 12,
/* LAST */ 15
};
} // End of namespace Trecision
#endif

412
engines/trecision/utils.cpp Normal file
View File

@@ -0,0 +1,412 @@
/* 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/system.h"
#include "trecision/scheduler.h"
#include "trecision/text.h"
#include "trecision/defines.h"
#include "trecision/graphics.h"
#include "trecision/trecision.h"
#include "trecision/struct.h"
namespace Trecision {
char *TrecisionEngine::getNextSentence() {
while (*_textPtr) {
*_textPtr = ~(*_textPtr);
++_textPtr;
}
++_textPtr;
return _textPtr;
}
/**
* Compute string length from character begin to end
*/
uint16 TrecisionEngine::textLength(const Common::String &text, uint16 begin, uint16 end) {
if (text.empty())
return 0;
if (end == 0)
end = text.size();
uint16 retVal = 0;
for (uint16 c = begin; c < end; ++c)
retVal += _graphicsMgr->getCharWidth(text[c]);
return retVal;
}
uint16 TrecisionEngine::getKey() {
Common::KeyCode key = _curKey;
uint16 ascii = _curAscii;
_curKey = Common::KEYCODE_INVALID;
_curAscii = 0;
switch (key) {
case Common::KEYCODE_SPACE:
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_RETURN:
case Common::KEYCODE_CLEAR:
case Common::KEYCODE_BACKSPACE:
return key;
case Common::KEYCODE_F1:
case Common::KEYCODE_F2:
case Common::KEYCODE_F3:
case Common::KEYCODE_F4:
case Common::KEYCODE_F5:
case Common::KEYCODE_F6:
return 0x3B + key - Common::KEYCODE_F1;
default:
if (ascii)
return ascii;
return 0;
}
}
uint16 TrecisionEngine::getAction() {
Common::CustomEventType customType = _curAction;
_curAction = kActionNone;
return customType;
}
Common::KeyCode TrecisionEngine::waitKey() {
_graphicsMgr->hideCursor();
while (_curKey == Common::KEYCODE_INVALID && _curAction == kActionNone && !_joyButtonUp)
checkSystem();
_graphicsMgr->showCursor();
Common::KeyCode t = _curKey;
_curKey = Common::KEYCODE_INVALID;
_joyButtonUp = false;
return t;
}
void TrecisionEngine::waitDelay(uint32 val) {
uint32 sv = readTime();
while (sv + val > readTime())
checkSystem();
}
void TrecisionEngine::freeKey() {
_curKey = Common::KEYCODE_INVALID;
}
uint32 TrecisionEngine::readTime() {
return (g_system->getMillis() * 3) / 50;
}
bool TrecisionEngine::checkMask(Common::Point pos) {
for (int8 i = MAXOBJINROOM - 1; i >= 0; --i) {
uint16 checkedObj = _room[_curRoom]._object[i];
Common::Rect lim = _obj[checkedObj]._area;
lim.translate(0, TOP);
// Trecision includes the bottom and right coordinates
++lim.right;
++lim.bottom;
if (checkedObj && isObjectVisible(checkedObj)) {
if (lim.contains(pos)) {
if (_obj[checkedObj].isModeFull() || _obj[checkedObj].isModeLim()) {
_curObj = checkedObj;
return true;
}
if (_obj[checkedObj].isModeMask()) {
uint8 *mask = _objectGraphics[i].mask;
int16 d = _obj[checkedObj]._rect.left;
uint16 max = _obj[checkedObj]._rect.bottom;
for (uint16 j = _obj[checkedObj]._rect.top; j < max; ++j) {
bool insideObj = false;
int16 e = 0;
while (e < _obj[checkedObj]._rect.width()) {
if (!insideObj) { // not inside an object
if (j + TOP == pos.y) {
if ((pos.x >= d + e) && (pos.x < d + e + *mask)) {
_curObj = 0;
}
}
e += *mask;
++mask;
insideObj = true;
} else { // inside an object
if (j + TOP == pos.y) {
if ((pos.x >= d + e) && (pos.x < d + e + *mask)) {
_curObj = checkedObj;
return true;
}
}
e += *mask;
++mask;
insideObj = false;
}
}
}
}
}
}
}
_curObj = 0;
return false;
}
float TrecisionEngine::sinCosAngle(float sinus, float cosinus) {
if (floatComp(sinus, 0.0f) == 0 && floatComp(cosinus, 0.0f) == 0)
return 0;
float t = (float)sqrt((double)(sinus * sinus) + (double)(cosinus * cosinus));
cosinus /= t;
sinus /= t;
// 1e3 & 2e4 quad
if (floatComp(sinus, 0.0f) >= 0)
// 1 & 2 quad
return (float)acos(cosinus);
// 3 quad
return (M_PI * 2) - (float)acos(cosinus);
}
void TrecisionEngine::processTime() {
_curTime = readTime();
if (_curTime >= _nextRefresh) {
if (_inventoryStatus == INV_PAINT || _inventoryStatus == INV_DEPAINT)
rollInventory(_inventoryStatus);
if (_inventoryStatus != INV_OFF) {
refreshInventory(_inventoryRefreshStartIcon, _inventoryRefreshStartLine);
}
_textMgr->drawTexts();
_graphicsMgr->paintScreen(false);
_textMgr->clearTextStack();
uint32 paintTime = readTime();
if (paintTime - _curTime >= 5)
_nextRefresh = paintTime + 1;
else
_nextRefresh = _curTime + 5;
}
}
void TrecisionEngine::processMouse() {
int16 mx = _mousePos.x;
int16 my = _mousePos.y;
checkSystem();
if (!_graphicsMgr->isCursorVisible())
return;
if (_mouseLeftBtn) {
_scheduler->leftClick(mx, my);
_mouseLeftBtn = false;
} else if (_mouseRightBtn) {
_scheduler->rightClick(mx, my);
_mouseRightBtn = false;
} else {
if (!_flagScriptActive && _mouseMoved) {
processMouseMovement();
_mouseMoved = false;
}
}
}
/**
* Fake distance between two 2D points
*/
float TrecisionEngine::dist2D(float x1, float y1, float x2, float y2) {
const double dx = x1 - x2;
const double dy = y1 - y2;
return (float)sqrt(dx * dx + dy * dy);
}
/**
* Distance between two 3D points
*/
float TrecisionEngine::dist3D(float x1, float y1, float z1, float x2, float y2, float z2) {
const double dx = x1 - x2;
const double dy = y1 - y2;
const double dz = z1 - z2;
return (float)sqrt(dx * dx + dy * dy + dz * dz);
}
bool TrecisionEngine::isBetween(int a, int x, int b) {
return x >= a && x <= b;
}
bool TrecisionEngine::isGameArea(Common::Point pos) {
return isBetween(TOP, pos.y, TOP + AREA - 1);
}
bool TrecisionEngine::isInventoryArea(Common::Point pos) {
return pos.y >= TOP + AREA;
}
bool TrecisionEngine::isIconArea(Common::Point pos) {
return pos.y >= TOP + AREA && pos.y < MAXY && pos.x >= ICONMARGSX && pos.x <= MAXX - ICONMARGDX;
}
int TrecisionEngine::getRoomObjectIndex(uint16 objectId) {
for (uint16 index = 0; index < MAXOBJINROOM; ++index) {
const uint16 curObjId = _room[_curRoom]._object[index];
if (curObjId == 0)
return -1;
if (curObjId == objectId)
return index;
}
return -1;
}
/************************************************
* SDText
************************************************/
void SDText::set(SDText *org) {
set(org->_rect, org->_subtitleRect, org->_textColor, org->_text);
}
void SDText::set(Common::Rect rect, Common::Rect subtitleRect, uint16 textCol, const Common::String &text) {
_rect = rect;
_subtitleRect = subtitleRect;
_textColor = textCol;
_text = text;
// Clean output buffer
for (int i = 0; i < MAXDTEXTLINES; ++i)
_drawTextLines[i] = "";
}
/**
* calcHeight - Computes and returns the dy of the current text
*/
uint16 SDText::calcHeight(TrecisionEngine *vm) {
if (_text.empty())
return 0;
uint8 curLine = 0;
if (vm->textLength(_text) <= _rect.width()) {
_drawTextLines[curLine] = _text;
return CARHEI;
}
uint16 index = 0;
uint16 tmpDy = 0;
uint16 lastSpace = 0;
uint16 curInit = 0;
while (index < _text.size()) {
++index;
if (index < _text.size() && _text[index] == ' ') {
if (vm->textLength(_text, curInit, index) <= _rect.width())
lastSpace = index;
else if (vm->textLength(_text, curInit, lastSpace) <= _rect.width()) {
_drawTextLines[curLine] = _text.substr(curInit, lastSpace - curInit);
++curLine;
curInit = lastSpace + 1;
tmpDy += CARHEI;
index = curInit;
} else
return 0;
} else if (index == _text.size()) {
if (vm->textLength(_text, curInit, index) <= _rect.width()) {
_drawTextLines[curLine] = _text.substr(curInit, index - curInit);
tmpDy += CARHEI;
return tmpDy;
}
if (vm->textLength(_text, curInit, lastSpace) <= _rect.width()) {
_drawTextLines[curLine] = _text.substr(curInit, lastSpace - curInit);
++curLine;
curInit = lastSpace + 1;
tmpDy += CARHEI;
if (curInit < _text.size()) {
_drawTextLines[curLine] = _text.substr(curInit);
tmpDy += CARHEI;
}
return tmpDy;
}
return 0;
}
}
return 0;
}
void SDText::draw(TrecisionEngine *vm, bool hideLastChar, Graphics::Surface *externalSurface) {
uint16 textColor = vm->_graphicsMgr->convertToScreenFormat(_textColor);
if (_text.empty())
return;
const uint16 curDy = calcHeight(vm);
for (uint16 line = 0; line < curDy / CARHEI; ++line) {
Common::String curText = _drawTextLines[line];
uint16 inc = (_rect.width() - vm->textLength(curText)) / 2;
if (curText.size() >= MAXCHARS) {
curText = vm->_sysText[kMessageError];
}
for (uint index = 0; index < curText.size(); ++index) {
const byte curChar = curText[index];
if (index == curText.size() - 1 && hideLastChar)
textColor = vm->_graphicsMgr->convertToScreenFormat(0);
vm->_graphicsMgr->drawChar(curChar, textColor, line, _rect, _subtitleRect, inc, externalSurface);
inc += vm->_graphicsMgr->getCharWidth(curChar);
}
}
}
int TrecisionEngine::floatComp(float f1, float f2) const {
static const float epsilon = 1.0e-05f;
const float diff = f1 - f2;
if (ABS(diff) < epsilon)
// equality
return 0;
if (f1 > f2)
return 1;
return -1;
}
} // End of namespace Trecision

214
engines/trecision/video.cpp Normal file
View 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/>.
*
*/
#include "common/config-manager.h"
#include "common/events.h"
#include "common/file.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "audio/decoders/raw.h"
#include "trecision/actor.h"
#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/sound.h"
#include "trecision/text.h"
#include "trecision/trecision.h"
#include "trecision/video.h"
namespace Trecision {
void NightlongVideoDecoder::muteTrack(uint track, bool mute) {
// FIXME: In the Amiga version, there's only one audio track
// for each video, so we silently ignore calls to mute the
// second audio track. Is this correct?
Track *t = getTrack(track);
if (t && t->getTrackType() == Track::kTrackTypeAudio) {
((AudioTrack *)t)->setMute(mute);
}
}
void NightlongVideoDecoder::setMute(bool mute) {
for (TrackList::iterator it = getTrackListBegin(); it != getTrackListEnd(); ++it) {
if ((*it)->getTrackType() == Track::kTrackTypeAudio)
((AudioTrack *)*it)->setMute(mute);
}
}
bool NightlongSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
if (!SmackerDecoder::loadStream(stream))
return false;
// Map audio tracks to sound types
for (uint32 i = 0; i < 8; i++) {
Track *t = getTrack(i);
if (t && t->getTrackType() == Track::kTrackTypeAudio) {
AudioTrack *audio = (AudioTrack *)t;
audio->setMute(false);
audio->setSoundType(i == 7 ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType);
}
}
return true;
}
bool NightlongSmackerDecoder::forceSeekToFrame(uint frame) {
const uint seekFrame = MAX<uint>(frame - 10, 0);
if (!isVideoLoaded())
return true;
if (seekFrame >= getFrameCount())
return false;
if (!rewind())
return false;
stopAudio();
SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0);
uint32 startPos = _fileStream->pos();
uint32 offset = 0;
for (uint32 i = 0; i < seekFrame; i++) {
videoTrack->increaseCurFrame();
// Frames with palette data contain palette entries which use
// the previous palette as their base. Therefore, we need to
// parse all palette entries up to the requested frame
if (_frameTypes[videoTrack->getCurFrame()] & 1) {
_fileStream->seek(startPos + offset, SEEK_SET);
videoTrack->unpackPalette(_fileStream);
}
offset += _frameSizes[i] & ~3;
}
if (!_fileStream->seek(startPos + offset, SEEK_SET))
return false;
while (getCurFrame() < (int)frame) {
decodeNextFrame();
}
_lastTimeChange = videoTrack->getFrameTime(frame);
_startTime = g_system->getMillis() - (_lastTimeChange.msecs() / getRate()).toInt();
startAudio();
return true;
}
// TODO: Background videos only loop smoothly like this,
// possibly an audio track bug?
bool NightlongSmackerDecoder::endOfFrames() const {
return getCurFrame() >= (int32)getFrameCount() - 1;
}
// ----------------------------------------------------------------------------
NightlongAmigaDecoder::AmigaVideoTrack::AmigaVideoTrack(Common::SeekableReadStream *stream) {
memset(_palette, 0, sizeof(_palette));
_curFrame = 0;
_frameCount = 10; // TODO: Anything > 1 to keep playing till the audio is done
// TODO: some videos have more than 256 entries
/*uint16 palEntries = stream->readUint16LE();
stream->skip(2); // unknown
for (uint16 i = 0; i < palEntries; i++) {
_palette[i * 3] = stream->readByte();
_palette[i * 3 + 1] = stream->readByte();
_palette[i * 3 + 2] = stream->readByte();
stream->skip(1); // unused alpha channel
}*/
delete stream;
}
uint16 NightlongAmigaDecoder::AmigaVideoTrack::getWidth() const {
// TODO
return 0;
}
uint16 NightlongAmigaDecoder::AmigaVideoTrack::getHeight() const {
// TODO
return 0;
}
Graphics::PixelFormat NightlongAmigaDecoder::AmigaVideoTrack::getPixelFormat() const {
// TODO
return g_system->getScreenFormat();
}
uint32 NightlongAmigaDecoder::AmigaVideoTrack::getNextFrameStartTime() const {
// TODO
return 0;
}
const Graphics::Surface *NightlongAmigaDecoder::AmigaVideoTrack::decodeNextFrame() {
// TODO
return nullptr;
}
NightlongAmigaDecoder::AmigaAudioTrack::AmigaAudioTrack(Common::SeekableReadStream *stream) :
AudioTrack(Audio::Mixer::SoundType::kSFXSoundType) {
_audioStream = Audio::makeRawStream(stream, 11025, 0, DisposeAfterUse::YES);
}
void NightlongAmigaDecoder::readNextPacket() {
AmigaVideoTrack *videoTrack = (AmigaVideoTrack *)getTrack(0);
if (videoTrack->endOfTrack())
return;
// TODO
}
bool NightlongAmigaDecoder::loadStream(Common::SeekableReadStream *stream) {
addTrack(new AmigaVideoTrack(stream));
return true;
}
bool NightlongAmigaDecoder::addAudioSideTrack(const Common::Path &path) {
Common::File *file = new Common::File();
if (!file->open(path)) {
delete file;
return false;
}
addTrack(new AmigaAudioTrack(file));
return true;
}
bool NightlongAmigaDecoder::forceSeekToFrame(uint frame) {
// TODO
return false;
}
const Common::Rect *NightlongAmigaDecoder::getNextDirtyRect() {
// TODO
return &_lastDirtyRect;
}
bool NightlongAmigaDecoder::endOfFrames() const {
//return getCurFrame() >= (int32)getFrameCount() - 1;
return true;
}
} // namespace Trecision

93
engines/trecision/video.h Normal file
View File

@@ -0,0 +1,93 @@
/* 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 TRECISION_VIDEO_H
#define TRECISION_VIDEO_H
#include "common/stream.h"
#include "common/serializer.h"
#include "video/smk_decoder.h"
#include "trecision/struct.h"
namespace Trecision {
class TrecisionEngine;
class NightlongVideoDecoder : public Video::SmackerDecoder {
public:
void muteTrack(uint track, bool mute);
void setMute(bool mute);
virtual bool forceSeekToFrame(uint frame) { return false; }
virtual bool endOfFrames() const { return false; }
};
class NightlongSmackerDecoder : public NightlongVideoDecoder {
public:
bool loadStream(Common::SeekableReadStream *stream) override;
bool forceSeekToFrame(uint frame) override;
bool endOfFrames() const override;
};
class NightlongAmigaDecoder : public NightlongVideoDecoder {
public:
bool loadStream(Common::SeekableReadStream *stream) override;
bool addAudioSideTrack(const Common::Path &path);
bool forceSeekToFrame(uint frame) override;
bool endOfFrames() const override;
const Common::Rect *getNextDirtyRect() override;
private:
Common::Rect _lastDirtyRect;
void readNextPacket() override;
class AmigaVideoTrack : public VideoTrack {
public:
AmigaVideoTrack(Common::SeekableReadStream *stream);
private:
byte _palette[3 * 256];
int _curFrame;
uint32 _frameCount;
uint16 getWidth() const override;
uint16 getHeight() const override;
Graphics::PixelFormat getPixelFormat() const override;
int getCurFrame() const override { return _curFrame; }
uint32 getNextFrameStartTime() const override;
const Graphics::Surface *decodeNextFrame() override;
int getFrameCount() const override { return _frameCount; }
const byte *getPalette() const override { return _palette; }
bool hasDirtyPalette() const override { return true; }
};
class AmigaAudioTrack : public AudioTrack {
public:
AmigaAudioTrack(Common::SeekableReadStream *stream);
private:
Audio::AudioStream *getAudioStream() const override { return _audioStream; }
Audio::AudioStream *_audioStream;
};
};
} // End of namespace Trecision
#endif