Initial commit
This commit is contained in:
1
engines/cge2/POTFILES
Normal file
1
engines/cge2/POTFILES
Normal file
@@ -0,0 +1 @@
|
||||
engines/cge2/metaengine.cpp
|
||||
461
engines/cge2/bitmap.cpp
Normal file
461
engines/cge2/bitmap.cpp
Normal file
@@ -0,0 +1,461 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/bitmap.h"
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/vga13h.h"
|
||||
#include "cge2/talk.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/debug-channels.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
Bitmap::Bitmap() : _w(0), _h(0), _v(nullptr), _b(nullptr), _map(0), _vm(nullptr) {
|
||||
}
|
||||
|
||||
void Bitmap::setVM(CGE2Engine *vm) {
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(CGE2Engine *vm, const char *fname) : _w(0), _h(0), _v(nullptr), _b(nullptr), _map(0), _vm(vm) {
|
||||
Common::String path;
|
||||
|
||||
if (!strcmp(fname, "04tal201")) {
|
||||
path = "04tal202";
|
||||
warning("Workaround for missing VBM: 04tal201");
|
||||
} else if (!strcmp(fname, "11oqlist-")) {
|
||||
path = "11oqlist";
|
||||
warning("Workaround for wrong VBM name: 11oqlist-");
|
||||
} else
|
||||
path = fname;
|
||||
|
||||
path = setExtension(path, ".VBM");
|
||||
|
||||
if (_vm->_resman->exist(path.c_str())) {
|
||||
EncryptedStream file(_vm->_resman, path.c_str());
|
||||
if (file.err())
|
||||
error("Unable to find VBM [%s]", fname);
|
||||
if (!loadVBM(&file))
|
||||
error("Bad VBM [%s]", fname);
|
||||
} else {
|
||||
warning("Missing VBM [%s]", path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 *map) : _w(w), _h(h), _v(nullptr), _map(0), _b(nullptr), _vm(vm) {
|
||||
if (map)
|
||||
code(map);
|
||||
}
|
||||
|
||||
// following routine creates filled rectangle
|
||||
// immediately as VGA video chunks, in near memory as fast as possible,
|
||||
// especially for text line real time display
|
||||
Bitmap::Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 fill)
|
||||
: _w((w + 3) & ~3), // only full uint32 allowed!
|
||||
_h(h), _map(0), _b(nullptr), _vm(vm) {
|
||||
|
||||
uint16 dsiz = _w >> 2; // data size (1 plane line size)
|
||||
uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap
|
||||
uint16 psiz = _h * lsiz; // - last gape, but + plane trailer
|
||||
uint8 *v = new uint8[4 * psiz + _h * sizeof(*_b)];// the same for 4 planes
|
||||
// + room for wash table
|
||||
|
||||
WRITE_LE_UINT16(v, (kBmpCPY | dsiz)); // data chunk hader
|
||||
memset(v + 2, fill, dsiz); // data bytes
|
||||
WRITE_LE_UINT16(v + lsiz - 2, (kBmpSKP | ((kScrWidth / 4) - dsiz))); // gap
|
||||
|
||||
// Replicate lines
|
||||
byte *destP;
|
||||
for (destP = v + lsiz; destP < (v + psiz); destP += lsiz)
|
||||
Common::copy(v, v + lsiz, destP);
|
||||
|
||||
WRITE_LE_UINT16(v + psiz - 2, kBmpEOI); // plane trailer uint16
|
||||
|
||||
// Replicate planes
|
||||
for (destP = v + psiz; destP < (v + 4 * psiz); destP += psiz)
|
||||
Common::copy(v, v + psiz, destP);
|
||||
|
||||
HideDesc *b = (HideDesc *)(v + 4 * psiz);
|
||||
b->_skip = (kScrWidth - _w) >> 2;
|
||||
b->_hide = _w >> 2;
|
||||
|
||||
// Replicate across the entire table
|
||||
for (HideDesc *hdP = b + 1; hdP < (b + _h); hdP++)
|
||||
*hdP = *b;
|
||||
|
||||
b->_skip = 0; // fix the first entry
|
||||
_v = v;
|
||||
_b = b;
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(CGE2Engine *vm, const Bitmap &bmp) : _w(bmp._w), _h(bmp._h), _v(nullptr), _map(0), _b(nullptr), _vm(vm) {
|
||||
uint8 *v0 = bmp._v;
|
||||
if (!v0)
|
||||
return;
|
||||
|
||||
uint16 vsiz = (uint8 *)(bmp._b) - (uint8 *)(v0);
|
||||
uint16 siz = vsiz + _h * sizeof(HideDesc);
|
||||
uint8 *v1 = new uint8[siz];
|
||||
memcpy(v1, v0, siz);
|
||||
_b = (HideDesc *)((_v = v1) + vsiz);
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap() {
|
||||
release();
|
||||
}
|
||||
|
||||
void Bitmap::release() {
|
||||
if (_v != nullptr)
|
||||
delete[] _v;
|
||||
_v = nullptr;
|
||||
}
|
||||
|
||||
Bitmap &Bitmap::operator=(const Bitmap &bmp) {
|
||||
if (this == &bmp)
|
||||
return *this;
|
||||
|
||||
uint8 *v0 = bmp._v;
|
||||
_w = bmp._w;
|
||||
_h = bmp._h;
|
||||
_map = 0;
|
||||
_vm = bmp._vm;
|
||||
delete[] _v;
|
||||
_v = nullptr;
|
||||
|
||||
if (v0) {
|
||||
uint16 vsiz = (uint8 *)bmp._b - (uint8 *)v0;
|
||||
uint16 siz = vsiz + _h * sizeof(HideDesc);
|
||||
uint8 *v1 = new uint8[siz];
|
||||
memcpy(v1, v0, siz);
|
||||
_b = (HideDesc *)((_v = v1) + vsiz);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Blatant rip from hopkins engine where it's ripped from gob engine. Hi DrMcCoy, hi Strangerke! ;>
|
||||
Common::String Bitmap::setExtension(const Common::String &str, const Common::String &ext) {
|
||||
if (str.empty())
|
||||
return str;
|
||||
|
||||
const char *dot = strrchr(str.c_str(), '.');
|
||||
if (dot)
|
||||
return Common::String(str.c_str(), dot - str.c_str()) + ext;
|
||||
|
||||
return str + ext;
|
||||
}
|
||||
|
||||
BitmapPtr Bitmap::code(uint8 *map) {
|
||||
if (!map)
|
||||
return nullptr;
|
||||
|
||||
uint16 cnt;
|
||||
|
||||
if (_v) { // old X-map exists, so remove it
|
||||
delete[] _v;
|
||||
_v = nullptr;
|
||||
}
|
||||
|
||||
while (true) { // at most 2 times: for (V == NULL) & for allocated block;
|
||||
uint8 *im = _v + 2;
|
||||
uint16 *cp = (uint16 *) _v;
|
||||
|
||||
if (_v) { // 2nd pass - fill the hide table
|
||||
for (uint i = 0; i < _h; i++) {
|
||||
_b[i]._skip = 0xFFFF;
|
||||
_b[i]._hide = 0x0000;
|
||||
}
|
||||
}
|
||||
for (int bpl = 0; bpl < 4; bpl++) { // once per each bitplane
|
||||
uint8 *bm = map;
|
||||
bool skip = (bm[bpl] == kPixelTransp);
|
||||
uint16 j;
|
||||
|
||||
cnt = 0;
|
||||
for (uint i = 0; i < _h; i++) { // once per each line
|
||||
uint8 pix;
|
||||
for (j = bpl; j < _w; j += 4) {
|
||||
pix = bm[j];
|
||||
if (_v && (pix != kPixelTransp)) {
|
||||
if (j < _b[i]._skip)
|
||||
_b[i]._skip = j;
|
||||
|
||||
if (j >= _b[i]._hide)
|
||||
_b[i]._hide = j + 1;
|
||||
}
|
||||
if (((pix == kPixelTransp) != skip) || (cnt >= 0x3FF0)) { // end of block
|
||||
cnt |= (skip) ? kBmpSKP : kBmpCPY;
|
||||
if (_v)
|
||||
WRITE_LE_UINT16(cp, cnt); // store block description uint16
|
||||
|
||||
cp = (uint16 *) im;
|
||||
im += 2;
|
||||
skip = (pix == kPixelTransp);
|
||||
cnt = 0;
|
||||
}
|
||||
if (!skip) {
|
||||
if (_v)
|
||||
*im = pix;
|
||||
im++;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
|
||||
bm += _w;
|
||||
if (_w < kScrWidth) {
|
||||
if (skip)
|
||||
cnt += (kScrWidth - j + 3) / 4;
|
||||
else {
|
||||
cnt |= kBmpCPY;
|
||||
if (_v)
|
||||
WRITE_LE_UINT16(cp, cnt);
|
||||
|
||||
cp = (uint16 *) im;
|
||||
im += 2;
|
||||
skip = true;
|
||||
cnt = (kScrWidth - j + 3) / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cnt && ! skip) {
|
||||
cnt |= kBmpCPY;
|
||||
if (_v)
|
||||
WRITE_LE_UINT16(cp, cnt);
|
||||
|
||||
cp = (uint16 *) im;
|
||||
im += 2;
|
||||
}
|
||||
if (_v)
|
||||
WRITE_LE_UINT16(cp, kBmpEOI);
|
||||
cp = (uint16 *) im;
|
||||
im += 2;
|
||||
}
|
||||
if (_v)
|
||||
break;
|
||||
|
||||
uint16 sizV = (uint16)(im - 2 - _v);
|
||||
_v = new uint8[sizV + _h * sizeof(*_b)];
|
||||
_b = (HideDesc *)(_v + sizV);
|
||||
}
|
||||
cnt = 0;
|
||||
for (uint i = 0; i < _h; i++) {
|
||||
if (_b[i]._skip == 0xFFFF) { // whole line is skipped
|
||||
_b[i]._skip = (cnt + kScrWidth) >> 2;
|
||||
cnt = 0;
|
||||
} else {
|
||||
uint16 s = _b[i]._skip & ~3;
|
||||
uint16 h = (_b[i]._hide + 3) & ~3;
|
||||
_b[i]._skip = (cnt + s) >> 2;
|
||||
_b[i]._hide = (h - s) >> 2;
|
||||
cnt = kScrWidth - h;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Bitmap::solidAt(V2D pos) {
|
||||
pos.x += _w >> 1;
|
||||
pos.y = _h - pos.y;
|
||||
|
||||
if (!pos.limited(V2D(_vm, _w, _h)))
|
||||
return false;
|
||||
|
||||
uint8 *m = _v;
|
||||
uint16 r = static_cast<uint16>(pos.x) % 4;
|
||||
uint16 n0 = (kScrWidth * pos.y + pos.x) / 4;
|
||||
uint16 n = 0;
|
||||
|
||||
while (r) {
|
||||
uint16 w, t;
|
||||
|
||||
w = READ_LE_UINT16(m);
|
||||
m += 2;
|
||||
t = w & 0xC000;
|
||||
w &= 0x3FFF;
|
||||
|
||||
switch (t) {
|
||||
case kBmpEOI:
|
||||
r--;
|
||||
// fall through
|
||||
case kBmpSKP:
|
||||
w = 0;
|
||||
break;
|
||||
case kBmpREP:
|
||||
w = 1;
|
||||
break;
|
||||
case kBmpCPY:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m += w;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
uint16 w, t;
|
||||
|
||||
w = READ_LE_UINT16(m);
|
||||
m += 2;
|
||||
t = w & 0xC000;
|
||||
w &= 0x3FFF;
|
||||
|
||||
if (n > n0)
|
||||
return false;
|
||||
|
||||
n += w;
|
||||
switch (t) {
|
||||
default:
|
||||
case kBmpEOI:
|
||||
return false;
|
||||
case kBmpSKP:
|
||||
w = 0;
|
||||
break;
|
||||
case kBmpREP:
|
||||
case kBmpCPY:
|
||||
if ((n - w <= n0) && (n > n0))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
m += ((t == kBmpREP) ? 1 : w);
|
||||
}
|
||||
}
|
||||
|
||||
bool Bitmap::loadVBM(EncryptedStream *f) {
|
||||
uint16 p = 0, n = 0;
|
||||
if (!f->err())
|
||||
f->read((uint8 *)&p, sizeof(p));
|
||||
p = FROM_LE_16(p);
|
||||
|
||||
if (!f->err())
|
||||
f->read((uint8 *)&n, sizeof(n));
|
||||
n = FROM_LE_16(n);
|
||||
|
||||
if (!f->err())
|
||||
f->read((uint8 *)&_w, sizeof(_w));
|
||||
_w = FROM_LE_16(_w);
|
||||
|
||||
if (!f->err())
|
||||
f->read((uint8 *)&_h, sizeof(_h));
|
||||
_h = FROM_LE_16(_h);
|
||||
|
||||
if (!f->err()) {
|
||||
if (p) {
|
||||
if (_vm->_bitmapPalette) {
|
||||
// Read in the palette
|
||||
byte palData[kPalSize];
|
||||
f->read(palData, kPalSize);
|
||||
|
||||
const byte *srcP = palData;
|
||||
for (int idx = 0; idx < kPalCount; idx++, srcP += 3) {
|
||||
_vm->_bitmapPalette[idx]._r = *srcP;
|
||||
_vm->_bitmapPalette[idx]._g = *(srcP + 1);
|
||||
_vm->_bitmapPalette[idx]._b = *(srcP + 2);
|
||||
}
|
||||
} else
|
||||
f->seek(f->pos() + kPalSize);
|
||||
}
|
||||
}
|
||||
_v = new uint8[n];
|
||||
|
||||
if (!f->err())
|
||||
f->read(_v, n);
|
||||
|
||||
_b = (HideDesc *)(_v + n - _h * sizeof(HideDesc));
|
||||
return (!f->err());
|
||||
}
|
||||
|
||||
void Bitmap::xLatPos(V2D& p) {
|
||||
p.x -= (_w >> 1);
|
||||
p.y = kWorldHeight - p.y - _h;
|
||||
}
|
||||
|
||||
#define _ kPixelTransp,
|
||||
#define L 1,
|
||||
#define G 2,
|
||||
#define D 3,
|
||||
#define kDesignSize 240
|
||||
|
||||
uint8 *Bitmap::makeSpeechBubbleTail(int which, uint8 colorSet[][4]) {
|
||||
static const uint8 kSLDesign[kDesignSize] = {
|
||||
G G G G G G G G G _ _ _ _ _ _
|
||||
L G G G G G G G G D _ _ _ _ _
|
||||
_ L G G G G G G G D _ _ _ _ _
|
||||
_ _ L G G G G G G G D _ _ _ _
|
||||
_ _ _ L G G G G G G D _ _ _ _
|
||||
_ _ _ _ L G G G G G D _ _ _ _
|
||||
_ _ _ _ _ L G G G G G D _ _ _
|
||||
_ _ _ _ _ _ L G G G G D _ _ _
|
||||
_ _ _ _ _ _ _ L G G G D _ _ _
|
||||
_ _ _ _ _ _ _ _ L G G G D _ _
|
||||
_ _ _ _ _ _ _ _ _ L G G D _ _
|
||||
_ _ _ _ _ _ _ _ _ _ L G D _ _
|
||||
_ _ _ _ _ _ _ _ _ _ _ L G D _
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ L D _
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ L D
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ D
|
||||
};
|
||||
|
||||
static const uint8 kSRDesign[kDesignSize] = {
|
||||
_ _ _ _ _ _ G G G G G G G G G
|
||||
_ _ _ _ _ L G G G G G G G G D
|
||||
_ _ _ _ _ L G G G G G G G D _
|
||||
_ _ _ _ L G G G G G G G D _ _
|
||||
_ _ _ _ L G G G G G G D _ _ _
|
||||
_ _ _ _ L G G G G G D _ _ _ _
|
||||
_ _ _ L G G G G G D _ _ _ _ _
|
||||
_ _ _ L G G G G D _ _ _ _ _ _
|
||||
_ _ _ L G G G D _ _ _ _ _ _ _
|
||||
_ _ L G G G D _ _ _ _ _ _ _ _
|
||||
_ _ L G G D _ _ _ _ _ _ _ _ _
|
||||
_ _ L G D _ _ _ _ _ _ _ _ _ _
|
||||
_ L G D _ _ _ _ _ _ _ _ _ _ _
|
||||
_ L D _ _ _ _ _ _ _ _ _ _ _ _
|
||||
L D _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
D _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
};
|
||||
|
||||
uint8 *des = new uint8[kDesignSize];
|
||||
switch (which) {
|
||||
case 0:
|
||||
memcpy(des, kSLDesign, sizeof(kSLDesign));
|
||||
break;
|
||||
case 1:
|
||||
memcpy(des, kSRDesign, sizeof(kSRDesign));
|
||||
break;
|
||||
default:
|
||||
error("Wrong parameter in Bitmap::makeSpeechBubbleTail!");
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < kDesignSize; i++) {
|
||||
if ((des[i] >= 1) && (des[i] <= 3))
|
||||
des[i] = colorSet[kCBSay][des[i]];
|
||||
}
|
||||
|
||||
return des;
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
93
engines/cge2/bitmap.h
Normal file
93
engines/cge2/bitmap.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_BITMAP_H
|
||||
#define CGE2_BITMAP_H
|
||||
|
||||
#include "cge2/general.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class CGE2Engine;
|
||||
class EncryptedStream;
|
||||
class V2D;
|
||||
|
||||
#define kMaxPath 128
|
||||
|
||||
enum {
|
||||
kBmpEOI = 0x0000,
|
||||
kBmpSKP = 0x4000,
|
||||
kBmpREP = 0x8000,
|
||||
kBmpCPY = 0xC000
|
||||
};
|
||||
|
||||
#include "common/pack-start.h"
|
||||
|
||||
struct HideDesc {
|
||||
uint16 _skip;
|
||||
uint16 _hide;
|
||||
};
|
||||
|
||||
#include "common/pack-end.h"
|
||||
|
||||
class Bitmap {
|
||||
CGE2Engine *_vm;
|
||||
|
||||
Common::String setExtension(const Common::String &str, const Common::String &ext);
|
||||
bool loadVBM(EncryptedStream *f);
|
||||
public:
|
||||
uint16 _w;
|
||||
uint16 _h;
|
||||
uint8 *_v;
|
||||
int32 _map;
|
||||
HideDesc *_b;
|
||||
|
||||
Bitmap();
|
||||
Bitmap(CGE2Engine *vm, const char *fname);
|
||||
Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 *map);
|
||||
Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 fill);
|
||||
Bitmap(CGE2Engine *vm, const Bitmap &bmp);
|
||||
~Bitmap();
|
||||
|
||||
void setVM(CGE2Engine *vm);
|
||||
Bitmap *code(uint8 *map);
|
||||
Bitmap &operator=(const Bitmap &bmp);
|
||||
void release();
|
||||
void hide(V2D pos);
|
||||
void show(V2D pos);
|
||||
bool solidAt(V2D pos);
|
||||
void xLatPos(V2D &p);
|
||||
|
||||
static uint8 *makeSpeechBubbleTail(int des, uint8 colorSet[][4]);
|
||||
};
|
||||
|
||||
|
||||
typedef Bitmap *BitmapPtr;
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_BITMAP_H
|
||||
208
engines/cge2/cge2.cpp
Normal file
208
engines/cge2/cge2.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "engines/util.h"
|
||||
#include "common/text-to-speech.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/bitmap.h"
|
||||
#include "cge2/vga13h.h"
|
||||
#include "cge2/sound.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/hero.h"
|
||||
#include "cge2/general.h"
|
||||
#include "cge2/spare.h"
|
||||
#include "cge2/talk.h"
|
||||
#include "cge2/cge2_main.h"
|
||||
#include "cge2/map.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
CGE2Engine::CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription)
|
||||
: Engine(syst), _gameDescription(gameDescription), _randomSource("cge2") {
|
||||
|
||||
_resman = nullptr;
|
||||
_vga = nullptr;
|
||||
_midiPlayer = nullptr;
|
||||
_fx = nullptr;
|
||||
_sound = nullptr;
|
||||
_text = nullptr;
|
||||
for (int i = 0; i < 2; i++)
|
||||
_heroTab[i] = nullptr;
|
||||
_eye = nullptr;
|
||||
for (int i = 0; i < kSceneMax; i++)
|
||||
_eyeTab[i] = nullptr;
|
||||
_spare = nullptr;
|
||||
_commandHandler = nullptr;
|
||||
_commandHandlerTurbo = nullptr;
|
||||
_font = nullptr;
|
||||
_infoLine = nullptr;
|
||||
_mouse = nullptr;
|
||||
_keyboard = nullptr;
|
||||
_talk = nullptr;
|
||||
for (int i = 0; i < kMaxPoint; i++)
|
||||
_point[i] = nullptr;
|
||||
_sys = nullptr;
|
||||
_busyPtr = nullptr;
|
||||
for (int i = 0; i < 2; i++)
|
||||
_vol[i] = nullptr;
|
||||
_eventManager = nullptr;
|
||||
_map = nullptr;
|
||||
_quitFlag = false;
|
||||
_bitmapPalette = nullptr;
|
||||
_gamePhase = kPhaseIntro;
|
||||
_now = 1;
|
||||
_sex = 1;
|
||||
_mouseTop = kWorldHeight / 3;
|
||||
_dark = false;
|
||||
_lastFrame = 0;
|
||||
_lastTick = 0;
|
||||
_waitSeq = 0;
|
||||
_waitRef = 0;
|
||||
_soundStat._wait = nullptr;
|
||||
_soundStat._ref[0] = 0;
|
||||
_soundStat._ref[1] = 0;
|
||||
_taken = false;
|
||||
_endGame = false;
|
||||
_req = 1;
|
||||
_midiNotify = nullptr;
|
||||
_spriteNotify = nullptr;
|
||||
_startGameSlot = 0;
|
||||
|
||||
_sayCap = ConfMan.getBool("subtitles");
|
||||
_sayVox = !ConfMan.getBool("speech_mute");
|
||||
_muteAll = ConfMan.getBool("mute");
|
||||
if (_muteAll) {
|
||||
_oldMusicVolume = _oldSfxVolume = 0;
|
||||
_music = _sayVox = false;
|
||||
} else {
|
||||
_oldMusicVolume = ConfMan.getInt("music_volume");
|
||||
_oldSfxVolume = ConfMan.getInt("sfx_volume");
|
||||
_music = _oldMusicVolume != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::init() {
|
||||
// Create debugger console
|
||||
setDebugger(new CGE2Console(this));
|
||||
_resman = new ResourceManager();
|
||||
_vga = new Vga(this);
|
||||
_fx = new Fx(this, 16);
|
||||
_sound = new Sound(this);
|
||||
_midiPlayer = new MusicPlayer(this);
|
||||
_text = new Text(this, "CGE");
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
_heroTab[i] = new HeroTab(this);
|
||||
|
||||
_eye = new V3D();
|
||||
for (int i = 0; i < kSceneMax; i++)
|
||||
_eyeTab[i] = new V3D();
|
||||
|
||||
_spare = new Spare(this);
|
||||
_commandHandler = new CommandHandler(this, false);
|
||||
_commandHandlerTurbo = new CommandHandler(this, true);
|
||||
_font = new Font(this);
|
||||
_infoLine = new InfoLine(this, kInfoW);
|
||||
_mouse = new Mouse(this);
|
||||
_keyboard = new Keyboard(this);
|
||||
|
||||
for (int i = 0; i < kMaxPoint; i++)
|
||||
_point[i] = new V3D();
|
||||
|
||||
_sys = new System(this);
|
||||
_eventManager = new EventManager(this);
|
||||
_map = new Map(this);
|
||||
_startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
|
||||
}
|
||||
|
||||
void CGE2Engine::deinit() {
|
||||
delete _spare;
|
||||
delete _resman;
|
||||
delete _vga;
|
||||
delete _fx;
|
||||
delete _sound;
|
||||
delete _midiPlayer;
|
||||
delete _text;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
delete _heroTab[i];
|
||||
|
||||
for (int i = 0; i < kSceneMax; i++)
|
||||
delete _eyeTab[i];
|
||||
|
||||
delete _eye;
|
||||
delete _commandHandler;
|
||||
delete _commandHandlerTurbo;
|
||||
delete _font;
|
||||
delete _infoLine;
|
||||
delete _mouse;
|
||||
delete _keyboard;
|
||||
|
||||
if (_talk != nullptr)
|
||||
delete _talk;
|
||||
|
||||
for (int i = 0; i < kMaxPoint; i++)
|
||||
delete _point[i];
|
||||
|
||||
delete _sys;
|
||||
delete _eventManager;
|
||||
delete _map;
|
||||
}
|
||||
|
||||
bool CGE2Engine::hasFeature(EngineFeature f) const {
|
||||
return (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime)
|
||||
|| (f == kSupportsReturnToLauncher);
|
||||
}
|
||||
|
||||
Common::Error CGE2Engine::run() {
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr) {
|
||||
ttsMan->setLanguage(Common::getLanguageCode(getLanguage()));
|
||||
ttsMan->enable(ConfMan.getBool("tts_enabled_speech") || ConfMan.getBool("tts_enabled_objects"));
|
||||
}
|
||||
syncSoundSettings();
|
||||
initGraphics(kScrWidth, kScrHeight);
|
||||
|
||||
init();
|
||||
cge2_main();
|
||||
deinit();
|
||||
|
||||
ConfMan.setBool("subtitles", _sayCap);
|
||||
ConfMan.setBool("speech_mute", !_sayVox);
|
||||
ConfMan.flushToDisk();
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::Language CGE2Engine::getLanguage() const {
|
||||
return _gameDescription->language;
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
343
engines/cge2/cge2.h
Normal file
343
engines/cge2/cge2.h
Normal file
@@ -0,0 +1,343 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_CGE2_H
|
||||
#define CGE2_CGE2_H
|
||||
|
||||
#include "common/random.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/serializer.h"
|
||||
#include "engines/engine.h"
|
||||
#include "common/system.h"
|
||||
#include "cge2/fileio.h"
|
||||
#include "cge2/console.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
struct ADGameDescription;
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class Vga;
|
||||
class Sprite;
|
||||
class MusicPlayer;
|
||||
class Fx;
|
||||
class Sound;
|
||||
class Text;
|
||||
struct HeroTab;
|
||||
class FXP;
|
||||
class V3D;
|
||||
class V2D;
|
||||
struct Dac;
|
||||
class Spare;
|
||||
class CommandHandler;
|
||||
class InfoLine;
|
||||
class Mouse;
|
||||
class Keyboard;
|
||||
class Talk;
|
||||
class Hero;
|
||||
class Bitmap;
|
||||
class System;
|
||||
class EventManager;
|
||||
class Font;
|
||||
class Map;
|
||||
struct SavegameHeader;
|
||||
|
||||
#define kScrWidth 320
|
||||
#define kScrHeight 240
|
||||
#define kScrDepth 480
|
||||
#define kPanHeight 40
|
||||
#define kWorldHeight (kScrHeight - kPanHeight)
|
||||
#define kMaxFile 128
|
||||
#define kPathMax 128
|
||||
#define kDimMax 8
|
||||
#define kWayMax 10
|
||||
#define kPocketMax 4
|
||||
#define kSceneMax 100
|
||||
#define kMaxPoint 4
|
||||
#define kInfoX 160
|
||||
#define kInfoY -11
|
||||
#define kInfoW 180
|
||||
#define kPocketsWidth 59
|
||||
#define kLineMax 512
|
||||
|
||||
#define kIntroExt ".I80"
|
||||
#define kTabName "CGE.TAB"
|
||||
#define kPocketFull 170
|
||||
#define kGameFrameDelay (750 / 50)
|
||||
#define kGameTickDelay (750 / 62)
|
||||
|
||||
#define kMusicRef 122
|
||||
#define kPowerRef 123
|
||||
#define kDvolRef 124
|
||||
#define kMvolRef 125
|
||||
#define kBusyRef 127
|
||||
|
||||
#define kOffUseCount 130
|
||||
#define kOffUseText 131
|
||||
|
||||
#define kSysTimeRate 6 // 12 Hz
|
||||
#define kBlinkRate 4 // 3 Hz
|
||||
|
||||
#define kQuitTitle 200
|
||||
#define kQuitText 201
|
||||
#define kNoQuitText 202
|
||||
|
||||
#define kSavegameVersion 2
|
||||
#define kSavegameStrSize 12
|
||||
#define kSavegameStr "SCUMMVM_CGE2"
|
||||
|
||||
#define kColorNum 6
|
||||
|
||||
enum CGEAction {
|
||||
kActionNone,
|
||||
kActionInfo,
|
||||
kActionEscape,
|
||||
kActionSave,
|
||||
kActionLoad,
|
||||
kActionQuit
|
||||
};
|
||||
|
||||
struct SavegameHeader {
|
||||
uint8 version;
|
||||
Common::String saveName;
|
||||
Graphics::Surface *thumbnail;
|
||||
int16 saveYear, saveMonth, saveDay;
|
||||
int16 saveHour, saveMinutes;
|
||||
uint32 playTime;
|
||||
};
|
||||
|
||||
enum ColorBank { kCBRel, kCBStd, kCBSay, kCBInf, kCBMnu, kCBWar };
|
||||
|
||||
enum GamePhase { kPhaseInGame, kPhaseIntro, kPhaseOver };
|
||||
|
||||
// our engine debug channels
|
||||
enum {
|
||||
kCGE2DebugOpcode = 1,
|
||||
};
|
||||
|
||||
enum CallbackType {
|
||||
kNullCB = 0, kQGame, kXScene
|
||||
};
|
||||
|
||||
enum Action { kNear, kMTake, kFTake, kActions };
|
||||
|
||||
typedef void (CGE2Engine::*NotifyFunctionType)();
|
||||
|
||||
class CGE2Engine : public Engine {
|
||||
private:
|
||||
uint32 _lastFrame, _lastTick;
|
||||
void tick();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header);
|
||||
void saveGame(int slotNumber, const Common::String &desc);
|
||||
bool loadGame(int slotNumber);
|
||||
void syncHeader(Common::Serializer &s);
|
||||
void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream);
|
||||
void resetGame();
|
||||
public:
|
||||
CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription);
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
Common::Language getLanguage() const;
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
Common::Error loadGameState(int slot) override;
|
||||
Common::Error run() override;
|
||||
|
||||
WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail = true);
|
||||
|
||||
bool showTitle(const char *name);
|
||||
void cge2_main();
|
||||
char *mergeExt(char *buf, const char *name, const char *ext);
|
||||
void inf(const char *text, ColorBank col = kCBInf);
|
||||
void movie(const char *ext);
|
||||
void runGame();
|
||||
void loadHeroes();
|
||||
void loadScript(const char *fname, bool onlyToolbar = false);
|
||||
Sprite *loadSprite(const char *fname, int ref, int scene, V3D &pos);
|
||||
void badLab(const char *fn);
|
||||
void sceneUp(int cav);
|
||||
void sceneDown();
|
||||
void closePocket();
|
||||
void switchScene(int scene);
|
||||
void storeHeroPos();
|
||||
void showBak(int ref);
|
||||
void loadTab();
|
||||
int newRandom(int range);
|
||||
void openPocket();
|
||||
void selectPocket(int n);
|
||||
void busy(bool on);
|
||||
void feedSnail(Sprite *spr, Action snq, Hero *hero);
|
||||
int freePockets(int sx);
|
||||
int findActivePocket(int ref);
|
||||
void pocFul();
|
||||
void killText();
|
||||
void mainLoop();
|
||||
void handleFrame();
|
||||
Sprite *locate(int ref);
|
||||
bool isHero(Sprite *spr);
|
||||
void loadUser();
|
||||
void loadPos();
|
||||
void releasePocket(Sprite *spr);
|
||||
void switchHero(int sex);
|
||||
void offUse();
|
||||
void setAutoColors();
|
||||
bool cross(const V2D &a, const V2D &b, const V2D &c, const V2D &d);
|
||||
bool contain(const V2D &a, const V2D &b, const V2D &p);
|
||||
long det(const V2D &a, const V2D &b, const V2D &c);
|
||||
int sgn(long n);
|
||||
int mapCross(const V2D &a, const V2D &b);
|
||||
Sprite *spriteAt(V2D pos);
|
||||
void keyClick();
|
||||
void swapInPocket(Sprite *spr, Sprite *xspr);
|
||||
void busyStep();
|
||||
|
||||
void optionTouch(int opt, uint16 mask);
|
||||
void switchColorMode();
|
||||
void switchMusic();
|
||||
void quit();
|
||||
void setVolume(int idx, int cnt);
|
||||
void checkVolumeSwitches();
|
||||
void switchCap();
|
||||
void switchVox();
|
||||
void switchSay();
|
||||
void initToolbar();
|
||||
void initVolumeSwitch(Sprite *volSwitch, int val);
|
||||
void checkMute();
|
||||
|
||||
void checkSounds();
|
||||
|
||||
void setEye(const V3D &e);
|
||||
void setEye(const V2D& e2, int z = -kScrWidth);
|
||||
void setEye(const char *s);
|
||||
|
||||
int number(char *s);
|
||||
char *token(char *s);
|
||||
char *tail(char *s);
|
||||
int takeEnum(const char *const *tab, const char *text);
|
||||
ID ident(const char *s);
|
||||
bool testBool(char *s);
|
||||
|
||||
void snKill(Sprite *spr);
|
||||
void snHide(Sprite *spr, int val);
|
||||
void snMidi(int val);
|
||||
void snSeq(Sprite *spr, int val);
|
||||
void snRSeq(Sprite *spr, int val);
|
||||
void snSend(Sprite *spr, int val);
|
||||
void snSwap(Sprite *spr, int val);
|
||||
void snCover(Sprite *spr, int val);
|
||||
void snUncover(Sprite *spr, Sprite *spr2);
|
||||
void snKeep(Sprite *spr, int val);
|
||||
void snGive(Sprite *spr, int val);
|
||||
void snGoto(Sprite *spr, int val);
|
||||
void snPort(Sprite *spr, int port);
|
||||
void snMouse(bool on);
|
||||
void snNNext(Sprite *spr, Action act, int val);
|
||||
void snRNNext(Sprite *spr, int val);
|
||||
void snRMTNext(Sprite *spr, int val);
|
||||
void snRFTNext(Sprite *spr, int val);
|
||||
void snRmNear(Sprite *spr);
|
||||
void snRmMTake(Sprite *spr);
|
||||
void snRmFTake(Sprite *spr);
|
||||
void snSetRef(Sprite *spr, int val);
|
||||
void snFlash(bool on);
|
||||
void snCycle(int cnt);
|
||||
void snWalk(Sprite *spr, int val);
|
||||
void snReach(Sprite *spr, int val);
|
||||
void snSound(Sprite *spr, int wav, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
|
||||
void snRoom(Sprite *spr, bool on);
|
||||
void snGhost(Bitmap *bmp);
|
||||
void snSay(Sprite *spr, int val);
|
||||
|
||||
void hide1(Sprite *spr);
|
||||
Sprite *expandSprite(Sprite *spr);
|
||||
void qGame();
|
||||
void xScene();
|
||||
|
||||
const ADGameDescription *_gameDescription;
|
||||
|
||||
Common::RandomSource _randomSource;
|
||||
|
||||
bool _quitFlag;
|
||||
Dac *_bitmapPalette;
|
||||
GamePhase _gamePhase; // Original name: startupmode
|
||||
int _now;
|
||||
int _sex;
|
||||
int _mouseTop;
|
||||
bool _dark;
|
||||
int _waitSeq;
|
||||
int _waitRef;
|
||||
|
||||
struct {
|
||||
int *_wait;
|
||||
int _ref[2];
|
||||
} _soundStat;
|
||||
|
||||
bool _taken;
|
||||
bool _endGame;
|
||||
int _req;
|
||||
NotifyFunctionType _midiNotify;
|
||||
NotifyFunctionType _spriteNotify;
|
||||
int _startGameSlot;
|
||||
|
||||
bool _sayCap;
|
||||
bool _sayVox;
|
||||
int _oldMusicVolume;
|
||||
int _oldSfxVolume;
|
||||
bool _music;
|
||||
bool _muteAll;
|
||||
|
||||
ResourceManager *_resman;
|
||||
Vga *_vga;
|
||||
MusicPlayer *_midiPlayer;
|
||||
Fx *_fx;
|
||||
Sound *_sound;
|
||||
Text *_text;
|
||||
HeroTab *_heroTab[2];
|
||||
V3D *_eye;
|
||||
V3D *_eyeTab[kSceneMax];
|
||||
Spare *_spare;
|
||||
CommandHandler *_commandHandler;
|
||||
CommandHandler *_commandHandlerTurbo;
|
||||
Font *_font;
|
||||
InfoLine *_infoLine;
|
||||
Mouse *_mouse;
|
||||
Keyboard *_keyboard;
|
||||
Talk *_talk;
|
||||
V3D *_point[kMaxPoint];
|
||||
System *_sys;
|
||||
Sprite *_busyPtr;
|
||||
Sprite *_vol[2];
|
||||
EventManager *_eventManager;
|
||||
Map *_map;
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_CGE2_H
|
||||
956
engines/cge2/cge2_main.cpp
Normal file
956
engines/cge2/cge2_main.cpp
Normal file
@@ -0,0 +1,956 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "sound.h"
|
||||
#include "cge2/cge2_main.h"
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/vga13h.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/snail.h"
|
||||
#include "cge2/hero.h"
|
||||
#include "cge2/spare.h"
|
||||
#include "cge2/map.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
System::System(CGE2Engine *vm) : Sprite(vm), _vm(vm) {
|
||||
_blinkCounter = 0;
|
||||
_blinkSprite = nullptr;
|
||||
tick();
|
||||
}
|
||||
|
||||
void System::touch(uint16 mask, V2D pos) {
|
||||
if (mask & kEventEsc) {
|
||||
// The original was calling keyClick()
|
||||
// The sound is uselessly annoying and noisy, so it has been removed
|
||||
_vm->killText();
|
||||
if (_vm->_gamePhase == kPhaseIntro) {
|
||||
_vm->_commandHandler->addCommand(kCmdClear, -1, 0, nullptr);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (_vm->_gamePhase != kPhaseInGame)
|
||||
return;
|
||||
_vm->_infoLine->setText(nullptr);
|
||||
|
||||
if (mask & kMouseLeftUp) {
|
||||
if (pos.y >= 0) { // world
|
||||
if (!_vm->_talk && pos.y < _vm->_mouseTop)
|
||||
_vm->_heroTab[_vm->_sex]->_ptr->walkTo(pos);
|
||||
} else { // panel
|
||||
if (_vm->_commandHandler->idle()) {
|
||||
int sex = pos.x < kPocketsWidth;
|
||||
if (sex || pos.x >= kScrWidth - kPocketsWidth) {
|
||||
_vm->switchHero(sex);
|
||||
if (_vm->_sex == sex) {
|
||||
int dx = kPocketsWidth >> 1,
|
||||
dy = 1 - (kPanHeight >> 1);
|
||||
Sprite *s;
|
||||
if (!sex)
|
||||
pos.x -= kScrWidth - kPocketsWidth;
|
||||
dx -= pos.x;
|
||||
dy -= pos.y;
|
||||
if (dx * dx + dy * dy > 10 * 10) {
|
||||
int n = 0;
|
||||
if (1 - pos.y >= (kPanHeight >> 1))
|
||||
n += 2;
|
||||
if (pos.x >= (kPocketsWidth >> 1))
|
||||
++n;
|
||||
s = _vm->_heroTab[_vm->_sex]->_pocket[n];
|
||||
if (_vm->_sys->_blinkSprite)
|
||||
_vm->_sys->_blinkSprite->_flags._hide = false;
|
||||
if (_vm->_sys->_blinkSprite == s)
|
||||
_vm->_sys->_blinkSprite = nullptr;
|
||||
else
|
||||
_vm->_sys->_blinkSprite = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void System::tick() {
|
||||
_time = kSysTimeRate;
|
||||
|
||||
if (_blinkCounter)
|
||||
--_blinkCounter;
|
||||
else {
|
||||
if (_blinkSprite)
|
||||
_blinkSprite->_flags._hide ^= 1;
|
||||
_blinkCounter = kBlinkRate;
|
||||
}
|
||||
}
|
||||
|
||||
int CGE2Engine::number(char *str) {
|
||||
char *s = token(str);
|
||||
if (s == nullptr)
|
||||
error("Wrong input for CGE2Engine::number()");
|
||||
int r = atoi(s);
|
||||
char *pp = strchr(s, ':');
|
||||
if (pp)
|
||||
r = (r << 8) + atoi(pp + 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
char *CGE2Engine::token(char *s) {
|
||||
return strtok(s, " =\t,;/()");
|
||||
}
|
||||
|
||||
char *CGE2Engine::tail(char *s) {
|
||||
if (s && (*s == '='))
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
int CGE2Engine::takeEnum(const char *const *tab, const char *text) {
|
||||
if (text) {
|
||||
for (const char *const *e = tab; *e; e++) {
|
||||
if (scumm_stricmp(text, *e) == 0)
|
||||
return e - tab;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ID CGE2Engine::ident(const char *s) {
|
||||
return ID(takeEnum(EncryptedStream::kIdTab, s));
|
||||
}
|
||||
|
||||
bool CGE2Engine::testBool(char *s) {
|
||||
return number(s) != 0;
|
||||
}
|
||||
|
||||
void CGE2Engine::badLab(const char *fn) {
|
||||
error("Misplaced label in %s!", fn);
|
||||
}
|
||||
|
||||
Sprite *CGE2Engine::loadSprite(const char *fname, int ref, int scene, V3D &pos) {
|
||||
int shpcnt = 0;
|
||||
int seqcnt = 0;
|
||||
int cnt[kActions];
|
||||
|
||||
for (int i = 0; i < kActions; i++)
|
||||
cnt[i] = 0;
|
||||
|
||||
ID section = kIdPhase;
|
||||
bool frnt = true;
|
||||
bool east = false;
|
||||
bool port = false;
|
||||
bool tran = false;
|
||||
Hero *h;
|
||||
ID id;
|
||||
|
||||
char tmpStr[kLineMax + 1];
|
||||
static_assert(sizeof(tmpStr) >= kPathMax, "mergeExt expects kPathMax buffer");
|
||||
mergeExt(tmpStr, fname, kSprExt);
|
||||
|
||||
if (_resman->exist(tmpStr)) { // sprite description file exist
|
||||
EncryptedStream sprf(_resman, tmpStr);
|
||||
if (sprf.err())
|
||||
error("Bad SPR [%s]", tmpStr);
|
||||
|
||||
int label = kNoByte;
|
||||
Common::String line;
|
||||
|
||||
for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()){
|
||||
if (line.empty())
|
||||
continue;
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
|
||||
char *p = token(tmpStr);
|
||||
if (*p == '@') {
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
label = atoi(p + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
id = ident(p);
|
||||
switch (id) {
|
||||
case kIdName: // will be taken in Expand routine
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
break;
|
||||
case kIdType:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
break;
|
||||
case kIdNear:
|
||||
case kIdMTake:
|
||||
case kIdFTake:
|
||||
case kIdPhase:
|
||||
case kIdSeq:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
section = id;
|
||||
break;
|
||||
case kIdFront:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
p = token(nullptr);
|
||||
frnt = testBool(p);
|
||||
break;
|
||||
case kIdEast:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
p = token(nullptr);
|
||||
east = testBool(p);
|
||||
break;
|
||||
case kIdPortable:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
p = token(nullptr);
|
||||
port = testBool(p);
|
||||
break;
|
||||
case kIdTransparent:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
p = token(nullptr);
|
||||
tran = testBool(p);
|
||||
break;
|
||||
default:
|
||||
if (id >= kIdNear)
|
||||
break;
|
||||
switch (section) {
|
||||
case kIdNear:
|
||||
case kIdMTake:
|
||||
case kIdFTake:
|
||||
if (_commandHandler->getComId(p) >= 0)
|
||||
++cnt[section];
|
||||
else
|
||||
error("Bad line %d [%s]", sprf.getLineCount(), tmpStr);
|
||||
break;
|
||||
case kIdPhase:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
++shpcnt;
|
||||
break;
|
||||
case kIdSeq:
|
||||
if (label != kNoByte)
|
||||
badLab(fname);
|
||||
++seqcnt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
label = kNoByte;
|
||||
}
|
||||
|
||||
if (!shpcnt)
|
||||
error("No shapes - %s", fname);
|
||||
} else // No sprite description: mono-shaped sprite with only .BMP file.
|
||||
++shpcnt;
|
||||
|
||||
// Make sprite of chosen type:
|
||||
Sprite *sprite = nullptr;
|
||||
char c = *fname | 0x20;
|
||||
if (c >= 'a' && c <= 'z' && fname[1] == '0' && fname[2] == '\0') {
|
||||
h = new Hero(this);
|
||||
h->gotoxyz(pos);
|
||||
sprite = h;
|
||||
} else {
|
||||
sprite = new Sprite(this);
|
||||
sprite->gotoxyz(pos);
|
||||
}
|
||||
|
||||
if (sprite) {
|
||||
sprite->_ref = ref;
|
||||
sprite->_scene = scene;
|
||||
|
||||
sprite->_flags._frnt = frnt;
|
||||
sprite->_flags._east = east;
|
||||
sprite->_flags._port = port;
|
||||
sprite->_flags._tran = tran;
|
||||
sprite->_flags._kill = true;
|
||||
|
||||
// Extract the filename, without the extension
|
||||
Common::strlcpy(sprite->_file, fname, sizeof(sprite->_file));
|
||||
char *p = strchr(sprite->_file, '.');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
sprite->_shpCnt = shpcnt;
|
||||
sprite->_seqCnt = seqcnt;
|
||||
|
||||
for (int i = 0; i < kActions; i++)
|
||||
sprite->_actionCtrl[i]._cnt = cnt[i];
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
void CGE2Engine::loadScript(const char *fname, bool onlyToolbar) {
|
||||
EncryptedStream scrf(_resman, fname);
|
||||
|
||||
if (scrf.err())
|
||||
return;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
char tmpStr[kLineMax + 1];
|
||||
Common::String line;
|
||||
|
||||
for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) {
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
|
||||
ok = false; // not OK if break
|
||||
|
||||
V3D P;
|
||||
|
||||
// sprite ident number
|
||||
int SpI = number(tmpStr);
|
||||
|
||||
if (onlyToolbar && SpI >= 141)
|
||||
return;
|
||||
|
||||
// sprite file name
|
||||
char *SpN;
|
||||
if ((SpN = token(nullptr)) == nullptr)
|
||||
break;
|
||||
|
||||
// sprite scene
|
||||
int SpA = number(nullptr);
|
||||
|
||||
// sprite column
|
||||
P._x = number(nullptr);
|
||||
|
||||
// sprite row
|
||||
P._y = number(nullptr);
|
||||
|
||||
// sprite Z pos
|
||||
P._z = number(nullptr);
|
||||
|
||||
// sprite life
|
||||
bool BkG = number(nullptr) == 0;
|
||||
|
||||
ok = true; // no break: OK
|
||||
|
||||
Sprite *sprite = loadSprite(SpN, SpI, SpA, P);
|
||||
if (sprite) {
|
||||
if (BkG)
|
||||
sprite->_flags._back = true;
|
||||
|
||||
int n = _spare->count();
|
||||
if (_spare->locate(sprite->_ref) == nullptr)
|
||||
_spare->dispose(sprite);
|
||||
else
|
||||
delete sprite;
|
||||
|
||||
if (_spare->count() == n)
|
||||
error("Duplicate reference! %s", SpN);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
error("Bad INI line %d [%s]", scrf.getLineCount(), fname);
|
||||
}
|
||||
|
||||
void CGE2Engine::movie(const char *ext) {
|
||||
assert(ext);
|
||||
|
||||
if (_quitFlag)
|
||||
return;
|
||||
|
||||
char fn[12];
|
||||
snprintf(fn, 12, "CGE%s", ext);
|
||||
|
||||
if (_resman->exist(fn)) {
|
||||
int now = _now;
|
||||
_now = atoi(ext + 2);
|
||||
loadScript(fn);
|
||||
sceneUp(_now);
|
||||
_keyboard->setClient(_sys);
|
||||
|
||||
while (!_commandHandler->idle() && !_quitFlag)
|
||||
mainLoop();
|
||||
|
||||
_keyboard->setClient(nullptr);
|
||||
_commandHandler->addCommand(kCmdClear, -1, 0, nullptr);
|
||||
_commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr);
|
||||
_spare->clear();
|
||||
_vga->_showQ->clear();
|
||||
_now = now;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::sceneUp(int cav) {
|
||||
_now = cav;
|
||||
int bakRef = _now << 8;
|
||||
if (_music)
|
||||
_midiPlayer->loadMidi(bakRef);
|
||||
showBak(bakRef);
|
||||
*_eye = *(_eyeTab[_now]);
|
||||
_mouseTop = V2D(this, V3D(0, 1, kScrDepth)).y;
|
||||
_map->load(_now);
|
||||
_spare->takeScene(_now);
|
||||
openPocket();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Hero *h = _heroTab[i]->_ptr;
|
||||
if (h && h->_scene == _now) {
|
||||
V2D p = *_heroTab[i]->_posTab[_now];
|
||||
h->gotoxyz(V3D(p.x, 0, p.y));
|
||||
h->clrHide();
|
||||
_vga->_showQ->insert(h);
|
||||
h->park();
|
||||
h->setCurrent();
|
||||
h->setContact();
|
||||
}
|
||||
}
|
||||
|
||||
_sound->stop();
|
||||
_fx->clear();
|
||||
|
||||
selectPocket(-1);
|
||||
_infoLine->setText(nullptr);
|
||||
busy(false);
|
||||
|
||||
if (!_dark)
|
||||
_vga->sunset();
|
||||
|
||||
_vga->show();
|
||||
_vga->copyPage(1, 0);
|
||||
_vga->show();
|
||||
_vga->sunrise(_vga->_sysPal);
|
||||
|
||||
_dark = false;
|
||||
|
||||
if (_gamePhase == kPhaseInGame)
|
||||
_mouse->on();
|
||||
|
||||
feedSnail(_vga->_showQ->locate(bakRef + 255), kNear, _heroTab[_sex]->_ptr);
|
||||
}
|
||||
|
||||
void CGE2Engine::sceneDown() {
|
||||
busy(true);
|
||||
_soundStat._wait = nullptr; // unlock snail
|
||||
Sprite *spr = _vga->_showQ->locate((_now << 8) | 254);
|
||||
|
||||
if (spr)
|
||||
feedSnail(spr, kNear, _heroTab[_sex]->_ptr);
|
||||
|
||||
while (!(_commandHandler->idle() && _commandHandlerTurbo->idle())) {
|
||||
_commandHandlerTurbo->runCommand();
|
||||
_commandHandler->runCommand();
|
||||
}
|
||||
|
||||
closePocket();
|
||||
for (int i = 0; i < 2; i++)
|
||||
_spare->update(_vga->_showQ->remove(_heroTab[i]->_ptr));
|
||||
_spare->dispose();
|
||||
}
|
||||
|
||||
void CGE2Engine::switchScene(int scene) {
|
||||
if (scene == _now)
|
||||
return;
|
||||
|
||||
_req = scene;
|
||||
storeHeroPos();
|
||||
*(_eyeTab[_now]) = *_eye;
|
||||
|
||||
if (scene < 0)
|
||||
_commandHandler->addCallback(kCmdExec, -1, 0, kQGame); // quit game
|
||||
else {
|
||||
if (_heroTab[_sex]->_ptr->_scene == _now) {
|
||||
_heroTab[_sex]->_ptr->setScene(scene);
|
||||
if (_heroTab[!_sex]->_ptr->_scene == _now)
|
||||
_heroTab[!_sex]->_ptr->setScene(scene);
|
||||
}
|
||||
_mouse->off();
|
||||
if (_heroTab[_sex]->_ptr)
|
||||
_heroTab[_sex]->_ptr->park();
|
||||
killText();
|
||||
_commandHandler->addCallback(kCmdExec, -1, 0, kXScene); // switch scene
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::storeHeroPos() {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Hero *h = _heroTab[i]->_ptr;
|
||||
if (h->_scene == _now) {
|
||||
delete _heroTab[i]->_posTab[_now];
|
||||
V2D *temp = new V2D(this, h->_pos3D._x.trunc(), h->_pos3D._z.trunc());
|
||||
_heroTab[i]->_posTab[_now] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::showBak(int ref) {
|
||||
Sprite *spr = _spare->locate(ref);
|
||||
if (spr != nullptr) {
|
||||
_bitmapPalette = _vga->_sysPal;
|
||||
spr->expand();
|
||||
_bitmapPalette = nullptr;
|
||||
spr->show(2);
|
||||
_vga->copyPage(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::mainLoop() {
|
||||
if (_gamePhase == kPhaseInGame)
|
||||
checkSounds();
|
||||
|
||||
_vga->show();
|
||||
_commandHandlerTurbo->runCommand();
|
||||
_commandHandler->runCommand();
|
||||
|
||||
// Handle a delay between game frames
|
||||
handleFrame();
|
||||
|
||||
// Handle any pending events
|
||||
_eventManager->poll();
|
||||
|
||||
// Check shouldQuit()
|
||||
_quitFlag = shouldQuit();
|
||||
}
|
||||
|
||||
void CGE2Engine::checkSounds() {
|
||||
checkMute();
|
||||
_sound->checkSoundHandles();
|
||||
checkVolumeSwitches();
|
||||
_midiPlayer->syncVolume();
|
||||
syncSoundSettings();
|
||||
}
|
||||
|
||||
void CGE2Engine::handleFrame() {
|
||||
// Game frame delay
|
||||
uint32 millis = g_system->getMillis();
|
||||
while (!_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) {
|
||||
// Handle any pending events
|
||||
_eventManager->poll();
|
||||
|
||||
if (millis >= (_lastTick + kGameTickDelay)) {
|
||||
// Dispatch the tick to any active objects
|
||||
tick();
|
||||
_lastTick = millis;
|
||||
}
|
||||
|
||||
// Slight delay
|
||||
g_system->delayMillis(5);
|
||||
millis = g_system->getMillis();
|
||||
}
|
||||
_lastFrame = millis;
|
||||
|
||||
if (millis >= (_lastTick + kGameTickDelay)) {
|
||||
// Dispatch the tick to any active objects
|
||||
tick();
|
||||
_lastTick = millis;
|
||||
}
|
||||
}
|
||||
|
||||
Sprite *CGE2Engine::locate(int ref) {
|
||||
_taken = false;
|
||||
Sprite *spr = _vga->_showQ->locate(ref);
|
||||
if (!spr) {
|
||||
spr = _spare->locate(ref);
|
||||
if (spr)
|
||||
_taken = true;
|
||||
}
|
||||
return spr;
|
||||
}
|
||||
|
||||
bool CGE2Engine::isHero(Sprite *spr) {
|
||||
return spr && spr->_ref / 10 == 14;
|
||||
}
|
||||
|
||||
void CGE2Engine::tick() {
|
||||
// system pseudo-sprite
|
||||
if (_sys && _sys->_time && (--_sys->_time == 0))
|
||||
_sys->tick();
|
||||
|
||||
for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) {
|
||||
if (spr->_time && (--spr->_time == 0))
|
||||
spr->tick();
|
||||
|
||||
if (_waitRef && (_waitRef == spr->_ref) && spr->seqTest(_waitSeq))
|
||||
_waitRef = 0;
|
||||
}
|
||||
|
||||
_mouse->tick();
|
||||
}
|
||||
|
||||
void CGE2Engine::busy(bool on) {
|
||||
if (on) {
|
||||
_spriteNotify = _midiNotify = &CGE2::CGE2Engine::busyStep;
|
||||
busyStep();
|
||||
} else {
|
||||
if (_busyPtr)
|
||||
_busyPtr->step(0);
|
||||
_spriteNotify = _midiNotify = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::busyStep() {
|
||||
if (_busyPtr) {
|
||||
_busyPtr->step((_busyPtr->_seqPtr) ? -1 : 1);
|
||||
_busyPtr->show(0);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::runGame() {
|
||||
if (_quitFlag)
|
||||
return;
|
||||
|
||||
loadUser();
|
||||
sceneUp(_now);
|
||||
initToolbar();
|
||||
|
||||
// main loop
|
||||
while (!_endGame && !_quitFlag)
|
||||
mainLoop();
|
||||
|
||||
// If leaving the game (close window, return to launcher, etc.
|
||||
// - except finishing the game), explicitly save it's state:
|
||||
if (!_endGame && canSaveGameStateCurrently())
|
||||
qGame();
|
||||
|
||||
_keyboard->setClient(nullptr);
|
||||
_commandHandler->addCommand(kCmdClear, -1, 0, nullptr);
|
||||
_commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr);
|
||||
_mouse->off();
|
||||
}
|
||||
|
||||
void CGE2Engine::loadUser() {
|
||||
loadPos();
|
||||
|
||||
if (_startGameSlot != -1)
|
||||
loadGame(_startGameSlot);
|
||||
else {
|
||||
loadScript("CGE.INI");
|
||||
loadHeroes();
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::loadHeroes() { // Original name: loadGame()
|
||||
// load sprites & pocket
|
||||
|
||||
Sprite *s;
|
||||
Hero *h = nullptr;
|
||||
|
||||
// initialize Andzia/Anna
|
||||
s = _spare->take(142);
|
||||
if (s) {
|
||||
h = new Hero(this);
|
||||
*(Sprite*)h = *s;
|
||||
delete s;
|
||||
h->expand();
|
||||
_spare->update(h);
|
||||
}
|
||||
_heroTab[0]->_ptr = h;
|
||||
s = _spare->locate(152);
|
||||
_vga->_showQ->insert(s);
|
||||
_heroTab[0]->_face = s;
|
||||
|
||||
// initialize Wacek/Vincent
|
||||
s = _spare->take(141);
|
||||
if (s) {
|
||||
h = new Hero(this);
|
||||
*(Sprite*)h = *s;
|
||||
delete s;
|
||||
h->expand();
|
||||
_spare->update(h);
|
||||
}
|
||||
_heroTab[1]->_ptr = h;
|
||||
s = _spare->locate(151);
|
||||
_vga->_showQ->insert(s);
|
||||
_heroTab[1]->_face = s;
|
||||
|
||||
//--- start!
|
||||
switchHero(_sex);
|
||||
}
|
||||
|
||||
void CGE2Engine::loadPos() {
|
||||
if (_resman->exist("CGE.HXY")) {
|
||||
for (int cav = 0; cav < kSceneMax; cav++)
|
||||
_heroTab[1]->_posTab[cav] = new V2D(this, 180, 10);
|
||||
|
||||
EncryptedStream file(_resman, "CGE.HXY");
|
||||
|
||||
for (int cav = 0; cav < kSceneMax; cav++) {
|
||||
_heroTab[0]->_posTab[cav] = new V2D(this);
|
||||
_heroTab[0]->_posTab[cav]->x = file.readSint16LE();
|
||||
_heroTab[0]->_posTab[cav]->y = file.readSint16LE();
|
||||
}
|
||||
|
||||
for (int cav = 0; cav < 41; cav++) { // (564 - 400) / 4 = 41
|
||||
_heroTab[1]->_posTab[cav]->x = file.readSint16LE();
|
||||
_heroTab[1]->_posTab[cav]->y = file.readSint16LE();
|
||||
}
|
||||
} else
|
||||
error("Missing file: CGE.HXY");
|
||||
}
|
||||
|
||||
void CGE2Engine::loadTab() {
|
||||
setEye(_text->getText(240));
|
||||
for (int i = 0; i < kSceneMax; i++)
|
||||
*(_eyeTab[i]) = *_eye;
|
||||
|
||||
if (_resman->exist(kTabName)) {
|
||||
EncryptedStream f(_resman, kTabName);
|
||||
|
||||
for (int i = 0; i < kSceneMax; i++) {
|
||||
uint32 v = f.readUint32LE();
|
||||
_eyeTab[i]->_x = FXP(v >> 8, static_cast<int>((int8)(v & 0xff)));
|
||||
|
||||
v = f.readUint32LE();
|
||||
_eyeTab[i]->_y = FXP(v >> 8, static_cast<int>((int8)(v & 0xff)));
|
||||
|
||||
v = f.readUint32LE();
|
||||
_eyeTab[i]->_z = FXP(v >> 8, static_cast<int>((int8)(v & 0xff)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::cge2_main() {
|
||||
loadTab();
|
||||
|
||||
if (_startGameSlot != -1) {
|
||||
// Starting up a savegame from the launcher
|
||||
runGame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (showTitle("WELCOME")) {
|
||||
movie(kIntroExt);
|
||||
|
||||
if (_text->getText(255) != nullptr) {
|
||||
runGame();
|
||||
_gamePhase = kPhaseOver;
|
||||
}
|
||||
|
||||
_vga->sunset();
|
||||
} else
|
||||
_vga->sunset();
|
||||
}
|
||||
|
||||
char *CGE2Engine::mergeExt(char *buf, const char *name, const char *ext) {
|
||||
Common::strcpy_s(buf, kPathMax, name);
|
||||
char *dot = strrchr(buf, '.');
|
||||
if (!dot)
|
||||
Common::strcat_s(buf, kPathMax, ext);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void CGE2Engine::setEye(const V3D &e) {
|
||||
*_eye = e;
|
||||
}
|
||||
|
||||
void CGE2Engine::setEye(const V2D& e2, int z) {
|
||||
_eye->_x = e2.x;
|
||||
_eye->_y = e2.y;
|
||||
_eye->_z = z;
|
||||
}
|
||||
|
||||
void CGE2Engine::setEye(const char *s) {
|
||||
size_t ln = strlen(s) + 1;
|
||||
char *tempStr = new char[ln];
|
||||
Common::strcpy_s(tempStr, ln, s);
|
||||
_eye->_x = atoi(token(tempStr));
|
||||
_eye->_y = atoi(token(nullptr));
|
||||
_eye->_z = atoi(token(nullptr));
|
||||
delete[] tempStr;
|
||||
}
|
||||
|
||||
int CGE2Engine::newRandom(int range) {
|
||||
if (!range)
|
||||
return 0;
|
||||
|
||||
return _randomSource.getRandomNumber(range - 1);
|
||||
}
|
||||
|
||||
bool CGE2Engine::showTitle(const char *name) {
|
||||
if (_quitFlag)
|
||||
return false;
|
||||
|
||||
_bitmapPalette = _vga->_sysPal;
|
||||
BitmapPtr LB = new Bitmap[1];
|
||||
LB[0] = Bitmap(this, name);
|
||||
_bitmapPalette = nullptr;
|
||||
|
||||
Sprite D(this, LB, 1);
|
||||
D._flags._kill = true;
|
||||
D.gotoxyz(kScrWidth >> 1, -(kPanHeight >> 1));
|
||||
|
||||
_vga->sunset();
|
||||
D.show(2);
|
||||
_vga->copyPage(1, 2);
|
||||
_vga->copyPage(0, 1);
|
||||
_vga->sunrise(_vga->_sysPal);
|
||||
_vga->update();
|
||||
|
||||
g_system->delayMillis(2500);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGE2Engine::killText() {
|
||||
if (!_talk)
|
||||
return;
|
||||
|
||||
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk);
|
||||
_talk = nullptr;
|
||||
}
|
||||
|
||||
void CGE2Engine::switchHero(int sex) {
|
||||
if (sex != _sex) {
|
||||
int scene = _heroTab[sex]->_ptr->_scene;
|
||||
if (_sys->_blinkSprite) {
|
||||
_sys->_blinkSprite->_flags._hide = false;
|
||||
_sys->_blinkSprite = nullptr;
|
||||
}
|
||||
|
||||
if (scene >= 0) {
|
||||
_commandHandler->addCommand(kCmdSeq, -1, 2, _heroTab[_sex]->_face);
|
||||
_sex ^= 1;
|
||||
switchScene(scene);
|
||||
}
|
||||
}
|
||||
Sprite *face = _heroTab[_sex]->_face;
|
||||
if (face->_seqPtr == 0)
|
||||
_commandHandler->addCommand(kCmdSeq, -1, 1, face);
|
||||
}
|
||||
|
||||
void Sprite::touch(uint16 mask, V2D pos) {
|
||||
if ((mask & kEventAttn) != 0)
|
||||
return;
|
||||
|
||||
if (_vm->_gamePhase == kPhaseInGame)
|
||||
_vm->_infoLine->setText(name());
|
||||
|
||||
if (_ref < 0)
|
||||
return; // cannot access system sprites
|
||||
|
||||
if (_ref / 10 == 12) {
|
||||
_vm->optionTouch(_ref % 10, mask);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mask & kMouseLeftUp) && _vm->_commandHandler->idle()) {
|
||||
if (_vm->isHero(this) && !_vm->_sys->_blinkSprite) {
|
||||
_vm->switchHero((this == _vm->_heroTab[1]->_ptr) ? 1 : 0);
|
||||
} else if (_flags._kept) { // sprite in pocket
|
||||
for (int sex = 0; sex < 2; ++sex) {
|
||||
for (int p = 0; p < kPocketMax; ++p) {
|
||||
if (_vm->_heroTab[sex]->_pocket[p] == this) {
|
||||
_vm->switchHero(sex);
|
||||
if (_vm->_sex == sex) {
|
||||
if (_vm->_sys->_blinkSprite)
|
||||
_vm->_sys->_blinkSprite->_flags._hide = false;
|
||||
|
||||
if (_vm->_sys->_blinkSprite == this)
|
||||
_vm->_sys->_blinkSprite = nullptr;
|
||||
else
|
||||
_vm->_sys->_blinkSprite = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // sprite NOT in pocket
|
||||
Hero *h = _vm->_heroTab[_vm->_sex]->_ptr;
|
||||
if (!_vm->_talk) {
|
||||
// the "+3" is a hack used to work around a script issue in scene 5
|
||||
if ((_ref & 0xFF) < 200 && h->distance(this) > (h->_maxDist << 1) + 3)
|
||||
h->walkTo(this);
|
||||
else if (_vm->_sys->_blinkSprite) {
|
||||
if (works(_vm->_sys->_blinkSprite)) {
|
||||
_vm->feedSnail(_vm->_sys->_blinkSprite, (_vm->_sex) ? kMTake : kFTake, _vm->_heroTab[_vm->_sex]->_ptr);
|
||||
_vm->_sys->_blinkSprite->_flags._hide = false;
|
||||
_vm->_sys->_blinkSprite = nullptr;
|
||||
} else
|
||||
_vm->offUse();
|
||||
|
||||
_vm->selectPocket(-1);
|
||||
// else, no pocket sprite selected
|
||||
} else if (_flags._port) { // portable
|
||||
if (_vm->findActivePocket(-1) < 0)
|
||||
_vm->pocFul();
|
||||
else {
|
||||
_vm->_commandHandler->addCommand(kCmdReach, -2, _ref, nullptr);
|
||||
_vm->_commandHandler->addCommand(kCmdKeep, -1, -1, this);
|
||||
_flags._port = false;
|
||||
}
|
||||
} else { // non-portable
|
||||
Action a = h->action();
|
||||
if (_actionCtrl[a]._cnt) {
|
||||
CommandHandler::Command *cmdList = snList(a);
|
||||
if (cmdList[_actionCtrl[a]._ptr]._commandType == kCmdNext)
|
||||
_vm->offUse();
|
||||
else
|
||||
_vm->feedSnail(this, a, h);
|
||||
} else
|
||||
_vm->offUse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::keyClick() {
|
||||
_commandHandlerTurbo->addCommand(kCmdSound, -1, 5, nullptr);
|
||||
}
|
||||
|
||||
void CGE2Engine::offUse() {
|
||||
int seq = 0;
|
||||
int offUseCount = atoi(_text->getText(kOffUseCount));
|
||||
|
||||
// This fixes the issue of empty speech bubbles in the original.
|
||||
// Now we only let this cycle pass if it randoms a valid value for getText().
|
||||
int txt = 0;
|
||||
do {
|
||||
txt = kOffUseText + _sex * offUseCount + newRandom(offUseCount);
|
||||
} while (_text->getText(txt) == nullptr);
|
||||
|
||||
Hero *h = _heroTab[_sex]->_ptr;
|
||||
h->park();
|
||||
_commandHandler->addCommand(kCmdWait, -1, -1, h);
|
||||
_commandHandler->addCommand(kCmdSeq, -1, seq, h);
|
||||
if (!_sayVox)
|
||||
_commandHandler->addCommand(kCmdSound, -1, 6 + _sex, h);
|
||||
_commandHandler->addCommand(kCmdWait, -1, -1, h);
|
||||
_commandHandler->addCommand(kCmdSay, -1, txt, h);
|
||||
}
|
||||
|
||||
Sprite *CGE2Engine::spriteAt(V2D pos) {
|
||||
Sprite *spr;
|
||||
|
||||
for (spr = _vga->_showQ->last(); spr; spr = spr->_prev) {
|
||||
if (!spr->_flags._hide && !spr->_flags._tran && (spr->getShp()->solidAt(pos - spr->_pos2D)))
|
||||
break;
|
||||
}
|
||||
|
||||
return spr;
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
51
engines/cge2/cge2_main.h
Normal file
51
engines/cge2/cge2_main.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_MAIN_H
|
||||
#define CGE2_MAIN_H
|
||||
|
||||
#include "cge2/events.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kShowScummVMVersion 15
|
||||
|
||||
class System : public Sprite {
|
||||
public:
|
||||
int _blinkCounter;
|
||||
Sprite *_blinkSprite;
|
||||
|
||||
System(CGE2Engine *vm);
|
||||
|
||||
void touch(uint16 mask, V2D pos) override;
|
||||
void tick() override;
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_MAIN_H
|
||||
3
engines/cge2/configure.engine
Normal file
3
engines/cge2/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine cge2 "CGE2" yes "" "" "" "midi"
|
||||
47
engines/cge2/console.cpp
Normal file
47
engines/cge2/console.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/* 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 "cge2/console.h"
|
||||
|
||||
#include "cge2/vga13h.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
CGE2Console::CGE2Console(CGE2Engine *vm) : _vm(vm), GUI::Debugger() {
|
||||
registerCmd("do_carpet_workaround", WRAP_METHOD(CGE2Console, doCarpetWorkaround));
|
||||
}
|
||||
|
||||
CGE2Console::~CGE2Console() {
|
||||
}
|
||||
|
||||
bool CGE2Console::doCarpetWorkaround(int argc, const char **argv) {
|
||||
Sprite *spr = _vm->_vga->_showQ->locate(1537); // 1537 is Carpet
|
||||
|
||||
if (spr) {
|
||||
if (spr->_actionCtrl[1]._ptr == 26) {
|
||||
spr->_actionCtrl[1]._ptr = 8;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace CGE
|
||||
44
engines/cge2/console.h
Normal file
44
engines/cge2/console.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* 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 CGE2_CONSOLE_H
|
||||
#define CGE2_CONSOLE_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class CGE2Engine;
|
||||
|
||||
class CGE2Console : public GUI::Debugger {
|
||||
public:
|
||||
CGE2Console(CGE2Engine *vm);
|
||||
~CGE2Console() override;
|
||||
|
||||
private:
|
||||
bool doCarpetWorkaround(int argc, const char **argv);
|
||||
|
||||
CGE2Engine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace CGE
|
||||
|
||||
#endif // CGE2_CONSOLE_H
|
||||
5
engines/cge2/credits.pl
Normal file
5
engines/cge2/credits.pl
Normal file
@@ -0,0 +1,5 @@
|
||||
begin_section("CGE2");
|
||||
add_person("Peter Bozsó", "uruk", "");
|
||||
add_person("Arnaud Boutonné", "Strangerke", "");
|
||||
add_person("Paul Gilbert", "dreammaster", "");
|
||||
end_section();
|
||||
160
engines/cge2/detection.cpp
Normal file
160
engines/cge2/detection.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "cge2/fileio.h"
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/detection.h"
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{CGE2::kCGE2DebugOpcode, "opcode", "CGE2 opcode debug channel"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
static const PlainGameDescriptor CGE2Games[] = {
|
||||
{ "sfinx", "Sfinx" },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
{
|
||||
"sfinx", "Freeware",
|
||||
AD_ENTRY2s("vol.cat", "21197b287d397c53261b6616bf0dd880", 129024,
|
||||
"vol.dat", "de14291869a8eb7c2732ab783c7542ef", 34180844),
|
||||
Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO2(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS)
|
||||
},
|
||||
|
||||
{
|
||||
"sfinx", "Freeware v1.0",
|
||||
AD_ENTRY2s("vol.cat", "aa402aed24a72c53a4d1211c456b79dd", 129024,
|
||||
"vol.dat", "5966ac26d91d664714349669f9dd09b5", 34180164),
|
||||
Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO2(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS)
|
||||
},
|
||||
|
||||
{
|
||||
"sfinx", "Freeware v1.1",
|
||||
AD_ENTRY2s("vol.cat", "aa402aed24a72c53a4d1211c456b79dd", 129024,
|
||||
"vol.dat", "5966ac26d91d664714349669f9dd09b5", 34180367),
|
||||
Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO2(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS)
|
||||
},
|
||||
|
||||
{
|
||||
"sfinx", "Demo",
|
||||
AD_ENTRY2s("vol.cat", "68271dc56aa37c4e83b792df69625a00", 24576,
|
||||
"vol.dat", "989c26861bc56fafa3a85784e374ca15", 2710971),
|
||||
Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
|
||||
},
|
||||
|
||||
{
|
||||
"sfinx", "Freeware v0.3",
|
||||
AD_ENTRY2s("vol.cat", "f158e469dccbebc5a632eb848df89779", 129024,
|
||||
"vol.dat", "d40a6b4ae173d6930be54ba56bee15d5", 34183430),
|
||||
Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO3(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
|
||||
},
|
||||
|
||||
{
|
||||
"sfinx", "Freeware v1.0",
|
||||
AD_ENTRY2s("vol.cat", "f158e469dccbebc5a632eb848df89779", 129024,
|
||||
"vol.dat", "d40a6b4ae173d6930be54ba56bee15d5", 34183443),
|
||||
Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO3(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
|
||||
},
|
||||
|
||||
{
|
||||
"sfinx", "Freeware v1.1",
|
||||
AD_ENTRY2s("vol.cat", "f158e469dccbebc5a632eb848df89779", 129024,
|
||||
"vol.dat", "d40a6b4ae173d6930be54ba56bee15d5", 34182773),
|
||||
Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO3(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
|
||||
},
|
||||
|
||||
AD_TABLE_END_MARKER
|
||||
};
|
||||
|
||||
class CGE2MetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
public:
|
||||
CGE2MetaEngineDetection() : AdvancedMetaEngineDetection(gameDescriptions, CGE2Games) {
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "cge2";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "CGE2";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Sfinx (C) 1994-1997 Janusz B. Wisniewski and L.K. Avalon";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
|
||||
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override;
|
||||
};
|
||||
|
||||
static ADGameDescription s_fallbackDesc = {
|
||||
"sfinx",
|
||||
"Unknown version",
|
||||
AD_ENTRY1(nullptr, nullptr), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF)
|
||||
};
|
||||
|
||||
static const ADFileBasedFallback fileBasedFallback[] = {
|
||||
{ &s_fallbackDesc, { "vol.cat", "vol.dat", nullptr } },
|
||||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
// This fallback detection looks identical to the one used for CGE. In fact, the difference resides
|
||||
// in the ResourceManager which handles a different archive format. The rest of the detection is identical.
|
||||
ADDetectedGame CGE2MetaEngineDetection::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const {
|
||||
ADDetectedGame game = detectGameFilebased(allFiles, CGE2::fileBasedFallback);
|
||||
|
||||
if (!game.desc)
|
||||
return ADDetectedGame();
|
||||
|
||||
SearchMan.addDirectory("CGE2MetaEngineDetection::fallbackDetect", fslist.begin()->getParent());
|
||||
ResourceManager *resman;
|
||||
resman = new ResourceManager();
|
||||
bool sayFileFound = resman->exist("CGE.SAY");
|
||||
delete resman;
|
||||
|
||||
SearchMan.remove("CGE2MetaEngineDetection::fallbackDetect");
|
||||
|
||||
if (!sayFileFound)
|
||||
return ADDetectedGame();
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
REGISTER_PLUGIN_STATIC(CGE2_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, CGE2::CGE2MetaEngineDetection);
|
||||
29
engines/cge2/detection.h
Normal file
29
engines/cge2/detection.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* 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 CGE2_DETECTION_H
|
||||
#define CGE2_DETECTION_H
|
||||
|
||||
#define GAMEOPTION_COLOR_BLIND_DEFAULT_OFF GUIO_GAMEOPTIONS1
|
||||
#define GAMEOPTION_TTS_OBJECTS GUIO_GAMEOPTIONS2
|
||||
#define GAMEOPTION_TTS_SPEECH GUIO_GAMEOPTIONS3
|
||||
|
||||
#endif
|
||||
250
engines/cge2/events.cpp
Normal file
250
engines/cge2/events.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "gui/saveload.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/events.h"
|
||||
#include "cge2/events.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/cge2_main.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
/*----------------- KEYBOARD interface -----------------*/
|
||||
|
||||
Keyboard::Keyboard(CGE2Engine *vm) : _client(nullptr), _vm(vm) {
|
||||
}
|
||||
|
||||
Keyboard::~Keyboard() {
|
||||
}
|
||||
|
||||
Sprite *Keyboard::setClient(Sprite *spr) {
|
||||
SWAP(_client, spr);
|
||||
return spr;
|
||||
}
|
||||
|
||||
void Keyboard::handleAction(Common::Event &event) {
|
||||
switch (event.customType) {
|
||||
case kActionInfo:
|
||||
// Display ScummVM version and translation strings
|
||||
for (int i = 0; i < 3; i++)
|
||||
_vm->_commandHandler->addCommand(kCmdInf, 1, kShowScummVMVersion + i, nullptr);
|
||||
break;
|
||||
case kActionSave:
|
||||
_vm->saveGameDialog();
|
||||
break;
|
||||
case kActionLoad:
|
||||
_vm->loadGameDialog();
|
||||
break;
|
||||
case kActionQuit:
|
||||
_vm->quit();
|
||||
break;
|
||||
case kActionEscape:
|
||||
if (_client) {
|
||||
CGE2Event &evt = _vm->_eventManager->getNextEvent();
|
||||
evt._x = 0;
|
||||
evt._y = 0;
|
||||
evt._mask = kEventEsc; // Event mask
|
||||
evt._spritePtr = _client; // Sprite pointer
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------- MOUSE interface -----------------*/
|
||||
|
||||
Mouse::Mouse(CGE2Engine *vm) : Sprite(vm), _busy(nullptr), _hold(nullptr), _hx(0), _point(vm), _vm(vm) {
|
||||
_hold = nullptr;
|
||||
_hx = 0;
|
||||
_hy = 0;
|
||||
_exist = true;
|
||||
_buttons = 0;
|
||||
_busy = nullptr;
|
||||
_active = false;
|
||||
_flags._kill = false;
|
||||
|
||||
setSeq(_stdSeq8);
|
||||
|
||||
BitmapPtr MC = new Bitmap[2];
|
||||
MC[0] = Bitmap(_vm, "MOUSE");
|
||||
MC[1] = Bitmap(_vm, "DUMMY");
|
||||
setShapeList(MC, 2);
|
||||
|
||||
step(1);
|
||||
on();
|
||||
off();
|
||||
}
|
||||
|
||||
Mouse::~Mouse() {
|
||||
off();
|
||||
}
|
||||
|
||||
void Mouse::on() {
|
||||
if (_seqPtr && _exist) {
|
||||
_active = true;
|
||||
step(0);
|
||||
if (_busy)
|
||||
_busy->step(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::off() {
|
||||
if (_seqPtr == 0) {
|
||||
if (_exist)
|
||||
_active = false;
|
||||
|
||||
step(1);
|
||||
if (_busy)
|
||||
_busy->step(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::newMouse(Common::Event &event) {
|
||||
if (!_active)
|
||||
return;
|
||||
|
||||
CGE2Event &evt = _vm->_eventManager->getNextEvent();
|
||||
evt._x = event.mouse.x;
|
||||
evt._y = event.mouse.y;
|
||||
evt._spritePtr = _vm->spriteAt(V2D(_vm, evt._x, evt._y));
|
||||
|
||||
switch (event.type) {
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
evt._mask = kMouseRoll;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
evt._mask = kMouseLeftDown;
|
||||
_buttons |= 1;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
evt._mask = kMouseLeftUp;
|
||||
_buttons &= ~1;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
evt._mask = kMouseRightDown;
|
||||
_buttons |= 2;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
evt._mask = kMouseRightUp;
|
||||
_buttons &= ~2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------- EventManager interface -----------------*/
|
||||
|
||||
EventManager::EventManager(CGE2Engine *vm) : _vm(vm) {
|
||||
_eventQueueHead = 0;
|
||||
_eventQueueTail = 0;
|
||||
for (uint16 k = 0; k < kEventMax; k++) {
|
||||
_eventQueue[k]._mask = 0;
|
||||
_eventQueue[k]._x = 0;
|
||||
_eventQueue[k]._y = 0;
|
||||
_eventQueue[k]._spritePtr = nullptr;
|
||||
}
|
||||
_event.joystick.axis = 0;
|
||||
_event.joystick.position = 0;
|
||||
_event.joystick.button = 0;
|
||||
}
|
||||
|
||||
void EventManager::poll() {
|
||||
while (g_system->getEventManager()->pollEvent(_event)) {
|
||||
_event.mouse.y = kWorldHeight - _event.mouse.y;
|
||||
switch (_event.type) {
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
// Handle keyboard events
|
||||
_vm->_keyboard->handleAction(_event);
|
||||
handleEvents();
|
||||
break;
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
// Handle mouse events
|
||||
_vm->_mouse->newMouse(_event);
|
||||
handleEvents();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::handleEvents() {
|
||||
while (_eventQueueTail != _eventQueueHead) {
|
||||
CGE2Event e = _eventQueue[_eventQueueTail];
|
||||
_vm->_mouse->_point = V2D(_vm, e._x, e._y);
|
||||
if (e._mask) {
|
||||
if (e._mask & kMouseMask) {
|
||||
e._spritePtr = _vm->spriteAt(_vm->_mouse->_point);
|
||||
e._x += (_vm->_mouse->_siz.x >> 1);
|
||||
e._y -= _vm->_mouse->_siz.y;
|
||||
if (_vm->_mouse->_hold && (e._spritePtr != _vm->_mouse->_hold)) {
|
||||
_vm->_mouse->_hold->touch(e._mask | kEventAttn,
|
||||
V2D(_vm, e._x - _vm->_mouse->_hold->_pos2D.x, e._y - _vm->_mouse->_hold->_pos2D.y));
|
||||
}
|
||||
// update mouse cursor position
|
||||
if (e._mask & kMouseRoll)
|
||||
_vm->_mouse->gotoxyz(V2D(_vm, e._x, e._y));
|
||||
}
|
||||
|
||||
// activate current touched SPRITE
|
||||
if (e._spritePtr) {
|
||||
e._spritePtr->touch(e._mask, _vm->_mouse->_point - e._spritePtr->_pos2D);
|
||||
} else if (_vm->_sys)
|
||||
_vm->_sys->touch(e._mask, _vm->_mouse->_point);
|
||||
|
||||
// discard Text if button released
|
||||
if (e._mask & (kMouseLeftUp | kMouseRightUp))
|
||||
_vm->killText();
|
||||
}
|
||||
_eventQueueTail = (_eventQueueTail + 1) % kEventMax;
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::clearEvent(Sprite *spr) {
|
||||
if (spr) {
|
||||
for (uint16 e = _eventQueueTail; e != _eventQueueHead; e = (e + 1) % kEventMax) {
|
||||
if (_eventQueue[e]._spritePtr == spr)
|
||||
_eventQueue[e]._mask = 0;
|
||||
}
|
||||
} else
|
||||
_eventQueueTail = _eventQueueHead;
|
||||
}
|
||||
|
||||
CGE2Event &EventManager::getNextEvent() {
|
||||
CGE2Event &evt = _eventQueue[_eventQueueHead];
|
||||
_eventQueueHead = (_eventQueueHead + 1) % kEventMax;
|
||||
|
||||
return evt;
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
113
engines/cge2/events.h
Normal file
113
engines/cge2/events.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_EVENTS_H
|
||||
#define CGE2_EVENTS_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "cge2/talk.h"
|
||||
#include "cge2/vga13h.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
/*----------------- KEYBOARD interface -----------------*/
|
||||
|
||||
#define kEventMax 256
|
||||
|
||||
enum EventMask {
|
||||
kMouseRoll = 1 << 0,
|
||||
kMouseLeftDown = 1 << 1,
|
||||
kMouseLeftUp = 1 << 2,
|
||||
kMouseRightDown = 1 << 3,
|
||||
kMouseRightUp = 1 << 4,
|
||||
kEventAttn = 1 << 5,
|
||||
kMouseMask = (kMouseRoll | kMouseLeftDown | kMouseLeftUp | kMouseRightDown | kMouseRightUp),
|
||||
kEventEsc = 1 << 7
|
||||
};
|
||||
|
||||
class Keyboard {
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
public:
|
||||
Sprite *_client;
|
||||
|
||||
void handleAction(Common::Event &event);
|
||||
Sprite *setClient(Sprite *spr);
|
||||
|
||||
Keyboard(CGE2Engine *vm);
|
||||
~Keyboard();
|
||||
};
|
||||
|
||||
/*----------------- MOUSE interface -----------------*/
|
||||
|
||||
struct CGE2Event {
|
||||
uint16 _mask;
|
||||
uint16 _x;
|
||||
uint16 _y;
|
||||
Sprite *_spritePtr;
|
||||
};
|
||||
|
||||
class Mouse : public Sprite {
|
||||
public:
|
||||
V2D _point;
|
||||
Sprite *_hold;
|
||||
bool _active;
|
||||
int _hx;
|
||||
int _hy;
|
||||
bool _exist;
|
||||
int _buttons;
|
||||
Sprite *_busy;
|
||||
Mouse(CGE2Engine *vm);
|
||||
~Mouse() override;
|
||||
void on();
|
||||
void off();
|
||||
void newMouse(Common::Event &event);
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
};
|
||||
|
||||
/*----------------- EventManager interface -----------------*/
|
||||
|
||||
class EventManager {
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
Common::Event _event;
|
||||
CGE2Event _eventQueue[kEventMax];
|
||||
uint16 _eventQueueHead;
|
||||
uint16 _eventQueueTail;
|
||||
|
||||
void handleEvents();
|
||||
public:
|
||||
EventManager(CGE2Engine *vm);
|
||||
void poll();
|
||||
void clearEvent(Sprite *spr);
|
||||
|
||||
CGE2Event &getNextEvent();
|
||||
};
|
||||
|
||||
} // End of namespace CGE
|
||||
|
||||
#endif // #define CGE2_EVENTS_H
|
||||
281
engines/cge2/fileio.cpp
Normal file
281
engines/cge2/fileio.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/str.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/memstream.h"
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/fileio.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* BtPage
|
||||
*-----------------------------------------------------------------------*/
|
||||
void BtPage::readBTree(Common::ReadStream &s) {
|
||||
_header._count = s.readUint16LE();
|
||||
_header._down = s.readUint16LE();
|
||||
|
||||
if (_header._down == kBtValNone) {
|
||||
// Leaf list
|
||||
for (int i = 0; i < kBtLeafCount; ++i) {
|
||||
s.read(_leaf[i]._key, kBtKeySize);
|
||||
_leaf[i]._pos = s.readUint32LE();
|
||||
_leaf[i]._size = s.readUint32LE();
|
||||
}
|
||||
} else {
|
||||
// Root index
|
||||
for (int i = 0; i < kBtInnerCount; ++i) {
|
||||
s.read(_inner[i]._key, kBtKeySize);
|
||||
_inner[i]._down = s.readUint16LE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* ResourceManager
|
||||
*-----------------------------------------------------------------------*/
|
||||
ResourceManager::ResourceManager() {
|
||||
_datFile = new Common::File();
|
||||
_datFile->open(kDatName);
|
||||
|
||||
_catFile = new Common::File();
|
||||
_catFile->open(kCatName);
|
||||
|
||||
if (!_datFile->isOpen() || !_catFile->isOpen())
|
||||
error("Unable to open data files");
|
||||
|
||||
for (int i = 0; i < kBtLevel; i++) {
|
||||
_buff[i]._page = new BtPage;
|
||||
_buff[i]._pageNo = kBtValNone;
|
||||
_buff[i]._index = -1;
|
||||
assert(_buff[i]._page != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
ResourceManager::~ResourceManager() {
|
||||
_datFile->close();
|
||||
delete _datFile;
|
||||
|
||||
_catFile->close();
|
||||
delete _catFile;
|
||||
|
||||
for (int i = 0; i < kBtLevel; i++)
|
||||
delete _buff[i]._page;
|
||||
}
|
||||
|
||||
void ResourceManager::xCrypt(byte *buf, uint16 length) {
|
||||
byte *b = buf;
|
||||
|
||||
for (uint16 i = 0; i < length; i++)
|
||||
*b++ ^= kCryptSeed;
|
||||
}
|
||||
|
||||
bool ResourceManager::seek(int32 offs, int whence) {
|
||||
return _datFile->seek(offs, whence);
|
||||
}
|
||||
|
||||
uint16 ResourceManager::read(byte *buf, uint16 length) {
|
||||
if (!_datFile->isOpen())
|
||||
return 0;
|
||||
|
||||
uint16 bytesRead = _datFile->read(buf, length);
|
||||
if (!bytesRead)
|
||||
error("Read %s - %d bytes", _datFile->getName(), length);
|
||||
xCrypt(buf, length);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
BtPage *ResourceManager::getPage(int level, uint16 pageId) {
|
||||
if (_buff[level]._pageNo != pageId) {
|
||||
int32 pos = pageId * kBtSize;
|
||||
_buff[level]._pageNo = pageId;
|
||||
|
||||
if (_catFile->size() <= pos)
|
||||
return nullptr;
|
||||
|
||||
// In the original, there was a check verifying if the
|
||||
// purpose was to write a new file. This should only be
|
||||
// to create a new file, thus it was removed.
|
||||
_catFile->seek(pageId * kBtSize, SEEK_SET);
|
||||
|
||||
// Read in the page
|
||||
byte buffer[kBtSize];
|
||||
int bytesRead = catRead(buffer, kBtSize);
|
||||
|
||||
// Unpack it into the page structure
|
||||
Common::MemoryReadStream stream(buffer, bytesRead, DisposeAfterUse::NO);
|
||||
_buff[level]._page->readBTree(stream);
|
||||
_buff[level]._index = -1;
|
||||
}
|
||||
return _buff[level]._page;
|
||||
}
|
||||
|
||||
BtKeypack *ResourceManager::find(const char *key) {
|
||||
int lev = 0;
|
||||
uint16 nxt = kBtValRoot;
|
||||
while (!_catFile->eos()) {
|
||||
BtPage *pg = getPage(lev, nxt);
|
||||
if (!pg)
|
||||
return nullptr;
|
||||
|
||||
// search
|
||||
if (pg->_header._down != kBtValNone) {
|
||||
int i;
|
||||
for (i = 0; i < pg->_header._count; i++) {
|
||||
// Does this work, or does it have to compare the entire buffer?
|
||||
if (scumm_strnicmp((const char *)key, (const char*)pg->_inner[i]._key, kBtKeySize) < 0)
|
||||
break;
|
||||
}
|
||||
nxt = (i) ? pg->_inner[i - 1]._down : pg->_header._down;
|
||||
_buff[lev]._index = i - 1;
|
||||
lev++;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < pg->_header._count - 1; i++) {
|
||||
if (scumm_stricmp((const char *)key, (const char *)pg->_leaf[i]._key) <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Hack to work around a mix between 24piram_ and 24pirami
|
||||
if (!strcmp(key, "24piram_.SPR") && (scumm_stricmp((const char *)key, (const char *)pg->_leaf[i]._key) < 0))
|
||||
++i;
|
||||
//
|
||||
|
||||
_buff[lev]._index = i;
|
||||
return &pg->_leaf[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ResourceManager::exist(const char *name) {
|
||||
BtKeypack *result = find(name);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
return scumm_stricmp(result->_key, name) == 0;
|
||||
}
|
||||
|
||||
uint16 ResourceManager::catRead(byte *buf, uint16 length) {
|
||||
if (!_catFile->isOpen())
|
||||
return 0;
|
||||
|
||||
uint16 bytesRead = _catFile->read(buf, length);
|
||||
if (!bytesRead)
|
||||
error("Read %s - %d bytes", _catFile->getName(), length);
|
||||
xCrypt(buf, length);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* EncryptedStream
|
||||
*-----------------------------------------------------------------------*/
|
||||
EncryptedStream::EncryptedStream(ResourceManager *resman, const char *name) : _lineCount(0) {
|
||||
_error = false;
|
||||
BtKeypack *kp = resman->find(name);
|
||||
if (scumm_stricmp(kp->_key, name) != 0)
|
||||
_error = true;
|
||||
|
||||
resman->seek(kp->_pos);
|
||||
byte *dataBuffer;
|
||||
int bufSize;
|
||||
|
||||
if ((strlen(name) > 4) && (scumm_stricmp(name + strlen(name) - 4, ".SPR") == 0)) {
|
||||
// SPR files have some inconsistencies. Some have extra 0x1A at the end, some others
|
||||
// do not have a carriage return at the end of the last line
|
||||
// Therefore, we remove this ending 0x1A and add extra new lines.
|
||||
// This fixes bug #6060
|
||||
dataBuffer = (byte *)malloc(kp->_size + 2);
|
||||
resman->read(dataBuffer, kp->_size);
|
||||
if (dataBuffer[kp->_size - 1] == 0x1A)
|
||||
dataBuffer[kp->_size - 1] = '\n';
|
||||
dataBuffer[kp->_size] = '\n';
|
||||
dataBuffer[kp->_size + 1] = '\n';
|
||||
bufSize = kp->_size + 2;
|
||||
} else {
|
||||
dataBuffer = (byte *)malloc(kp->_size);
|
||||
resman->read(dataBuffer, kp->_size);
|
||||
bufSize = kp->_size;
|
||||
}
|
||||
|
||||
_readStream = new Common::MemoryReadStream(dataBuffer, bufSize, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
uint32 EncryptedStream::read(byte *dataPtr, uint32 dataSize) {
|
||||
return _readStream->read(dataPtr, dataSize);
|
||||
}
|
||||
|
||||
int16 EncryptedStream::readSint16LE() {
|
||||
return _readStream->readSint16LE();
|
||||
}
|
||||
|
||||
uint32 EncryptedStream::readUint32LE() {
|
||||
return _readStream->readUint32LE();
|
||||
}
|
||||
|
||||
bool EncryptedStream::err() {
|
||||
return (_error || _readStream->err());
|
||||
}
|
||||
|
||||
bool EncryptedStream::eos() {
|
||||
return _readStream->eos();
|
||||
}
|
||||
|
||||
bool EncryptedStream::seek(int32 offset) {
|
||||
return _readStream->seek(offset);
|
||||
}
|
||||
|
||||
Common::String EncryptedStream::readLine() {
|
||||
_lineCount++;
|
||||
Common::String line = _readStream->readLine();
|
||||
if (!line.empty() && (line[0] == ';' || line[0] == '.' || line[0] == '*'))
|
||||
line.clear(); // Returns an empty string, if the line is invalid.
|
||||
return line;
|
||||
}
|
||||
|
||||
int32 EncryptedStream::size() {
|
||||
return _readStream->size();
|
||||
}
|
||||
|
||||
int32 EncryptedStream::pos() {
|
||||
return _readStream->pos();
|
||||
}
|
||||
|
||||
EncryptedStream::~EncryptedStream() {
|
||||
delete _readStream;
|
||||
}
|
||||
|
||||
const char *const EncryptedStream::kIdTab[] = {
|
||||
"[near]", "[mtake]", "[ftake]", "[phase]", "[seq]",
|
||||
"Name", "Type", "Front", "East",
|
||||
"Portable", "Transparent",
|
||||
nullptr
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
129
engines/cge2/fileio.h
Normal file
129
engines/cge2/fileio.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_FILEIO_H
|
||||
#define CGE2_FILEIO_H
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kBtSize 2048
|
||||
#define kBtKeySize 13
|
||||
#define kBtLevel 2
|
||||
#define kBtInnerCount ((kBtSize - 4 /*sizeof(Header) */) / (kBtKeySize + 2 /*sizeof(Inner) */))
|
||||
#define kBtLeafCount ((kBtSize - 4 /*sizeof(Header) */) / (kBtKeySize + 4 + 4 /*sizeof(BtKeypack) */))
|
||||
#define kBtValNone 0xFFFF
|
||||
#define kBtValRoot 0
|
||||
#define kCatName "VOL.CAT"
|
||||
#define kDatName "VOL.DAT"
|
||||
#define kCryptSeed 0xA5
|
||||
|
||||
enum ID {
|
||||
kIdNear, kIdMTake, kIdFTake, kIdPhase, kIdSeq,
|
||||
kIdName, kIdType, kIdFront, kIdEast,
|
||||
kIdPortable, kIdTransparent,
|
||||
kIdNone = -1
|
||||
};
|
||||
|
||||
struct BtKeypack {
|
||||
char _key[kBtKeySize];
|
||||
uint32 _pos;
|
||||
uint32 _size;
|
||||
};
|
||||
|
||||
struct Inner {
|
||||
uint8 _key[kBtKeySize];
|
||||
uint16 _down;
|
||||
};
|
||||
|
||||
struct Header {
|
||||
uint16 _count;
|
||||
uint16 _down;
|
||||
};
|
||||
|
||||
struct BtPage {
|
||||
Header _header;
|
||||
union {
|
||||
// dummy filler to make proper size of union
|
||||
uint8 _data[kBtSize - 4]; /* 4 is the size of struct Header */
|
||||
// inner version of data: key + word-sized page link
|
||||
Inner _inner[kBtInnerCount];
|
||||
// leaf version of data: key + all user data
|
||||
BtKeypack _leaf[kBtLeafCount];
|
||||
};
|
||||
|
||||
void readBTree(Common::ReadStream &s);
|
||||
};
|
||||
|
||||
class ResourceManager {
|
||||
private:
|
||||
struct {
|
||||
BtPage *_page;
|
||||
uint16 _pageNo;
|
||||
int _index;
|
||||
} _buff[kBtLevel];
|
||||
|
||||
BtPage *getPage(int level, uint16 pageId);
|
||||
uint16 catRead(byte *buf, uint16 length);
|
||||
Common::File *_catFile;
|
||||
Common::File *_datFile;
|
||||
void xCrypt(byte *buf, uint16 length);
|
||||
public:
|
||||
ResourceManager();
|
||||
~ResourceManager();
|
||||
uint16 read(byte *buf, uint16 length);
|
||||
bool seek(int32 offs, int whence = SEEK_SET);
|
||||
|
||||
BtKeypack *find(const char *key);
|
||||
bool exist(const char *name);
|
||||
};
|
||||
|
||||
class EncryptedStream {
|
||||
private:
|
||||
Common::SeekableReadStream *_readStream;
|
||||
int _lineCount;
|
||||
bool _error;
|
||||
public:
|
||||
EncryptedStream(ResourceManager *resman, const char *name);
|
||||
~EncryptedStream();
|
||||
bool err();
|
||||
bool eos();
|
||||
bool seek(int32 offset);
|
||||
int32 pos();
|
||||
int32 size();
|
||||
uint32 read(byte *dataPtr, uint32 dataSize);
|
||||
int16 readSint16LE();
|
||||
uint32 readUint32LE();
|
||||
Common::String readLine();
|
||||
int getLineCount() { return _lineCount; }
|
||||
|
||||
static const char *const kIdTab[];
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_FILEIO_H
|
||||
44
engines/cge2/general.h
Normal file
44
engines/cge2/general.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_GENERAL_H
|
||||
#define CGE2_GENERAL_H
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class CGE2Engine;
|
||||
|
||||
struct Dac {
|
||||
uint8 _r;
|
||||
uint8 _g;
|
||||
uint8 _b;
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_GENERAL_H
|
||||
624
engines/cge2/hero.cpp
Normal file
624
engines/cge2/hero.cpp
Normal file
@@ -0,0 +1,624 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/hero.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/map.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
Hero::Hero(CGE2Engine *vm) : Sprite(vm), _contact(nullptr), _dir(kNoDir),
|
||||
_curDim(0), _tracePtr(-1), _ignoreMap(false), _maxDist(0) {
|
||||
|
||||
for (int i = 0; i < kDimMax; i++)
|
||||
_dim[i] = nullptr;
|
||||
|
||||
_reachStart = _reachCycle = _sayStart = _funStart = 0;
|
||||
_funDel0 = _funDel = 0;
|
||||
}
|
||||
|
||||
Hero::~Hero() {
|
||||
contract();
|
||||
}
|
||||
|
||||
Sprite *Hero::expand() {
|
||||
if (_ext)
|
||||
return this;
|
||||
|
||||
char fname[kMaxPath];
|
||||
_vm->mergeExt(fname, _file, kSprExt);
|
||||
|
||||
if (_ext != nullptr)
|
||||
delete _ext;
|
||||
|
||||
_ext = new SprExt(_vm);
|
||||
|
||||
if (!*_file)
|
||||
return this;
|
||||
|
||||
for (int i = 0; i < kDimMax; i++) {
|
||||
if (_dim[i] != nullptr) {
|
||||
delete[] _dim[i];
|
||||
_dim[i] = nullptr;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kDimMax; i++) {
|
||||
_dim[i] = new Bitmap[_shpCnt];
|
||||
for (int j = 0; j < _shpCnt; j++)
|
||||
_dim[i][j].setVM(_vm);
|
||||
}
|
||||
|
||||
int cnt[kActions];
|
||||
|
||||
for (int i = 0; i < kActions; i++)
|
||||
cnt[i] = 0;
|
||||
|
||||
for (int i = 0; i < kActions; i++) {
|
||||
byte n = _actionCtrl[i]._cnt;
|
||||
if (n)
|
||||
_ext->_actions[i] = new CommandHandler::Command[n];
|
||||
else
|
||||
_ext->_actions[i] = nullptr;
|
||||
}
|
||||
|
||||
Seq *curSeq = nullptr;
|
||||
if (_seqCnt)
|
||||
curSeq = new Seq[_seqCnt];
|
||||
|
||||
if (_vm->_resman->exist(fname)) { // sprite description file exist
|
||||
EncryptedStream sprf(_vm->_resman, fname);
|
||||
if (sprf.err())
|
||||
error("Bad SPR [%s]", fname);
|
||||
|
||||
ID section = kIdPhase;
|
||||
ID id;
|
||||
Common::String line;
|
||||
char tmpStr[kLineMax + 1];
|
||||
int shpcnt = 0;
|
||||
int seqcnt = 0;
|
||||
int maxnow = 0;
|
||||
int maxnxt = 0;
|
||||
|
||||
for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) {
|
||||
if (line.empty())
|
||||
continue;
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
|
||||
char *p = _vm->token(tmpStr);
|
||||
|
||||
id = _vm->ident(p);
|
||||
switch (id) {
|
||||
case kIdNear:
|
||||
case kIdMTake:
|
||||
case kIdFTake:
|
||||
case kIdPhase:
|
||||
case kIdSeq:
|
||||
section = id;
|
||||
break;
|
||||
case kIdName:
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
for (p = tmpStr; *p != '='; p++) // We search for the =
|
||||
;
|
||||
setName(_vm->tail(p));
|
||||
break;
|
||||
default:
|
||||
if (id >= kIdNear)
|
||||
break;
|
||||
Seq *s;
|
||||
switch (section) {
|
||||
case kIdNear:
|
||||
case kIdMTake:
|
||||
case kIdFTake:
|
||||
id = (ID)_vm->_commandHandler->getComId(p);
|
||||
if (_actionCtrl[section]._cnt) {
|
||||
CommandHandler::Command *c = &_ext->_actions[section][cnt[section]++];
|
||||
c->_commandType = CommandType(id);
|
||||
c->_ref = _vm->number(nullptr);
|
||||
c->_val = _vm->number(nullptr);
|
||||
c->_spritePtr = nullptr;
|
||||
}
|
||||
break;
|
||||
case kIdSeq:
|
||||
s = &curSeq[seqcnt++];
|
||||
s->_now = atoi(p);
|
||||
if (s->_now > maxnow)
|
||||
maxnow = s->_now;
|
||||
s->_next = _vm->number(nullptr);
|
||||
switch (s->_next) {
|
||||
case 0xFF:
|
||||
s->_next = seqcnt;
|
||||
break;
|
||||
case 0xFE:
|
||||
s->_next = seqcnt - 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (s->_next > maxnxt)
|
||||
maxnxt = s->_next;
|
||||
s->_dx = _vm->number(nullptr);
|
||||
s->_dy = _vm->number(nullptr);
|
||||
s->_dz = _vm->number(nullptr);
|
||||
s->_dly = _vm->number(nullptr);
|
||||
break;
|
||||
case kIdPhase:
|
||||
for (int i = 0; i < kDimMax; i++) {
|
||||
char *q = p;
|
||||
q[1] = '0' + i;
|
||||
Bitmap b(_vm, q);
|
||||
_dim[i][shpcnt] = b;
|
||||
if (!shpcnt)
|
||||
_hig[i] = b._h;
|
||||
}
|
||||
++shpcnt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (curSeq) {
|
||||
if (maxnow >= shpcnt)
|
||||
error("Bad PHASE in SEQ %s", fname);
|
||||
if (maxnxt >= seqcnt)
|
||||
error("Bad JUMP in SEQ %s", fname);
|
||||
setSeq(curSeq);
|
||||
} else
|
||||
setSeq(_stdSeq8);
|
||||
|
||||
setShapeList(_dim[0], shpcnt);
|
||||
}
|
||||
|
||||
char *tempStr = _vm->_text->getText(_ref + 100);
|
||||
size_t ln = strlen(tempStr) + 1;
|
||||
char *text = new char[ln];
|
||||
Common::strcpy_s(text, ln, tempStr);
|
||||
_reachStart = atoi(_vm->token(text));
|
||||
_reachCycle = atoi(_vm->token(nullptr));
|
||||
_sayStart = atoi(_vm->token(nullptr));
|
||||
_funStart = atoi(_vm->token(nullptr));
|
||||
_funDel = _funDel0 = (72 / _ext->_seq[0]._dly) * atoi(_vm->token(nullptr));
|
||||
delete[] text;
|
||||
|
||||
int i = stepSize() / 2;
|
||||
_maxDist = (int)sqrt(double(i * i * 2));
|
||||
setCurrent();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Sprite *Hero::contract() {
|
||||
for (int i = 0; i < kDimMax; i++) {
|
||||
if (_dim[i] != nullptr) {
|
||||
delete[] _dim[i];
|
||||
if (_ext->_shpList == _dim[i])
|
||||
_ext->_shpList = nullptr;
|
||||
_dim[i] = nullptr;
|
||||
}
|
||||
}
|
||||
Sprite::contract();
|
||||
return this;
|
||||
}
|
||||
|
||||
void Hero::setCurrent() {
|
||||
FXP m = _vm->_eye->_z / (_pos3D._z - _vm->_eye->_z);
|
||||
FXP tmp = m * _siz.y;
|
||||
int h = -(tmp.trunc());
|
||||
|
||||
int i = 0;
|
||||
for (; i < kDimMax - 1; i++) {
|
||||
if (h >= (_hig[i] + _hig[i + 1]) / 2)
|
||||
break;
|
||||
}
|
||||
|
||||
_ext->_shpList = _dim[_curDim = i];
|
||||
}
|
||||
|
||||
void Hero::hStep() {
|
||||
if (!_ignoreMap && _ext) {
|
||||
Seq *seq = _ext->_seq;
|
||||
int ptr = seq[_seqPtr]._next;
|
||||
seq += ptr;
|
||||
if (seq->_dx | seq->_dz) {
|
||||
V2D p0(_vm, _pos3D._x.round(), _pos3D._z.round());
|
||||
V2D p1(_vm, p0.x + seq->_dx, p0.y + seq->_dz);
|
||||
if (mapCross(p0, p1)) {
|
||||
park();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
step();
|
||||
}
|
||||
|
||||
Sprite *Hero::setContact() {
|
||||
Sprite *spr;
|
||||
int md = _maxDist << 1;
|
||||
for (spr = _vm->_vga->_showQ->first(); spr; spr = spr->_next) {
|
||||
if (spr->_actionCtrl[kNear]._cnt && ((spr->_ref & 255) != 255) && (distance(spr) <= md)) {
|
||||
if (spr == _contact)
|
||||
return nullptr;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (_contact = spr);
|
||||
}
|
||||
|
||||
void Hero::tick() {
|
||||
int z = _pos3D._z.trunc();
|
||||
//-- maybe not exactly wid/2, but wid/3 ?
|
||||
int d = ((_siz.x / 2) * _vm->_eye->_z.trunc()) / (_vm->_eye->_z.trunc() - z);
|
||||
|
||||
if (_dir != kNoDir) { // just walking...
|
||||
if (_flags._hold || _tracePtr < 0)
|
||||
park();
|
||||
else {
|
||||
Sprite *spr = setContact();
|
||||
if (spr)
|
||||
_vm->feedSnail(spr, kNear, this);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
if (_tracePtr >= 0) {
|
||||
if (distance(_trace[_tracePtr]) <= _maxDist)
|
||||
--_tracePtr;
|
||||
|
||||
if (_tracePtr < 0)
|
||||
park();
|
||||
else {
|
||||
int stp = stepSize() / 2;
|
||||
int dx = _trace[_tracePtr]._x.round() - _pos3D._x.round();
|
||||
int dz = _trace[_tracePtr]._z.round() - _pos3D._z.round();
|
||||
Dir dir = (dx > stp) ? kEE : ((-dx > stp) ? kWW : ((dz > stp) ? kNN : kSS));
|
||||
turn(dir);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
hStep();
|
||||
setCurrent();
|
||||
switch (_dir) {
|
||||
case kSS:
|
||||
if (_pos3D._z < stepSize() / 2)
|
||||
park();
|
||||
break;
|
||||
case kWW:
|
||||
if (_pos2D.x <= d)
|
||||
park();
|
||||
break;
|
||||
case kNN:
|
||||
if (_pos3D._z > kScrDepth)
|
||||
park();
|
||||
break;
|
||||
case kEE:
|
||||
if (_pos2D.x >= kScrWidth - 1 - d)
|
||||
park();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (_flags._trim)
|
||||
gotoxyz_(_pos2D);
|
||||
|
||||
if (_pos3D._z.trunc() != z)
|
||||
_flags._zmov = true;
|
||||
|
||||
if (--_funDel == 0)
|
||||
fun();
|
||||
}
|
||||
|
||||
int Hero::distance(V3D pos) {
|
||||
V3D di = _pos3D - pos;
|
||||
int x = di._x.round();
|
||||
int z = di._z.round();
|
||||
int retval = (int)sqrt((double)x * x + z * z);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int Hero::distance(Sprite *spr) {
|
||||
V3D pos = spr->_pos3D;
|
||||
int mdx = (spr->_siz.x >> 1) + (_siz.x >> 1);
|
||||
int dx = (_pos3D._x - spr->_pos3D._x).round();
|
||||
if (dx < 0) {
|
||||
mdx = -mdx;
|
||||
if (dx > mdx)
|
||||
pos._x = _pos3D._x;
|
||||
else
|
||||
pos._x += mdx;
|
||||
} else if (dx < mdx)
|
||||
pos._x = _pos3D._x;
|
||||
else
|
||||
pos._x += mdx;
|
||||
|
||||
return distance(pos);
|
||||
}
|
||||
|
||||
void Hero::turn(Dir d) {
|
||||
Dir dir = (_dir == kNoDir) ? kSS : _dir;
|
||||
if (d != _dir) {
|
||||
step((d == dir) ? 57 : (8 + 4 * dir + d));
|
||||
_dir = d;
|
||||
}
|
||||
resetFun();
|
||||
}
|
||||
|
||||
void Hero::park() {
|
||||
if (_dir != kNoDir) {
|
||||
step(8 + 5 * _dir);
|
||||
_dir = kNoDir;
|
||||
_trace[0] = _pos3D;
|
||||
_tracePtr = -1;
|
||||
setCurrent();
|
||||
_flags._zmov = true;
|
||||
}
|
||||
_ignoreMap = false;
|
||||
if (_time == 0)
|
||||
++_time;
|
||||
}
|
||||
|
||||
bool Hero::lower(Sprite * spr) {
|
||||
return (spr->_pos3D._y + (spr->_siz.y >> 2) < 10);
|
||||
}
|
||||
|
||||
void Hero::reach(int mode) {
|
||||
Sprite *spr = nullptr;
|
||||
if (mode >= 4) {
|
||||
spr = _vm->_vga->_showQ->locate(mode);
|
||||
if (spr) {
|
||||
mode = !spr->_flags._east; // 0-1
|
||||
if (lower(spr)) // 2-3
|
||||
mode += 2;
|
||||
}
|
||||
}
|
||||
// note: insert SNAIL commands in reverse order
|
||||
_vm->_commandHandler->insertCommand(kCmdPause, -1, 24, nullptr);
|
||||
_vm->_commandHandler->insertCommand(kCmdSeq, -1, _reachStart + _reachCycle * mode, this);
|
||||
if (spr) {
|
||||
_vm->_commandHandler->insertCommand(kCmdWait, -1, -1, this);
|
||||
_vm->_commandHandler->insertCommand(kCmdWalk, -1, spr->_ref, this);
|
||||
}
|
||||
// sequence is not finished,
|
||||
// now it is just at sprite appear (disappear) point
|
||||
resetFun();
|
||||
}
|
||||
|
||||
void Hero::fun() {
|
||||
if (_vm->_commandHandler->idle()) {
|
||||
park();
|
||||
_vm->_commandHandler->addCommand(kCmdWait, -1, -1, this);
|
||||
_vm->_commandHandler->addCommand(kCmdSeq, -1, _funStart, this);
|
||||
}
|
||||
_funDel = _funDel0 >> 2;
|
||||
}
|
||||
|
||||
int Hero::len(V2D v) {
|
||||
return (int)sqrt(double(v.x * v.x + v.y * v.y));
|
||||
}
|
||||
|
||||
bool Hero::findWay(){
|
||||
V2D p0(_vm, _pos3D._x.round(), _pos3D._z.round());
|
||||
V2D p1(_vm, _trace[_tracePtr]._x.round(), _trace[_tracePtr]._z.round());
|
||||
V2D ph(_vm, p1.x, p0.y);
|
||||
V2D pv(_vm, p0.x, p1.y);
|
||||
bool pvOk = (!mapCross(p0, pv) && !mapCross(pv, p1));
|
||||
bool phOk = (!mapCross(p0, ph) && !mapCross(ph, p1));
|
||||
int md = (_maxDist >> 1);
|
||||
if (pvOk && (len(ph - p0) <= md || len(p1 - ph) <= md))
|
||||
return true;
|
||||
|
||||
if (phOk && (len(pv - p0) <= md || len(p1 - pv) <= md))
|
||||
return true;
|
||||
|
||||
if (pvOk) {
|
||||
_trace[++_tracePtr] = V3D(pv.x, 0, pv.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (phOk) {
|
||||
_trace[++_tracePtr] = V3D(ph.x, 0, ph.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Hero::snap(int p, int q, int grid) {
|
||||
int d = abs(q - p) % grid;
|
||||
if (d > (grid >> 1))
|
||||
d -= grid;
|
||||
return (q >= p) ? (q - d) : (q + d);
|
||||
}
|
||||
|
||||
void Hero::walkTo(V3D pos) {
|
||||
if (distance(pos) <= _maxDist)
|
||||
return;
|
||||
|
||||
int stp = stepSize();
|
||||
pos._x = snap(_pos3D._x.round(), pos._x.round(), stp);
|
||||
pos._y = 0;
|
||||
pos._z = snap(_pos3D._z.round(), pos._z.round(), stp);
|
||||
|
||||
V2D p0(_vm, _pos3D._x.round(), _pos3D._z.round());
|
||||
V2D p1(_vm, pos._x.round(), pos._z.round());
|
||||
resetFun();
|
||||
int cnt = mapCross(p0, p1);
|
||||
if ((cnt & 1) == 0) { // even == way exists
|
||||
_trace[_tracePtr = 0] = pos;
|
||||
if (!findWay()) {
|
||||
int i;
|
||||
++_tracePtr;
|
||||
for (i = stp; i < kMaxTry; i += stp) {
|
||||
_trace[_tracePtr] = pos + V3D(i, 0, 0);
|
||||
if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay())
|
||||
break;
|
||||
|
||||
_trace[_tracePtr] = pos + V3D(-i, 0, 0);
|
||||
if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay())
|
||||
break;
|
||||
|
||||
_trace[_tracePtr] = pos + V3D(0, 0, i);
|
||||
if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay())
|
||||
break;
|
||||
|
||||
_trace[_tracePtr] = pos + V3D(0, 0, -i);
|
||||
if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay())
|
||||
break;
|
||||
}
|
||||
if (i >= kMaxTry)
|
||||
_trace[_tracePtr] = V3D(_pos3D._x, 0, pos._z); // not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hero::walkTo(Sprite *spr) {
|
||||
int mdx = _siz.x >> 1;
|
||||
int stp = (stepSize() + 1) / 2;
|
||||
if (!spr->_flags._east)
|
||||
mdx = -mdx;
|
||||
walkTo(spr->_pos3D + V3D(mdx, 0, (!spr->_flags._frnt || spr->_pos3D._z < 8) ? stp : -stp));
|
||||
}
|
||||
|
||||
V3D Hero::screenToGround(V2D pos) {
|
||||
FXP z = _vm->_eye->_z + (_vm->_eye->_y * _vm->_eye->_z) / (FXP(pos.y) - _vm->_eye->_y);
|
||||
FXP x = _vm->_eye->_x - ((FXP(pos.x) - _vm->_eye->_x) * (z - _vm->_eye->_z)) / _vm->_eye->_z;
|
||||
return V3D(x.round(), 0, z.round());
|
||||
}
|
||||
|
||||
int Hero::cross(const V2D &a, const V2D &b) {
|
||||
int x = _pos3D._x.trunc();
|
||||
int z = _pos3D._z.trunc();
|
||||
int r = ((_siz.x / 3) * _vm->_eye->_z.trunc()) / (_vm->_eye->_z.trunc() - z);
|
||||
return _vm->cross(a, b, V2D(_vm, x - r, z), V2D(_vm, x + r, z)) << 1;
|
||||
}
|
||||
|
||||
bool CGE2Engine::cross(const V2D &a, const V2D &b, const V2D &c, const V2D &d) {
|
||||
if (contain(a, b, c) || contain(a, b, d) || contain(c, d, a) || contain(c, d, b))
|
||||
return true;
|
||||
|
||||
return sgn(det(a, b, c)) != sgn(det(a, b, d)) && sgn(det(c, d, a)) != sgn(det(c, d, b));
|
||||
}
|
||||
|
||||
bool CGE2Engine::contain(const V2D &a, const V2D &b, const V2D &p) {
|
||||
if (det(a, b, p))
|
||||
return false;
|
||||
|
||||
return ((long)(a.x - p.x) * (p.x - b.x) >= 0 && (long)(a.y - p.y) * (p.y - b.y) >= 0);
|
||||
}
|
||||
|
||||
long CGE2Engine::det(const V2D &a, const V2D &b, const V2D &c) {
|
||||
return ((long)a.x * b.y + (long)b.x * c.y + (long)c.x * a.y) - ((long)c.x * b.y + (long)b.x * a.y + (long)a.x * c.y);
|
||||
}
|
||||
|
||||
int CGE2Engine::sgn(long n) {
|
||||
return (n == 0) ? 0 : ((n > 0) ? 1 : -1);
|
||||
}
|
||||
|
||||
int Hero::mapCross(const V2D &a, const V2D &b) {
|
||||
Hero *o = other();
|
||||
int n = (o->_scene == _scene) ? o->cross(a, b) : 0;
|
||||
if (!_ignoreMap)
|
||||
n += _vm->mapCross(a, b);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int Hero::mapCross(const V3D &a, const V3D &b) {
|
||||
return mapCross(V2D(_vm, a._x.round(), a._z.round()), V2D(_vm, b._x.round(), b._z.round()));
|
||||
}
|
||||
|
||||
int CGE2Engine::mapCross(const V2D &a, const V2D &b) {
|
||||
int cnt = 0;
|
||||
V2D *n0 = nullptr;
|
||||
V2D *p = nullptr;
|
||||
for (int i = 0; i < _map->size(); i++) {
|
||||
V2D *n = _map->getCoord(i);
|
||||
if (p) {
|
||||
if (cross(a, b, *n0, *n))
|
||||
++cnt;
|
||||
|
||||
if (*n == *p)
|
||||
p = nullptr;
|
||||
} else {
|
||||
p = n;
|
||||
}
|
||||
n0 = n;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
void Hero::setScene(int c) {
|
||||
Sprite::setScene(c);
|
||||
resetFun();
|
||||
}
|
||||
|
||||
void Hero::operator++() {
|
||||
if (_curDim > 0)
|
||||
_ext->_shpList = _dim[--_curDim];
|
||||
}
|
||||
|
||||
void Hero::operator--() {
|
||||
if (_curDim < kDimMax - 1)
|
||||
_ext->_shpList = _dim[++_curDim];
|
||||
}
|
||||
|
||||
bool Sprite::works(Sprite *spr) {
|
||||
if (!spr || !spr->_ext)
|
||||
return false;
|
||||
|
||||
bool ok = false;
|
||||
|
||||
Action a = _vm->_heroTab[_vm->_sex]->_ptr->action();
|
||||
CommandHandler::Command *ct = spr->_ext->_actions[a];
|
||||
if (ct) {
|
||||
int i = spr->_actionCtrl[a]._ptr;
|
||||
int n = spr->_actionCtrl[a]._cnt;
|
||||
while (i < n && !ok) {
|
||||
CommandHandler::Command *c = &ct[i++];
|
||||
if (c->_commandType != kCmdUse)
|
||||
break;
|
||||
ok = (c->_ref == _ref);
|
||||
if (c->_val > 255) {
|
||||
if (ok) {
|
||||
int p = spr->labVal(a, c->_val >> 8);
|
||||
if (p >= 0)
|
||||
spr->_actionCtrl[a]._ptr = p;
|
||||
else
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
if (c->_val && c->_val != _vm->_now)
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
113
engines/cge2/hero.h
Normal file
113
engines/cge2/hero.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_HERO_H
|
||||
#define CGE2_HERO_H
|
||||
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/vga13h.h"
|
||||
#include "cge2/snail.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kMaxTry 400
|
||||
|
||||
class Hero;
|
||||
|
||||
struct HeroTab {
|
||||
Hero *_ptr;
|
||||
Sprite *_face;
|
||||
Sprite *_pocket[kPocketMax + 1];
|
||||
int _downPocketId[kPocketMax + 1];
|
||||
int _pocPtr;
|
||||
V2D *_posTab[kSceneMax];
|
||||
HeroTab(CGE2Engine *vm) {
|
||||
_ptr = nullptr;
|
||||
_face = nullptr;
|
||||
for (int i = 0; i < kPocketMax + 1; i++) {
|
||||
_pocket[i] = nullptr;
|
||||
_downPocketId[i] = -1;
|
||||
}
|
||||
_pocPtr = 0;
|
||||
for (int i = 0; i < kSceneMax; i++)
|
||||
_posTab[i] = nullptr;
|
||||
}
|
||||
~HeroTab() {
|
||||
for (int i = 0; i < kSceneMax; i++)
|
||||
delete _posTab[i];
|
||||
}
|
||||
};
|
||||
|
||||
class Hero : public Sprite {
|
||||
int _hig[kDimMax];
|
||||
Sprite *_contact;
|
||||
public:
|
||||
BitmapPtr _dim[kDimMax];
|
||||
V3D _trace[kWayMax];
|
||||
enum Dir { kNoDir = -1, kSS, kWW, kNN, kEE } _dir;
|
||||
int _curDim;
|
||||
int _tracePtr;
|
||||
int _reachStart, _reachCycle, _sayStart, _funStart;
|
||||
int _funDel0, _funDel;
|
||||
int _maxDist;
|
||||
bool _ignoreMap;
|
||||
Hero(CGE2Engine *vm);
|
||||
~Hero() override;
|
||||
void tick() override;
|
||||
Sprite *expand() override;
|
||||
Sprite *contract() override;
|
||||
Sprite *setContact();
|
||||
int stepSize() { return _ext->_seq[7]._dx; }
|
||||
int distance(V3D pos);
|
||||
int distance(Sprite * spr);
|
||||
void turn(Dir d);
|
||||
void park();
|
||||
int len(V2D v);
|
||||
bool findWay();
|
||||
static int snap(int p, int q, int grid);
|
||||
void walkTo(V3D pos);
|
||||
void walkTo(V2D pos) { walkTo(screenToGround(pos)); }
|
||||
V3D screenToGround(V2D pos);
|
||||
void walkTo(Sprite *spr);
|
||||
void say() { step(_sayStart); }
|
||||
void fun();
|
||||
void resetFun() { _funDel = _funDel0; }
|
||||
void hStep();
|
||||
bool lower(Sprite * spr);
|
||||
int cross(const V2D &a, const V2D &b);
|
||||
int mapCross(const V2D &a, const V2D &b);
|
||||
int mapCross(const V3D &a, const V3D &b);
|
||||
Hero *other() { return _vm->_heroTab[!(_ref & 1)]->_ptr;}
|
||||
Action action() { return (Action)(_ref % 10); }
|
||||
void reach(int mode);
|
||||
void setCurrent();
|
||||
void setScene(int c) override;
|
||||
void operator++();
|
||||
void operator--();
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_HERO_H
|
||||
103
engines/cge2/inventory.cpp
Normal file
103
engines/cge2/inventory.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/hero.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
int CGE2Engine::findActivePocket(int ref) {
|
||||
for (int i = 0; i < kPocketMax; i++) {
|
||||
Sprite *spr = _heroTab[_sex]->_pocket[i];
|
||||
if (ref >= 0) {
|
||||
if (spr && (spr->_ref == ref))
|
||||
return i;
|
||||
} else if (!spr)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CGE2Engine::selectPocket(int n) {
|
||||
Sprite **p = _heroTab[_sex]->_pocket;
|
||||
int &pp = _heroTab[_sex]->_pocPtr;
|
||||
if ((n < 0) || (pp == n)) {
|
||||
n = findActivePocket(-1);
|
||||
if (n >= 0)
|
||||
pp = n;
|
||||
} else if (p[n])
|
||||
pp = n;
|
||||
}
|
||||
|
||||
void CGE2Engine::pocFul() {
|
||||
Hero *h = _heroTab[_sex]->_ptr;
|
||||
h->park();
|
||||
_commandHandler->addCommand(kCmdWait, -1, -1, h);
|
||||
_commandHandler->addCommand(kCmdSound, -1, 2, h);
|
||||
_commandHandler->addCommand(kCmdSay, -1, kPocketFull + _sex, h);
|
||||
}
|
||||
|
||||
void CGE2Engine::releasePocket(Sprite *spr) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kPocketMax; j++) {
|
||||
Sprite *&poc = _heroTab[i]->_pocket[j];
|
||||
if (poc == spr) {
|
||||
spr->_flags._kept = false;
|
||||
poc = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CGE2Engine::freePockets(int sx) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < kPocketMax; i++){
|
||||
if (_heroTab[sx]->_pocket[i] == nullptr)
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void CGE2Engine::openPocket() {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kPocketMax + 1; j++) {
|
||||
int ref = (int)_heroTab[i]->_downPocketId[j];
|
||||
_heroTab[i]->_pocket[j] = (ref == -1) ? nullptr : _vga->_showQ->locate(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::closePocket() {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kPocketMax + 1; j++) {
|
||||
Sprite *spr = _heroTab[i]->_pocket[j];
|
||||
_heroTab[i]->_downPocketId[j] = (spr) ? spr->_ref : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
91
engines/cge2/map.cpp
Normal file
91
engines/cge2/map.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/map.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
Map::Map(CGE2Engine *vm) :_vm(vm) {}
|
||||
|
||||
Map::~Map() {
|
||||
_container.clear();
|
||||
}
|
||||
|
||||
void Map::clear() {
|
||||
_container.clear();
|
||||
}
|
||||
|
||||
void Map::load(int scene) {
|
||||
clear();
|
||||
|
||||
const char *fname = "%.2d.MAP";
|
||||
Common::String fileName = Common::String::format(fname, scene);
|
||||
if (!_vm->_resman->exist(fileName.c_str()))
|
||||
return;
|
||||
|
||||
EncryptedStream file(_vm->_resman, fileName.c_str());
|
||||
|
||||
Common::String line;
|
||||
for (line = file.readLine(); !file.eos(); line = file.readLine()) {
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
char tmpStr[kLineMax + 1];
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
|
||||
char *currPos = tmpStr;
|
||||
int x = nextNum(currPos);
|
||||
while (true) {
|
||||
int y = nextNum(nullptr);
|
||||
_container.push_back(V2D(_vm, convertCoord(x), convertCoord(y)));
|
||||
x = nextNum(nullptr);
|
||||
if (x == -1) // We stop if there are no more data left to process in the current line.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Map::nextNum(char *currPos) {
|
||||
currPos = strtok(currPos, " (),");
|
||||
if (currPos == nullptr)
|
||||
return -1;
|
||||
int num = atoi(currPos);
|
||||
return num;
|
||||
}
|
||||
|
||||
int Map::convertCoord(int coord) {
|
||||
return (coord + (kMapGrid >> 1)) & kMapMask;
|
||||
}
|
||||
|
||||
int Map::size() {
|
||||
return _container.size();
|
||||
}
|
||||
|
||||
V2D *Map::getCoord(int idx) {
|
||||
return &_container[idx];
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
54
engines/cge2/map.h
Normal file
54
engines/cge2/map.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_MAP_H
|
||||
#define CGE2_MAP_H
|
||||
|
||||
#include "cge2/vga13h.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kMapGrid 4
|
||||
#define kMapMask (~(kMapGrid - 1))
|
||||
|
||||
class Map {
|
||||
CGE2Engine *_vm;
|
||||
Common::Array<V2D> _container;
|
||||
|
||||
int convertCoord(int coord);
|
||||
int nextNum(char *currPos);
|
||||
public:
|
||||
Map(CGE2Engine *vm);
|
||||
~Map();
|
||||
void clear();
|
||||
void load(int scene);
|
||||
int size();
|
||||
V2D *getCoord(int idx);
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_MAP_H
|
||||
270
engines/cge2/metaengine.cpp
Normal file
270
engines/cge2/metaengine.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/detection.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_COLOR_BLIND_DEFAULT_OFF,
|
||||
{
|
||||
_s("Color Blind Mode"),
|
||||
_s("Enable Color Blind Mode by default"),
|
||||
"enable_color_blind",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef USE_TTS
|
||||
{
|
||||
GAMEOPTION_TTS_OBJECTS,
|
||||
{
|
||||
_s("Enable Text to Speech for Objects and Options"),
|
||||
_s("Use TTS to read the descriptions (if TTS is available)"),
|
||||
"tts_enabled_objects",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_TTS_SPEECH,
|
||||
{
|
||||
_s("Enable Text to Speech for Subtitles"),
|
||||
_s("Use TTS to read the subtitles (if TTS is available)"),
|
||||
"tts_enabled_speech",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
#endif
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
class CGE2MetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "cge2";
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return optionsList;
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
Common::Error CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||
*engine = new CGE2::CGE2Engine(syst, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool CGE2MetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportCreationDate) ||
|
||||
(f == kSavesSupportPlayTime) ||
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSimpleSavesNames);
|
||||
}
|
||||
|
||||
int CGE2MetaEngine::getMaximumSaveSlot() const {
|
||||
return 99;
|
||||
}
|
||||
|
||||
SaveStateList CGE2MetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::StringArray filenames;
|
||||
Common::String pattern = target;
|
||||
pattern += ".###";
|
||||
|
||||
filenames = saveFileMan->listSavefiles(pattern);
|
||||
|
||||
SaveStateList saveList;
|
||||
for (const auto &filename : filenames) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(filename.c_str() + filename.size() - 3);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 99) {
|
||||
|
||||
Common::InSaveFile *file = saveFileMan->openForLoading(filename);
|
||||
if (file) {
|
||||
CGE2::SavegameHeader header;
|
||||
|
||||
// Check to see if it's a ScummVM savegame or not
|
||||
char buffer[kSavegameStrSize + 1];
|
||||
file->read(buffer, kSavegameStrSize + 1);
|
||||
|
||||
if (!strncmp(buffer, kSavegameStr, kSavegameStrSize + 1)) {
|
||||
// Valid savegame
|
||||
if (CGE2::CGE2Engine::readSavegameHeader(file, header)) {
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, header.saveName));
|
||||
}
|
||||
} else {
|
||||
// Must be an original format savegame
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, "Unknown"));
|
||||
}
|
||||
|
||||
delete file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
SaveStateDescriptor CGE2MetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::String fileName = Common::String::format("%s.%03d", target, slot);
|
||||
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
|
||||
|
||||
if (f) {
|
||||
CGE2::SavegameHeader header;
|
||||
|
||||
// Check to see if it's a ScummVM savegame or not
|
||||
char buffer[kSavegameStrSize + 1];
|
||||
f->read(buffer, kSavegameStrSize + 1);
|
||||
|
||||
bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) &&
|
||||
CGE2::CGE2Engine::readSavegameHeader(f, header, false);
|
||||
delete f;
|
||||
|
||||
if (!hasHeader) {
|
||||
// Original savegame perhaps?
|
||||
SaveStateDescriptor desc(this, slot, "Unknown");
|
||||
return desc;
|
||||
} else {
|
||||
// Create the return descriptor
|
||||
SaveStateDescriptor desc(this, slot, header.saveName);
|
||||
desc.setThumbnail(header.thumbnail);
|
||||
desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay);
|
||||
desc.setSaveTime(header.saveHour, header.saveMinutes);
|
||||
|
||||
if (header.playTime) {
|
||||
desc.setPlayTime(header.playTime * 1000);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
bool CGE2MetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
Common::String fileName = Common::String::format("%s.%03d", target, slot);
|
||||
return g_system->getSavefileManager()->removeSavefile(fileName);
|
||||
}
|
||||
|
||||
Common::KeymapArray CGE2MetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
|
||||
Keymap *keymap = new Keymap(Keymap::kKeymapTypeGame, "Sfinx", _("Game Keymappings"));
|
||||
|
||||
Common::Action *act;
|
||||
|
||||
act = new Common::Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
keymap->addAction(act);
|
||||
|
||||
act = new Common::Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
keymap->addAction(act);
|
||||
|
||||
// I18N: This closes the Dialog/text box.
|
||||
act = new Common::Action("CLOSEBOX", _("Close the dialog box"));
|
||||
act->setCustomEngineActionEvent(kActionEscape);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
keymap->addAction(act);
|
||||
|
||||
act = new Common::Action(kStandardActionSave, _("Save game"));
|
||||
act->setCustomEngineActionEvent(kActionSave);
|
||||
act->addDefaultInputMapping("F5");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
keymap->addAction(act);
|
||||
|
||||
act = new Common::Action(kStandardActionLoad, _("Load game"));
|
||||
act->setCustomEngineActionEvent(kActionLoad);
|
||||
act->addDefaultInputMapping("F7");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
keymap->addAction(act);
|
||||
|
||||
// I18N: 3-4 dialogs of game version info, (translation) credits, etc.
|
||||
act = new Common::Action("INFO", _("Game info"));
|
||||
act->setCustomEngineActionEvent(kActionInfo);
|
||||
act->addDefaultInputMapping("F1");
|
||||
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
|
||||
keymap->addAction(act);
|
||||
|
||||
// I18N: This opens a Quit Prompt where you have to choose
|
||||
// [Confirm] or [Continue Playing] lines with Left Click.
|
||||
act = new Common::Action("QUIT", _("Quit prompt"));
|
||||
act->setCustomEngineActionEvent(kActionQuit);
|
||||
act->addDefaultInputMapping("A+x");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
|
||||
keymap->addAction(act);
|
||||
|
||||
return Keymap::arrayOf(keymap);
|
||||
}
|
||||
} // End of namespace CGE2
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(CGE2)
|
||||
REGISTER_PLUGIN_DYNAMIC(CGE2, PLUGIN_TYPE_ENGINE, CGE2::CGE2MetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(CGE2, PLUGIN_TYPE_ENGINE, CGE2::CGE2MetaEngine);
|
||||
#endif
|
||||
40
engines/cge2/module.mk
Normal file
40
engines/cge2/module.mk
Normal file
@@ -0,0 +1,40 @@
|
||||
MODULE := engines/cge2
|
||||
|
||||
MODULE_OBJS = \
|
||||
cge2.o \
|
||||
vga13h.o \
|
||||
bitmap.o \
|
||||
fileio.o \
|
||||
sound.o \
|
||||
cge2_main.o \
|
||||
text.o \
|
||||
hero.o \
|
||||
snail.o \
|
||||
spare.o \
|
||||
talk.o \
|
||||
events.o \
|
||||
map.o \
|
||||
metaengine.o \
|
||||
vmenu.o \
|
||||
saveload.o \
|
||||
toolbar.o \
|
||||
inventory.o \
|
||||
console.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_CGE2), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
|
||||
# Skip building the following objects if a static
|
||||
# module is enabled, because it already has the contents.
|
||||
ifneq ($(ENABLE_CGE2), STATIC_PLUGIN)
|
||||
# External dependencies for detection.
|
||||
DETECT_OBJS += $(MODULE)/fileio.o
|
||||
endif
|
||||
280
engines/cge2/saveload.cpp
Normal file
280
engines/cge2/saveload.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/thumbnail.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/paletteman.h"
|
||||
#include "graphics/scaler.h"
|
||||
#include "cge2/events.h"
|
||||
#include "cge2/snail.h"
|
||||
#include "cge2/hero.h"
|
||||
#include "cge2/text.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kSavegameCheckSum (1997 + _now + _sex + kWorldHeight)
|
||||
#define kBadSVG 99
|
||||
|
||||
bool CGE2Engine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
return (_gamePhase == kPhaseInGame) && _mouse->_active &&
|
||||
_commandHandler->idle() && (_soundStat._wait == nullptr);
|
||||
}
|
||||
|
||||
Common::Error CGE2Engine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
storeHeroPos();
|
||||
saveGame(slot, desc);
|
||||
sceneUp(_now);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void CGE2Engine::saveGame(int slotNumber, const Common::String &desc) {
|
||||
// Set up the serializer
|
||||
Common::String slotName = getSaveStateName(slotNumber);
|
||||
Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName);
|
||||
|
||||
// Write out the ScummVM savegame header
|
||||
SavegameHeader header;
|
||||
header.saveName = desc;
|
||||
header.version = kSavegameVersion;
|
||||
writeSavegameHeader(saveFile, header);
|
||||
|
||||
// Write out the data of the savegame
|
||||
sceneDown();
|
||||
syncGame(nullptr, saveFile);
|
||||
|
||||
// Finish writing out game data
|
||||
saveFile->finalize();
|
||||
delete saveFile;
|
||||
}
|
||||
|
||||
bool CGE2Engine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
return (_gamePhase == kPhaseInGame) && _mouse->_active;
|
||||
}
|
||||
|
||||
Common::Error CGE2Engine::loadGameState(int slot) {
|
||||
_commandHandler->clear();
|
||||
_commandHandlerTurbo->clear();
|
||||
sceneDown();
|
||||
if (!loadGame(slot))
|
||||
return Common::kReadingFailed;
|
||||
sceneUp(_now);
|
||||
initToolbar();
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool CGE2Engine::loadGame(int slotNumber) {
|
||||
Common::MemoryReadStream *readStream;
|
||||
|
||||
// Open up the savegame file
|
||||
Common::String slotName = getSaveStateName(slotNumber);
|
||||
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
|
||||
|
||||
// Read the data into a data buffer
|
||||
int size = saveFile->size();
|
||||
byte *dataBuffer = (byte *)malloc(size);
|
||||
saveFile->read(dataBuffer, size);
|
||||
readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
|
||||
delete saveFile;
|
||||
|
||||
// Check to see if it's a ScummVM savegame or not
|
||||
char buffer[kSavegameStrSize + 1];
|
||||
readStream->read(buffer, kSavegameStrSize + 1);
|
||||
|
||||
if (strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) != 0) {
|
||||
delete readStream;
|
||||
return false;
|
||||
} else {
|
||||
SavegameHeader saveHeader;
|
||||
|
||||
if (!readSavegameHeader(readStream, saveHeader)) {
|
||||
delete readStream;
|
||||
return false;
|
||||
}
|
||||
|
||||
g_engine->setTotalPlayTime(saveHeader.playTime * 1000);
|
||||
}
|
||||
|
||||
resetGame();
|
||||
|
||||
// Get in the savegame
|
||||
syncGame(readStream, nullptr);
|
||||
delete readStream;
|
||||
|
||||
loadHeroes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGE2Engine::resetGame() {
|
||||
_busyPtr = nullptr;
|
||||
busy(false);
|
||||
_spare->clear();
|
||||
_vga->_showQ->clear();
|
||||
loadScript("CGE.INI", true);
|
||||
delete _infoLine;
|
||||
_infoLine = new InfoLine(this, kInfoW);
|
||||
}
|
||||
|
||||
void CGE2Engine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) {
|
||||
// Write out a savegame header
|
||||
out->write(kSavegameStr, kSavegameStrSize + 1);
|
||||
|
||||
out->writeByte(kSavegameVersion);
|
||||
|
||||
// Write savegame name
|
||||
out->write(header.saveName.c_str(), header.saveName.size() + 1);
|
||||
|
||||
// Get the active palette
|
||||
uint8 thumbPalette[256 * 3];
|
||||
g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
|
||||
|
||||
// Stop the heroes from moving and redraw them before taking the picture.
|
||||
for (int i = 0; i < 2; i++)
|
||||
_heroTab[i]->_ptr->park();
|
||||
_vga->show();
|
||||
|
||||
// Create a thumbnail and save it
|
||||
Graphics::Surface *thumb = new Graphics::Surface();
|
||||
Graphics::Surface *s = _vga->_page[0];
|
||||
::createThumbnail(thumb, (const byte *)s->getPixels(), kScrWidth, kScrHeight, thumbPalette);
|
||||
Graphics::saveThumbnail(*out, *thumb);
|
||||
thumb->free();
|
||||
delete thumb;
|
||||
|
||||
// Write out the save date/time
|
||||
TimeDate td;
|
||||
g_system->getTimeAndDate(td);
|
||||
out->writeSint16LE(td.tm_year + 1900);
|
||||
out->writeSint16LE(td.tm_mon + 1);
|
||||
out->writeSint16LE(td.tm_mday);
|
||||
out->writeSint16LE(td.tm_hour);
|
||||
out->writeSint16LE(td.tm_min);
|
||||
|
||||
out->writeUint32LE(g_engine->getTotalPlayTime() / 1000);
|
||||
}
|
||||
|
||||
WARN_UNUSED_RESULT bool CGE2Engine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) {
|
||||
header.version = 0;
|
||||
header.saveName.clear();
|
||||
header.thumbnail = nullptr;
|
||||
header.saveYear = 0;
|
||||
header.saveMonth = 0;
|
||||
header.saveDay = 0;
|
||||
header.saveHour = 0;
|
||||
header.saveMinutes = 0;
|
||||
header.playTime = 0;
|
||||
|
||||
// Get the savegame version
|
||||
header.version = in->readByte();
|
||||
if (header.version > kSavegameVersion)
|
||||
return false;
|
||||
|
||||
// Read in the string
|
||||
char ch;
|
||||
while ((ch = (char)in->readByte()) != '\0')
|
||||
header.saveName += ch;
|
||||
|
||||
// Get the thumbnail
|
||||
if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in save date/time
|
||||
header.saveYear = in->readSint16LE();
|
||||
header.saveMonth = in->readSint16LE();
|
||||
header.saveDay = in->readSint16LE();
|
||||
header.saveHour = in->readSint16LE();
|
||||
header.saveMinutes = in->readSint16LE();
|
||||
|
||||
if (header.version >= 2) {
|
||||
header.playTime = in->readUint32LE();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGE2Engine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) {
|
||||
Common::Serializer s(readStream, writeStream);
|
||||
|
||||
// Synchronise header data
|
||||
syncHeader(s);
|
||||
|
||||
// Synchronise _spare
|
||||
_spare->sync(s);
|
||||
|
||||
if (s.isSaving()) {
|
||||
// Save the references of the items in the heroes pockets:
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kPocketMax; j++) {
|
||||
int ref = _heroTab[i]->_downPocketId[j];
|
||||
s.syncAsSint16LE(ref);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Load items to the pockets
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kPocketMax; j++) {
|
||||
int ref = 0;
|
||||
s.syncAsSint16LE(ref);
|
||||
_heroTab[i]->_downPocketId[j] = ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Heroes' _posTabs
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kSceneMax; j++) {
|
||||
s.syncAsSint16LE(_heroTab[i]->_posTab[j]->x);
|
||||
s.syncAsSint16LE(_heroTab[i]->_posTab[j]->y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::syncHeader(Common::Serializer &s) {
|
||||
s.syncAsUint16LE(_now);
|
||||
s.syncAsUint16LE(_sex);
|
||||
s.syncAsUint16LE(_vga->_rot._len);
|
||||
s.syncAsUint16LE(_waitSeq);
|
||||
s.syncAsUint16LE(_waitRef);
|
||||
|
||||
if (s.isSaving()) {
|
||||
// Write checksum
|
||||
int checksum = kSavegameCheckSum;
|
||||
s.syncAsUint16LE(checksum);
|
||||
} else {
|
||||
// Read checksum and validate it
|
||||
uint16 checksum = 0;
|
||||
s.syncAsUint16LE(checksum);
|
||||
if (checksum != kSavegameCheckSum)
|
||||
error("%s", _text->getText(kBadSVG));
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
872
engines/cge2/snail.cpp
Normal file
872
engines/cge2/snail.cpp
Normal file
@@ -0,0 +1,872 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/snail.h"
|
||||
#include "cge2/fileio.h"
|
||||
#include "cge2/hero.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/sound.h"
|
||||
#include "cge2/events.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
const char *CommandHandler::_commandText[] = {
|
||||
"NOP", "USE", "PAUSE", "INF", "CAVE", "SETX", "SETY", "SETZ", "ADD",
|
||||
"FLASH", "CYCLE", "CLEAR", "MOUSE", "MAP", "MIDI", ".DUMMY.", "WAIT",
|
||||
"HIDE", "ROOM", "SAY", "SOUND", "KILL", "RSEQ", "SEQ", "SEND", "SWAP",
|
||||
"KEEP", "GIVE", "GETPOS", "GOTO", "PORT", "NEXT", "NNEXT", "MTNEXT",
|
||||
"FTNEXT", "RNNEXT", "RMTNEXT", "RFTNEXT", "RMNEAR", "RMMTAKE", "RMFTAKE",
|
||||
"SETREF", "WALKTO", "REACH", "COVER", "UNCOVER", "EXEC", "GHOST",
|
||||
nullptr };
|
||||
|
||||
CommandHandler::CommandHandler(CGE2Engine *vm, bool turbo)
|
||||
: _turbo(turbo), _textDelay(false), _timerExpiry(0), _talkEnable(true),
|
||||
_head(0), _tail(0), _commandList((Command *)malloc(sizeof(Command)* 256)),
|
||||
_vm(vm) {
|
||||
}
|
||||
|
||||
CommandHandler::~CommandHandler() {
|
||||
free(_commandList);
|
||||
}
|
||||
|
||||
void CommandHandler::runCommand() {
|
||||
if (!_turbo && _vm->_soundStat._wait) {
|
||||
if (*(_vm->_soundStat._wait))
|
||||
return;
|
||||
|
||||
++_vm->_soundStat._ref[0];
|
||||
if (_vm->_fx->exist(_vm->_soundStat._ref[1], _vm->_soundStat._ref[0])) {
|
||||
int16 oldRepeat = _vm->_sound->getRepeat();
|
||||
_vm->_sound->setRepeat(1);
|
||||
_vm->_sound->play(Audio::Mixer::kSpeechSoundType, _vm->_soundStat._ref[1], _vm->_soundStat._ref[0], _vm->_sound->_smpinf._span);
|
||||
_vm->_sound->setRepeat(oldRepeat);
|
||||
return;
|
||||
}
|
||||
_vm->_soundStat._wait = nullptr;
|
||||
}
|
||||
|
||||
uint8 tmpHead = _head;
|
||||
while (_tail != tmpHead) {
|
||||
Command tailCmd = _commandList[_tail];
|
||||
|
||||
if (!_turbo) { // only for the slower one
|
||||
if (_vm->_waitRef)
|
||||
break;
|
||||
|
||||
if (_timerExpiry) {
|
||||
// Delay in progress
|
||||
if (_timerExpiry > g_system->getMillis())
|
||||
// Delay not yet ended
|
||||
break;
|
||||
|
||||
// Delay is finished
|
||||
_timerExpiry = 0;
|
||||
} else if (_textDelay) {
|
||||
if (_vm->_talk) {
|
||||
_vm->snKill((Sprite *)_vm->_talk);
|
||||
_vm->_talk = nullptr;
|
||||
}
|
||||
_textDelay = false;
|
||||
}
|
||||
|
||||
if (_vm->_talk && tailCmd._commandType != kCmdPause)
|
||||
break;
|
||||
}
|
||||
++_tail;
|
||||
_vm->_taken = false;
|
||||
Sprite *spr = nullptr;
|
||||
if (tailCmd._commandType > kCmdSpr)
|
||||
spr = (tailCmd._ref < 0) ? ((Sprite *)tailCmd._spritePtr) : _vm->locate(tailCmd._ref);
|
||||
|
||||
Common::String sprStr;
|
||||
if (tailCmd._commandType != kCmdGhost && spr && *spr->_file)
|
||||
// In case of kCmdGhost _spritePtr stores a pointer to a Bitmap, not to a Sprite...
|
||||
sprStr = Common::String(spr->_file);
|
||||
else
|
||||
sprStr = "None";
|
||||
|
||||
if (sprStr.empty())
|
||||
sprStr = "None";
|
||||
debugC(1, kCGE2DebugOpcode, "Command: %s; Ref: %d; Val: %d; Sprite: %s;", getComStr(tailCmd._commandType), tailCmd._ref, tailCmd._val, sprStr.c_str());
|
||||
|
||||
switch (tailCmd._commandType) {
|
||||
case kCmdUse:
|
||||
break;
|
||||
case kCmdPause:
|
||||
_timerExpiry = g_system->getMillis() + tailCmd._val * kCommandFrameDelay;
|
||||
if (_vm->_talk)
|
||||
_textDelay = true;
|
||||
break;
|
||||
case kCmdWait:
|
||||
if (spr && spr->active() && (spr->_scene == _vm->_now || spr->_scene == 0)) {
|
||||
_vm->_waitSeq = tailCmd._val;
|
||||
_vm->_waitRef = spr->_ref;
|
||||
}
|
||||
break;
|
||||
case kCmdHide:
|
||||
_vm->snHide(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdSay:
|
||||
_vm->snSay(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdInf:
|
||||
if (_talkEnable)
|
||||
_vm->inf(((tailCmd._val) >= 0) ? _vm->_text->getText(tailCmd._val) : (const char *)tailCmd._spritePtr);
|
||||
break;
|
||||
case kCmdCave:
|
||||
_vm->switchScene(tailCmd._val);
|
||||
break;
|
||||
case kCmdMidi:
|
||||
_vm->snMidi(tailCmd._val);
|
||||
break;
|
||||
case kCmdKill:
|
||||
_vm->snKill(spr);
|
||||
break;
|
||||
case kCmdSeq:
|
||||
_vm->snSeq(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdRSeq:
|
||||
_vm->snRSeq(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdSend:
|
||||
_vm->snSend(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdSwap:
|
||||
_vm->snSwap(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdCover:
|
||||
_vm->snCover(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdUncover:
|
||||
_vm->snUncover(spr, (tailCmd._val >= 0) ? _vm->locate(tailCmd._val) : ((Sprite *)tailCmd._spritePtr));
|
||||
break;
|
||||
case kCmdKeep:
|
||||
_vm->snKeep(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdGive:
|
||||
_vm->snGive(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdSetX:
|
||||
_vm->_point[tailCmd._val]->_x = tailCmd._ref;
|
||||
break;
|
||||
case kCmdSetY:
|
||||
_vm->_point[tailCmd._val]->_y = tailCmd._ref;
|
||||
break;
|
||||
case kCmdSetZ:
|
||||
_vm->_point[tailCmd._val]->_z = tailCmd._ref;
|
||||
break;
|
||||
case kCmdAdd:
|
||||
*(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) + *(_vm->_point[tailCmd._val]);
|
||||
break;
|
||||
case kCmdGetPos:
|
||||
if (spr)
|
||||
*(_vm->_point[tailCmd._val]) = spr->_pos3D;
|
||||
break;
|
||||
case kCmdGoto:
|
||||
_vm->snGoto(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdPort:
|
||||
_vm->snPort(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdNext:
|
||||
break;
|
||||
case kCmdMouse:
|
||||
_vm->snMouse(tailCmd._val != 0);
|
||||
break;
|
||||
case kCmdNNext:
|
||||
_vm->snNNext(spr, kNear, tailCmd._val);
|
||||
break;
|
||||
case kCmdMTNext:
|
||||
_vm->snNNext(spr, kMTake, tailCmd._val);
|
||||
break;
|
||||
case kCmdFTNext:
|
||||
_vm->snNNext(spr, kFTake, tailCmd._val);
|
||||
break;
|
||||
case kCmdRNNext:
|
||||
_vm->snRNNext(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdRMTNext:
|
||||
_vm->snRMTNext(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdRFTNext:
|
||||
_vm->snRFTNext(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdRMNear:
|
||||
_vm->snRmNear(spr);
|
||||
break;
|
||||
case kCmdRMMTake:
|
||||
_vm->snRmMTake(spr);
|
||||
break;
|
||||
case kCmdRMFTake:
|
||||
_vm->snRmFTake(spr);
|
||||
break;
|
||||
case kCmdSetRef:
|
||||
_vm->snSetRef(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdFlash:
|
||||
_vm->snFlash(tailCmd._val != 0);
|
||||
break;
|
||||
case kCmdCycle:
|
||||
_vm->snCycle(tailCmd._val);
|
||||
break;
|
||||
case kCmdWalk:
|
||||
_vm->snWalk(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdReach:
|
||||
_vm->snReach(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdSound:
|
||||
_vm->snSound(spr, tailCmd._val);
|
||||
_vm->_sound->setRepeat(1);
|
||||
break;
|
||||
case kCmdMap:
|
||||
_vm->_heroTab[tailCmd._ref & 1]->_ptr->_ignoreMap = tailCmd._val == 0;
|
||||
break;
|
||||
case kCmdRoom:
|
||||
_vm->snRoom(spr, tailCmd._val);
|
||||
break;
|
||||
case kCmdExec:
|
||||
switch (tailCmd._cbType) {
|
||||
case kQGame:
|
||||
_vm->qGame();
|
||||
break;
|
||||
case kXScene:
|
||||
_vm->xScene();
|
||||
break;
|
||||
default:
|
||||
error("Unknown Callback Type in SNEXEC");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case kCmdGhost:
|
||||
_vm->snGhost((Bitmap *)tailCmd._spritePtr);
|
||||
break;
|
||||
case kCmdNop: // Do nothing.
|
||||
break;
|
||||
default:
|
||||
warning("Unhandled command");
|
||||
break;
|
||||
}
|
||||
|
||||
if (_vm->_taken && spr)
|
||||
_vm->_spare->dispose(spr);
|
||||
|
||||
if (!_turbo)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snKill(Sprite *spr) {
|
||||
if (spr) {
|
||||
if (spr->_flags._kept)
|
||||
releasePocket(spr);
|
||||
Sprite *nx = spr->_next;
|
||||
hide1(spr);
|
||||
_vga->_showQ->remove(spr);
|
||||
_eventManager->clearEvent(spr);
|
||||
if (spr->_flags._kill) {
|
||||
_spare->take(spr->_ref);
|
||||
delete spr;
|
||||
} else {
|
||||
spr->setScene(-1);
|
||||
_spare->dispose(spr);
|
||||
}
|
||||
if (nx && nx->_flags._slav)
|
||||
snKill(nx);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snHide(Sprite *spr, int val) {
|
||||
if (spr) {
|
||||
spr->_flags._hide = (val >= 0) ? (val != 0) : (!spr->_flags._hide);
|
||||
if (spr->_flags._shad)
|
||||
spr->_prev->_flags._hide = spr->_flags._hide;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snMidi(int val) {
|
||||
if (val < 0)
|
||||
_midiPlayer->killMidi();
|
||||
else if (_music)
|
||||
_midiPlayer->loadMidi(val);
|
||||
}
|
||||
|
||||
void CGE2Engine::snSeq(Sprite *spr, int val) {
|
||||
if (spr) {
|
||||
if (isHero(spr) && (val == 0))
|
||||
((Hero*)spr)->park();
|
||||
else
|
||||
spr->step(val);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snRSeq(Sprite *spr, int val) {
|
||||
if (spr)
|
||||
snSeq(spr, spr->_seqPtr + val);
|
||||
}
|
||||
|
||||
void CGE2Engine::snSend(Sprite *spr, int val) {
|
||||
if (!spr)
|
||||
return;
|
||||
|
||||
// Sending", spr->_file
|
||||
// from scene", spr->_scene
|
||||
// to scene", val
|
||||
bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr);
|
||||
bool val1 = (val == 0 || val == _now);
|
||||
spr->_scene = val;
|
||||
releasePocket(spr);
|
||||
if (val1 != was1) {
|
||||
if (was1) {
|
||||
// deactivating
|
||||
hide1(spr);
|
||||
spr->_flags._slav = false;
|
||||
if ((spr == _heroTab[_sex]->_ptr) && (_heroTab[!_sex]->_ptr->_scene == _now))
|
||||
switchHero(!_sex);
|
||||
_spare->dispose(spr);
|
||||
} else {
|
||||
// activating
|
||||
if (byte(spr->_ref) == 0)
|
||||
_bitmapPalette = _vga->_sysPal;
|
||||
_vga->_showQ->insert(spr);
|
||||
if (isHero(spr)) {
|
||||
V2D p = *_heroTab[spr->_ref & 1]->_posTab[val];
|
||||
spr->gotoxyz(V3D(p.x, 0, p.y));
|
||||
((Hero*)spr)->setCurrent();
|
||||
}
|
||||
_taken = false;
|
||||
_bitmapPalette = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snSwap(Sprite *spr, int val) {
|
||||
bool tak = _taken;
|
||||
Sprite *xspr = locate(val);
|
||||
if (spr && xspr) {
|
||||
bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr);
|
||||
bool xwas1 = (_vga->_showQ->locate(val) != nullptr);
|
||||
|
||||
int tmp = spr->_scene;
|
||||
spr->setScene(xspr->_scene);
|
||||
xspr->setScene(tmp);
|
||||
|
||||
SWAP(spr->_pos2D, xspr->_pos2D);
|
||||
SWAP(spr->_pos3D, xspr->_pos3D);
|
||||
if (spr->_flags._kept)
|
||||
swapInPocket(spr, xspr);
|
||||
if (xwas1 != was1) {
|
||||
if (was1) {
|
||||
hide1(spr);
|
||||
_spare->dispose(spr);
|
||||
} else
|
||||
expandSprite(spr);
|
||||
if (xwas1) {
|
||||
hide1(xspr);
|
||||
_spare->dispose(xspr);
|
||||
} else {
|
||||
expandSprite(xspr);
|
||||
_taken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_taken)
|
||||
_spare->dispose(xspr);
|
||||
_taken = tak;
|
||||
}
|
||||
|
||||
void CGE2Engine::snCover(Sprite *spr, int val) {
|
||||
bool tak = _taken;
|
||||
Sprite *xspr = locate(val);
|
||||
if (spr && xspr) {
|
||||
spr->_flags._hide = true;
|
||||
xspr->setScene(spr->_scene);
|
||||
xspr->gotoxyz(spr->_pos3D);
|
||||
expandSprite(xspr);
|
||||
if ((xspr->_flags._shad = spr->_flags._shad) == true) {
|
||||
_vga->_showQ->insert(_vga->_showQ->remove(spr->_prev), xspr);
|
||||
spr->_flags._shad = false;
|
||||
}
|
||||
feedSnail(xspr, kNear, _heroTab[_sex]->_ptr);
|
||||
_taken = false;
|
||||
}
|
||||
if (_taken)
|
||||
_spare->dispose(xspr);
|
||||
_taken = tak;
|
||||
}
|
||||
|
||||
void CGE2Engine::snUncover(Sprite *spr, Sprite *spr2) {
|
||||
if (spr && spr2) {
|
||||
spr->_flags._hide = false;
|
||||
spr->setScene(spr2->_scene);
|
||||
if ((spr->_flags._shad = spr2->_flags._shad) == true) {
|
||||
_vga->_showQ->insert(_vga->_showQ->remove(spr2->_prev), spr);
|
||||
spr2->_flags._shad = false;
|
||||
}
|
||||
spr->gotoxyz(spr2->_pos3D);
|
||||
snSend(spr2, -1);
|
||||
if (spr->_time == 0)
|
||||
++spr->_time;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snKeep(Sprite *spr, int stp) {
|
||||
int sex = _sex;
|
||||
if (stp > 127) {
|
||||
_sex = stp & 1; // for another hero
|
||||
stp = -1;
|
||||
}
|
||||
HeroTab *ht = _heroTab[_sex];
|
||||
selectPocket(-1);
|
||||
int pp = ht->_pocPtr;
|
||||
|
||||
if (spr && !spr->_flags._kept && ht->_pocket[pp] == nullptr) {
|
||||
V3D pos(14, -10, -1);
|
||||
int16 oldRepeat = _sound->getRepeat();
|
||||
_sound->setRepeat(1);
|
||||
snSound(ht->_ptr, 3);
|
||||
_sound->setRepeat(oldRepeat);
|
||||
if (_taken) {
|
||||
_vga->_showQ->insert(spr);
|
||||
_taken = false;
|
||||
}
|
||||
ht->_pocket[pp] = spr;
|
||||
spr->setScene(0);
|
||||
spr->_flags._kept = true;
|
||||
if (!_sex)
|
||||
pos._x += kScrWidth - 58;
|
||||
if (pp & 1)
|
||||
pos._x += 29;
|
||||
if (pp >> 1)
|
||||
pos._y -= 20;
|
||||
pos._y -= (spr->_siz.y / 2);
|
||||
spr->gotoxyz(pos);
|
||||
if (stp >= 0)
|
||||
spr->step(stp);
|
||||
}
|
||||
_sex = sex;
|
||||
selectPocket(-1);
|
||||
}
|
||||
|
||||
void CGE2Engine::snGive(Sprite *spr, int val) {
|
||||
if (spr) {
|
||||
int p = findActivePocket(spr->_ref);
|
||||
if (p >= 0) {
|
||||
releasePocket(spr);
|
||||
spr->setScene(_now);
|
||||
if (val >= 0)
|
||||
spr->step(val);
|
||||
}
|
||||
}
|
||||
selectPocket(-1);
|
||||
}
|
||||
|
||||
void CGE2Engine::snGoto(Sprite *spr, int val) {
|
||||
if (spr) {
|
||||
V3D eye = *_eye;
|
||||
if (spr->_scene > 0)
|
||||
setEye(*_eyeTab[spr->_scene]);
|
||||
spr->gotoxyz(*_point[val]);
|
||||
setEye(eye);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snPort(Sprite *spr, int port) {
|
||||
if (spr)
|
||||
spr->_flags._port = (port < 0) ? !spr->_flags._port : (port != 0);
|
||||
}
|
||||
|
||||
void CGE2Engine::snMouse(bool on) {
|
||||
if (on)
|
||||
_mouse->on();
|
||||
else
|
||||
_mouse->off();
|
||||
}
|
||||
|
||||
void CGE2Engine::snNNext(Sprite *spr, Action act, int val) {
|
||||
if (spr) {
|
||||
if (val > 255)
|
||||
val = spr->labVal(act, val >> 8);
|
||||
spr->_actionCtrl[act]._ptr = val;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snRNNext(Sprite *spr, int val) {
|
||||
if (spr)
|
||||
spr->_actionCtrl[kNear]._ptr += val;
|
||||
}
|
||||
|
||||
void CGE2Engine::snRMTNext(Sprite *spr, int val) {
|
||||
if (spr)
|
||||
spr->_actionCtrl[kMTake]._ptr += val;
|
||||
}
|
||||
|
||||
void CGE2Engine::snRFTNext(Sprite * spr, int val) {
|
||||
if (spr)
|
||||
spr->_actionCtrl[kFTake]._ptr += val;
|
||||
}
|
||||
|
||||
void CGE2Engine::snRmNear(Sprite *spr) {
|
||||
if (spr)
|
||||
spr->_actionCtrl[kNear]._cnt = 0;
|
||||
}
|
||||
|
||||
void CGE2Engine::snRmMTake(Sprite *spr) {
|
||||
if (spr)
|
||||
spr->_actionCtrl[kMTake]._cnt = 0;
|
||||
}
|
||||
|
||||
void CGE2Engine::snRmFTake(Sprite *spr) {
|
||||
if (spr)
|
||||
spr->_actionCtrl[kFTake]._cnt = 0;
|
||||
}
|
||||
|
||||
void CGE2Engine::snSetRef(Sprite *spr, int val) {
|
||||
if (spr)
|
||||
spr->_ref = val;
|
||||
}
|
||||
|
||||
void CGE2Engine::snFlash(bool on) {
|
||||
if (on) {
|
||||
Dac *pal = (Dac *)malloc(sizeof(Dac) * kPalCount);
|
||||
if (pal) {
|
||||
memcpy(pal, _vga->_sysPal, kPalSize);
|
||||
for (int i = 0; i < kPalCount; i++) {
|
||||
int c;
|
||||
c = pal[i]._r << 1;
|
||||
pal[i]._r = (c < 64) ? c : 63;
|
||||
c = pal[i]._g << 1;
|
||||
pal[i]._g = (c < 64) ? c : 63;
|
||||
c = pal[i]._b << 1;
|
||||
pal[i]._b = (c < 64) ? c : 63;
|
||||
}
|
||||
_vga->setColors(pal, 64);
|
||||
}
|
||||
|
||||
free(pal);
|
||||
} else
|
||||
_vga->setColors(_vga->_sysPal, 64);
|
||||
_dark = false;
|
||||
}
|
||||
|
||||
void CGE2Engine::snCycle(int cnt) {
|
||||
_vga->_rot._len = cnt;
|
||||
}
|
||||
|
||||
void CGE2Engine::snWalk(Sprite *spr, int val) {
|
||||
if (isHero(spr)) {
|
||||
if (val < kMaxPoint)
|
||||
((Hero *)spr)->walkTo(*_point[val]);
|
||||
else {
|
||||
Sprite *s = _vga->_showQ->locate(val);
|
||||
if (s)
|
||||
((Hero *)spr)->walkTo(s);
|
||||
}
|
||||
((Hero *)spr)->_time = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snReach(Sprite *spr, int val) {
|
||||
if (isHero(spr))
|
||||
((Hero *)spr)->reach(val);
|
||||
}
|
||||
|
||||
void CGE2Engine::snSound(Sprite *spr, int wav, Audio::Mixer::SoundType soundType) {
|
||||
if (wav == -1)
|
||||
_sound->stop();
|
||||
else {
|
||||
if (_sound->_smpinf._counter && wav < 20)
|
||||
return;
|
||||
if (_soundStat._wait && ((wav & 255) > 80))
|
||||
return;
|
||||
|
||||
_soundStat._ref[1] = wav;
|
||||
_soundStat._ref[0] = !_fx->exist(_soundStat._ref[1]);
|
||||
_sound->play(soundType, _soundStat._ref[1], _soundStat._ref[0],
|
||||
(spr) ? (spr->_pos2D.x / (kScrWidth / 16)) : 8);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snRoom(Sprite *spr, bool on) {
|
||||
if (!isHero(spr))
|
||||
return;
|
||||
|
||||
int sex = spr->_ref & 1;
|
||||
Sprite **p = _heroTab[sex]->_pocket;
|
||||
if (on) {
|
||||
if (freePockets(sex) == 0 && p[kPocketMax] == nullptr) {
|
||||
SWAP(p[kPocketMax], p[kPocketMax - 1]);
|
||||
snHide(p[kPocketMax], 1);
|
||||
}
|
||||
} else if (p[kPocketMax]) {
|
||||
for (int i = 0; i < kPocketMax; i++) {
|
||||
if (p[i] == nullptr) {
|
||||
snHide(p[kPocketMax], 0);
|
||||
SWAP(p[kPocketMax], p[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::snGhost(Bitmap *bmp) {
|
||||
V2D p(this, bmp->_map & 0xFFFF, bmp->_map >> 16);
|
||||
bmp->hide(p);
|
||||
bmp->release();
|
||||
delete[] bmp->_b;
|
||||
bmp->_b = nullptr;
|
||||
delete bmp;
|
||||
bmp = nullptr;
|
||||
}
|
||||
|
||||
void CGE2Engine::snSay(Sprite *spr, int val) {
|
||||
if (spr && spr->active() && _commandHandler->_talkEnable) {
|
||||
//-- mouth animation
|
||||
if (isHero(spr) && spr->seqTest(-1))
|
||||
((Hero *)spr)->say();
|
||||
if (_sayCap)
|
||||
_text->say(_text->getText(val), spr);
|
||||
if (_sayVox) {
|
||||
int i = val;
|
||||
if (i < 256)
|
||||
i -= 100;
|
||||
int16 oldRepeat = _sound->getRepeat();
|
||||
_sound->setRepeat(1);
|
||||
if (!ConfMan.getBool("tts_enabled_speech") || getLanguage() == Common::PL_POL)
|
||||
snSound(spr, i, Audio::Mixer::kSpeechSoundType);
|
||||
_sound->setRepeat(oldRepeat);
|
||||
_soundStat._wait = &_sound->_smpinf._counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::hide1(Sprite *spr) {
|
||||
_commandHandlerTurbo->addCommand(kCmdGhost, -1, 0, spr->ghost());
|
||||
}
|
||||
|
||||
void CGE2Engine::swapInPocket(Sprite *spr, Sprite *xspr) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kPocketMax; j++) {
|
||||
Sprite *&poc = _heroTab[i]->_pocket[j];
|
||||
if (poc == spr) {
|
||||
spr->_flags._kept = false;
|
||||
poc = xspr;
|
||||
xspr->_flags._kept = true;
|
||||
xspr->_flags._port = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sprite *CGE2Engine::expandSprite(Sprite *spr) {
|
||||
if (spr)
|
||||
_vga->_showQ->insert(spr);
|
||||
return spr;
|
||||
}
|
||||
|
||||
void CGE2Engine::qGame() {
|
||||
// Write out the user's progress
|
||||
saveGame(0, Common::String("Automatic Savegame"));
|
||||
|
||||
busy(false);
|
||||
_vga->sunset();
|
||||
_endGame = true;
|
||||
}
|
||||
|
||||
void CGE2Engine::xScene() {
|
||||
sceneDown();
|
||||
sceneUp(_req);
|
||||
}
|
||||
|
||||
void CommandHandler::addCommand(CommandType com, int ref, int val, void *ptr) {
|
||||
if (ref == -2)
|
||||
ref = 142 - _vm->_sex;
|
||||
Command *headCmd = &_commandList[_head++];
|
||||
headCmd->_commandType = com;
|
||||
headCmd->_ref = ref;
|
||||
headCmd->_val = val;
|
||||
headCmd->_spritePtr = ptr;
|
||||
headCmd->_cbType = kNullCB;
|
||||
if (headCmd->_commandType == kCmdClear) {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandHandler::addCallback(CommandType com, int ref, int val, CallbackType cbType) {
|
||||
Command *headCmd = &_commandList[_head++];
|
||||
headCmd->_commandType = com;
|
||||
headCmd->_ref = ref;
|
||||
headCmd->_val = val;
|
||||
headCmd->_spritePtr = nullptr;
|
||||
headCmd->_cbType = cbType;
|
||||
if (headCmd->_commandType == kCmdClear) {
|
||||
_tail = _head;
|
||||
_vm->killText();
|
||||
_timerExpiry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandHandler::insertCommand(CommandType com, int ref, int val, void *ptr) {
|
||||
if (ref == -2)
|
||||
ref = 142 - _vm->_sex;
|
||||
--_tail;
|
||||
Command *tailCmd = &_commandList[_tail];
|
||||
tailCmd->_commandType = com;
|
||||
tailCmd->_ref = ref;
|
||||
tailCmd->_val = val;
|
||||
tailCmd->_spritePtr = ptr;
|
||||
tailCmd->_cbType = kNullCB;
|
||||
if (com == kCmdClear) {
|
||||
_tail = _head;
|
||||
_vm->killText();
|
||||
_timerExpiry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandHandler::idle() {
|
||||
return (!_vm->_waitRef && _head == _tail);
|
||||
}
|
||||
|
||||
void CommandHandler::clear() {
|
||||
_tail = _head;
|
||||
_vm->killText();
|
||||
_timerExpiry = 0;
|
||||
}
|
||||
|
||||
int CommandHandler::getComId(const char *com) {
|
||||
int i = _vm->takeEnum(_commandText, com);
|
||||
return (i < 0) ? i : i + kCmdCom0 + 1;
|
||||
}
|
||||
|
||||
const char *CommandHandler::getComStr(CommandType cmdType) {
|
||||
return _commandText[cmdType - kCmdNop];
|
||||
}
|
||||
|
||||
void CGE2Engine::feedSnail(Sprite *spr, Action snq, Hero *hero) {
|
||||
if (!spr || !spr->active())
|
||||
return;
|
||||
|
||||
int cnt = spr->_actionCtrl[snq]._cnt;
|
||||
if (cnt) {
|
||||
byte ptr = spr->_actionCtrl[snq]._ptr;
|
||||
CommandHandler::Command *comtab = spr->snList(snq);
|
||||
CommandHandler::Command *c = &comtab[ptr];
|
||||
CommandHandler::Command *q = &comtab[cnt];
|
||||
|
||||
if (hero != nullptr) {
|
||||
int pocFre = freePockets(hero->_ref & 1);
|
||||
int pocReq = 0;
|
||||
CommandHandler::Command *p = c;
|
||||
for (; p < q && p->_commandType != kCmdNext; p++) { // scan commands
|
||||
// drop from pocket?
|
||||
if ((p->_commandType == kCmdSend && p->_val != _now)
|
||||
|| p->_commandType == kCmdGive) {
|
||||
int ref = p->_ref;
|
||||
if (ref < 0)
|
||||
ref = spr->_ref;
|
||||
if (findActivePocket(ref) >= 0)
|
||||
--pocReq;
|
||||
}
|
||||
// make/dispose additional room?
|
||||
if (p->_commandType == kCmdRoom) {
|
||||
if (p->_val == 0)
|
||||
++pocReq;
|
||||
else
|
||||
--pocReq;
|
||||
}
|
||||
// put into pocket?
|
||||
if (p->_commandType == kCmdKeep)
|
||||
++pocReq;
|
||||
// overloaded?
|
||||
if (pocReq > pocFre) {
|
||||
pocFul();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (c < q) {
|
||||
if ((c->_val == -1) && (c->_commandType == kCmdWalk || c->_commandType == kCmdReach))
|
||||
c->_val = spr->_ref;
|
||||
|
||||
if (c->_commandType == kCmdNext) {
|
||||
Sprite *s;
|
||||
|
||||
switch (c->_ref) {
|
||||
case -2:
|
||||
s = hero;
|
||||
break;
|
||||
case -1:
|
||||
s = spr;
|
||||
break;
|
||||
default:
|
||||
s = _vga->_showQ->locate(c->_ref);
|
||||
break;
|
||||
}
|
||||
|
||||
if (s && s->_actionCtrl[snq]._cnt) {
|
||||
int v;
|
||||
switch (c->_val) {
|
||||
case -1:
|
||||
v = int(c - comtab + 1);
|
||||
break;
|
||||
case -2:
|
||||
v = int(c - comtab);
|
||||
break;
|
||||
case -3:
|
||||
v = -1;
|
||||
break;
|
||||
default:
|
||||
v = c->_val;
|
||||
if ((v > 255) && s)
|
||||
v = s->labVal(snq, v >> 8);
|
||||
break;
|
||||
}
|
||||
if (v >= 0) {
|
||||
s->_actionCtrl[snq]._ptr = v;
|
||||
if (spr->_ref == 1537 && s->_actionCtrl[snq]._ptr == 26)
|
||||
{
|
||||
debug(1, "Carpet Clothes Horse Rehanging Workaround Triggered!");
|
||||
s->_actionCtrl[snq]._ptr = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s == spr)
|
||||
break;
|
||||
}
|
||||
|
||||
_commandHandler->addCommand(c->_commandType, c->_ref, c->_val, spr);
|
||||
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace CGE2.
|
||||
128
engines/cge2/snail.h
Normal file
128
engines/cge2/snail.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_SNAIL_H
|
||||
#define CGE2_SNAIL_H
|
||||
|
||||
#include "cge2/cge2.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kCommandFrameRate 80
|
||||
#define kCommandFrameDelay (1000 / kCommandFrameRate)
|
||||
#define kNoByte -1 // Recheck this! We have no proof for it's original value.
|
||||
|
||||
|
||||
enum CommandType {
|
||||
kCmdCom0 = 128,
|
||||
kCmdNop, // NOP :: do nothing
|
||||
kCmdUse, // USE <spr> <cav>|<lab> :: hint for using
|
||||
kCmdPause, // PAUSE -1 <dly> :: delay <dly>/72 seconds
|
||||
kCmdInf, // INF -1 <ref> :: show text referrenced by <ref>
|
||||
kCmdCave, // CAVE -1 <cav> :: go to board <cav>
|
||||
kCmdSetX, // SETX <x> <idx> :: set sprite shift in x axis
|
||||
kCmdSetY, // SETX <y> <idx> :: set sprite shift in y axis
|
||||
kCmdSetZ, // SETX <z> <idx> :: set sprite shift in z axis
|
||||
kCmdAdd, // ADD <idx1> <idx2> :: sum vectors
|
||||
kCmdFlash, // FLASH -1 0|1 :: lighten whole image (on/off)
|
||||
kCmdCycle, // CYCLE <cnt> :: rotate <cnt> colors from 1
|
||||
kCmdClear, // CLEAR -1 0 :: clear kCmdAIL queue
|
||||
kCmdMouse, // MOUSE -1 0|1 :: enable mouse (on/off)
|
||||
kCmdMap, // MAP 0|1 0 :: temporarily turn off map for hero
|
||||
kCmdMidi, // MIDI -1 <midi> :: play MIDI referenced by <midi> (-1 = off)
|
||||
|
||||
kCmdSpr,
|
||||
|
||||
kCmdWait, // WAIT <spr> <seq>|-1 :: wait for SEQ <seq> (-1 = freeze)
|
||||
kCmdHide, // HIDE <spr> 0|1 :: visibility of sprite
|
||||
kCmdRoom, // ROOM <hero> 0|1 :: additional room in pocket (no/yes)
|
||||
kCmdSay, // SAY <spr> <ref> :: say text referenced by <ref>
|
||||
kCmdSound, // SOUND <spr> <ref> :: play sound effect referenced by <ref>
|
||||
kCmdKill, // KILL <spr> 0 :: remove sprite
|
||||
kCmdRSeq, // RSEQ <spr> <nr> :: relative jump SEQ <nr> lines
|
||||
kCmdSeq, // SEQ <spr> <seq> :: jump to certain SEQ
|
||||
kCmdSend, // SEND <spr> <cav> :: move sprite to board <cav>
|
||||
kCmdSwap, // SWAP <spr1> spr2> :: sprite exchange
|
||||
kCmdKeep, // KEEP <spr> <seq> :: take sprite into pocket and jump to <seq>
|
||||
kCmdGive, // GIVE <spr> <seq> :: remove sprite from pocket and jump to <seq>
|
||||
kCmdGetPos, // GETPOS <spr> <idx> :: take sprite's position
|
||||
kCmdGoto, // GOTO <spr> <idx> :: move sprite to position
|
||||
kCmdPort, // PORT <spr> 0|1 :: clear/set "takeability" of sprite
|
||||
kCmdNext, // NEXT <spr> <nr> :: jump to <nr> - NEAR or TAKE
|
||||
kCmdNNext, // NNEXT <spr> <nr> :: jump to <nr> - NEAR
|
||||
kCmdMTNext, // MTNEXT <spr> <nr> :: jump to <nr> - TAKE
|
||||
kCmdFTNext, // FTNEXT <spr> <nr> :: jump to <nr> - TAKE
|
||||
kCmdRNNext, // RNNEXT <spr> <nr> :: relative jump to <nr> - NEAR
|
||||
kCmdRMTNext, // RMTNEXT <spr> <nr> :: relative jump to <nr> - TAKE
|
||||
kCmdRFTNext, // RFTNEXT <spr> <nr> :: relative jump to <nr> - TAKE
|
||||
kCmdRMNear, // RMNEAR <spr> 0 :: remove NEAR list
|
||||
kCmdRMMTake, // RMMTAKE <spr> 0 :: remove TAKE list
|
||||
kCmdRMFTake, // RMFTAKE <spr> 0 :: remove TAKE list
|
||||
kCmdSetRef, // SETREF <spr> <ref> :: change reference of sprite <spr> to <ref>
|
||||
kCmdWalk, // WALKTO <hero> <ref>|<point> :: go close to the sprite or point
|
||||
kCmdReach, // REACH <hero> <ref>|<m> :: reach the sprite or point with <m> method
|
||||
kCmdCover, // COVER <sp1> <sp2> :: cover sprite <sp1> with sprite <sp2>
|
||||
kCmdUncover, // UNCOVER <sp1> <sp2> :: restore the state before COVER
|
||||
|
||||
kCmdExec,
|
||||
kCmdGhost
|
||||
};
|
||||
|
||||
class CommandHandler {
|
||||
public:
|
||||
struct Command {
|
||||
CommandType _commandType;
|
||||
byte _lab;
|
||||
int _ref;
|
||||
int _val;
|
||||
void *_spritePtr;
|
||||
CallbackType _cbType;
|
||||
} *_commandList;
|
||||
static const char *_commandText[];
|
||||
bool _talkEnable;
|
||||
|
||||
CommandHandler(CGE2Engine *vm, bool turbo);
|
||||
~CommandHandler();
|
||||
void runCommand();
|
||||
void addCommand(CommandType com, int ref, int val, void *ptr);
|
||||
void addCallback(CommandType com, int ref, int val, CallbackType cbType);
|
||||
void insertCommand(CommandType com, int ref, int val, void *ptr);
|
||||
bool idle();
|
||||
void clear();
|
||||
int getComId(const char *com);
|
||||
const char *getComStr(CommandType cmdType);
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
bool _turbo;
|
||||
uint8 _head;
|
||||
uint8 _tail;
|
||||
bool _textDelay;
|
||||
uint32 _timerExpiry; // "pause" in the original.
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif
|
||||
275
engines/cge2/sound.cpp
Normal file
275
engines/cge2/sound.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/sound.h"
|
||||
#include "common/memstream.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/midiparser.h"
|
||||
#include "cge2/cge2.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
DataCk::DataCk(byte *buf, int bufSize) {
|
||||
_buf = buf;
|
||||
_ckSize = bufSize;
|
||||
}
|
||||
|
||||
DataCk::~DataCk() {
|
||||
free(_buf);
|
||||
}
|
||||
|
||||
Sound::Sound(CGE2Engine *vm) : _vm(vm) {
|
||||
_audioStream = nullptr;
|
||||
_soundRepeatCount = 1;
|
||||
open();
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
close();
|
||||
}
|
||||
|
||||
void Sound::close() {
|
||||
_vm->_midiPlayer->killMidi();
|
||||
_vm->_mixer->stopAll();
|
||||
}
|
||||
|
||||
void Sound::open() {
|
||||
setRepeat(1);
|
||||
if (_vm->_commandHandlerTurbo != nullptr)
|
||||
_vm->switchSay();
|
||||
play(Audio::Mixer::kSFXSoundType, 99, 99);
|
||||
}
|
||||
|
||||
void Sound::setRepeat(int16 count) {
|
||||
_soundRepeatCount = count;
|
||||
}
|
||||
|
||||
int16 Sound::getRepeat() {
|
||||
return _soundRepeatCount;
|
||||
}
|
||||
|
||||
void Sound::play(Audio::Mixer::SoundType soundType, int ref, int sub, int pan) {
|
||||
stop();
|
||||
DataCk *wav = _vm->_fx->load(ref, sub);
|
||||
if (wav) {
|
||||
_smpinf._saddr = &*(wav->addr());
|
||||
_smpinf._slen = (uint16)wav->size();
|
||||
_smpinf._span = pan;
|
||||
_smpinf._counter = getRepeat();
|
||||
sndDigiStart(&_smpinf, soundType);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::sndDigiStart(SmpInfo *PSmpInfo, Audio::Mixer::SoundType soundType) {
|
||||
// Create an audio stream wrapper for sound
|
||||
Common::MemoryReadStream *stream = new Common::MemoryReadStream(PSmpInfo->_saddr,
|
||||
PSmpInfo->_slen, DisposeAfterUse::NO);
|
||||
_audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
|
||||
|
||||
// Decide which handle to use
|
||||
Audio::SoundHandle *handle = nullptr;
|
||||
switch (soundType) {
|
||||
case Audio::Mixer::kSFXSoundType:
|
||||
handle = &_sfxHandle;
|
||||
break;
|
||||
case Audio::Mixer::kSpeechSoundType:
|
||||
handle = &_speechHandle;
|
||||
break;
|
||||
default:
|
||||
error("Wrong sound type passed to sndDigiStart()");
|
||||
}
|
||||
|
||||
// Start the new sound
|
||||
_vm->_mixer->playStream(soundType, handle,
|
||||
Audio::makeLoopingAudioStream(_audioStream, (uint)PSmpInfo->_counter));
|
||||
|
||||
// CGE pan:
|
||||
// 8 = Center
|
||||
// Less = Left
|
||||
// More = Right
|
||||
_vm->_mixer->setChannelBalance(*handle, (int8)CLIP(((PSmpInfo->_span - 8) * 16), -127, 127));
|
||||
}
|
||||
|
||||
void Sound::stop() {
|
||||
sndDigiStop(_sfxHandle);
|
||||
sndDigiStop(_speechHandle);
|
||||
_audioStream = nullptr;
|
||||
}
|
||||
|
||||
void Sound::checkSoundHandles() {
|
||||
if (!_vm->_mixer->isSoundHandleActive(_speechHandle) && !_vm->_mixer->isSoundHandleActive(_sfxHandle))
|
||||
_smpinf._counter = 0;
|
||||
}
|
||||
|
||||
void Sound::sndDigiStop(Audio::SoundHandle &handle) {
|
||||
if (_vm->_mixer->isSoundHandleActive(handle))
|
||||
_vm->_mixer->stopHandle(handle);
|
||||
}
|
||||
|
||||
Fx::Fx(CGE2Engine *vm, int size) : _current(nullptr), _vm(vm) {
|
||||
}
|
||||
|
||||
Fx::~Fx() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void Fx::clear() {
|
||||
if (_current)
|
||||
delete _current;
|
||||
_current = nullptr;
|
||||
}
|
||||
|
||||
Common::String Fx::name(int ref, int sub) {
|
||||
const char *fxname = "%.2dfx%.2d.WAV";
|
||||
const char *subName = "%.2dfx%.2d?.WAV";
|
||||
const char *p = (sub) ? subName : fxname;
|
||||
Common::String filename = Common::String::format(p, ref >> 8, ref & 0xFF);
|
||||
if (sub)
|
||||
filename.setChar('@' + sub, 6);
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool Fx::exist(int ref, int sub) {
|
||||
return _vm->_resman->exist(name(ref, sub).c_str());
|
||||
}
|
||||
|
||||
DataCk *Fx::load(int ref, int sub) {
|
||||
Common::String filename = name(ref, sub);
|
||||
EncryptedStream file(_vm->_resman, filename.c_str());
|
||||
clear();
|
||||
return (_current = loadWave(&file));
|
||||
}
|
||||
|
||||
DataCk *Fx::loadWave(EncryptedStream *file) {
|
||||
byte *data = (byte *)malloc(file->size());
|
||||
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
file->read(data, file->size());
|
||||
|
||||
return new DataCk(data, file->size());
|
||||
}
|
||||
|
||||
MusicPlayer::MusicPlayer(CGE2Engine *vm) : _vm(vm) {
|
||||
_data = nullptr;
|
||||
_isGM = false;
|
||||
|
||||
MidiPlayer::createDriver();
|
||||
|
||||
int ret = _driver->open();
|
||||
if (ret == 0) {
|
||||
if (_nativeMT32)
|
||||
_driver->sendMT32Reset();
|
||||
else
|
||||
_driver->sendGMReset();
|
||||
|
||||
// TODO: Load cmf.ins with the instrument table. It seems that an
|
||||
// interface for such an operation is supported for AdLib. Maybe for
|
||||
// this card, setting instruments is necessary.
|
||||
|
||||
_driver->setTimerCallback(this, &timerCallback);
|
||||
}
|
||||
_dataSize = -1;
|
||||
}
|
||||
|
||||
MusicPlayer::~MusicPlayer() {
|
||||
killMidi();
|
||||
}
|
||||
|
||||
void MusicPlayer::killMidi() {
|
||||
Audio::MidiPlayer::stop();
|
||||
|
||||
free(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
void MusicPlayer::loadMidi(int ref) {
|
||||
if (_vm->_midiNotify != nullptr)
|
||||
(_vm->*_vm->_midiNotify)();
|
||||
|
||||
// Work out the filename and check the given MIDI file exists
|
||||
Common::String filename = Common::String::format("%.2dSG%.2d.MID", ref >> 8, ref & 0xFF);
|
||||
if (!_vm->_resman->exist(filename.c_str()))
|
||||
return;
|
||||
|
||||
// Stop any currently playing MIDI file
|
||||
killMidi();
|
||||
|
||||
// Read in the data for the file
|
||||
EncryptedStream mid(_vm->_resman, filename.c_str());
|
||||
_dataSize = mid.size();
|
||||
_data = (byte *)malloc(_dataSize);
|
||||
mid.read(_data, _dataSize);
|
||||
|
||||
// Start playing the music
|
||||
sndMidiStart();
|
||||
}
|
||||
|
||||
void MusicPlayer::sndMidiStart() {
|
||||
_isGM = true;
|
||||
|
||||
MidiParser *parser = MidiParser::createParser_SMF();
|
||||
if (parser->loadMusic(_data, _dataSize)) {
|
||||
parser->setTrack(0);
|
||||
parser->setMidiDriver(this);
|
||||
parser->setTimerRate(_driver->getBaseTempo());
|
||||
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
|
||||
|
||||
_parser = parser;
|
||||
|
||||
syncVolume();
|
||||
|
||||
// Al the tracks are supposed to loop
|
||||
_isLooping = true;
|
||||
_isPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPlayer::send(uint32 b) {
|
||||
if (((b & 0xF0) == 0xC0) && !_isGM && !_nativeMT32) {
|
||||
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
|
||||
}
|
||||
|
||||
Audio::MidiPlayer::send(b);
|
||||
}
|
||||
|
||||
void MusicPlayer::sendToChannel(byte channel, uint32 b) {
|
||||
if (!_channelsTable[channel]) {
|
||||
_channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
|
||||
// If a new channel is allocated during the playback, make sure
|
||||
// its volume is correctly initialized.
|
||||
if (_channelsTable[channel])
|
||||
_channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
|
||||
}
|
||||
|
||||
if (_channelsTable[channel])
|
||||
_channelsTable[channel]->send(b);
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
128
engines/cge2/sound.h
Normal file
128
engines/cge2/sound.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_SOUND_H
|
||||
#define CGE2_SOUND_H
|
||||
|
||||
#include "audio/midiplayer.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Audio {
|
||||
class RewindableAudioStream;
|
||||
}
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class CGE2Engine;
|
||||
class EncryptedStream;
|
||||
|
||||
// sample info
|
||||
struct SmpInfo {
|
||||
const uint8 *_saddr; // address
|
||||
uint16 _slen; // length
|
||||
uint16 _span; // left/right pan (0-15)
|
||||
int _counter; // number of time the sample should be played
|
||||
};
|
||||
|
||||
class DataCk {
|
||||
byte *_buf;
|
||||
int _ckSize;
|
||||
public:
|
||||
DataCk(byte *buf, int bufSize);
|
||||
~DataCk();
|
||||
inline const byte *addr() {
|
||||
return _buf;
|
||||
}
|
||||
inline int size() {
|
||||
return _ckSize;
|
||||
}
|
||||
};
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
SmpInfo _smpinf;
|
||||
|
||||
explicit Sound(CGE2Engine *vm);
|
||||
~Sound();
|
||||
void open();
|
||||
void close();
|
||||
void play(Audio::Mixer::SoundType soundType, int ref, int sub, int pan = 8);
|
||||
int16 getRepeat();
|
||||
void setRepeat(int16 count);
|
||||
void stop();
|
||||
void checkSoundHandles();
|
||||
private:
|
||||
int _soundRepeatCount;
|
||||
CGE2Engine *_vm;
|
||||
Audio::SoundHandle _sfxHandle;
|
||||
Audio::SoundHandle _speechHandle;
|
||||
Audio::RewindableAudioStream *_audioStream;
|
||||
|
||||
void sndDigiStart(SmpInfo *PSmpInfo, Audio::Mixer::SoundType soundType);
|
||||
void sndDigiStop(Audio::SoundHandle &handle);
|
||||
};
|
||||
|
||||
class Fx {
|
||||
CGE2Engine *_vm;
|
||||
|
||||
DataCk *loadWave(EncryptedStream *file);
|
||||
Common::String name(int ref, int sub);
|
||||
public:
|
||||
DataCk *_current;
|
||||
|
||||
Fx(CGE2Engine *vm, int size);
|
||||
~Fx();
|
||||
void clear();
|
||||
bool exist(int ref, int sub = 0);
|
||||
DataCk *load(int ref, int sub = 0);
|
||||
};
|
||||
|
||||
class MusicPlayer: public Audio::MidiPlayer {
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
byte *_data;
|
||||
int _dataSize;
|
||||
bool _isGM;
|
||||
|
||||
// Start MIDI File
|
||||
void sndMidiStart();
|
||||
|
||||
// Stop MIDI File
|
||||
void sndMidiStop();
|
||||
public:
|
||||
explicit MusicPlayer(CGE2Engine *vm);
|
||||
~MusicPlayer() override;
|
||||
|
||||
void loadMidi(int ref);
|
||||
void killMidi();
|
||||
|
||||
void send(uint32 b) override;
|
||||
void sendToChannel(byte channel, uint32 b) override;
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_SOUND_H
|
||||
127
engines/cge2/spare.cpp
Normal file
127
engines/cge2/spare.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/spare.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
void Spare::sync(Common::Serializer &s) {
|
||||
int size = 0;
|
||||
if (s.isSaving()) {
|
||||
for (uint i = 0; i < _container.size(); i++)
|
||||
if (_container[i]->_ref >= 141)
|
||||
size++;
|
||||
s.syncAsSint16LE(size);
|
||||
|
||||
for (uint i = 0; i < _container.size(); i++) {
|
||||
if (_container[i]->_ref >= 141)
|
||||
_container[i]->sync(s);
|
||||
}
|
||||
} else {
|
||||
s.syncAsSint16LE(size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
Sprite *sprite = new Sprite(_vm);
|
||||
sprite->sync(s);
|
||||
update(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spare::clear() {
|
||||
for (uint i = 0; i < _container.size(); i++)
|
||||
delete _container[i];
|
||||
|
||||
_container.clear();
|
||||
}
|
||||
|
||||
Sprite *Spare::locate(int ref) {
|
||||
for (uint i = 0; i < _container.size(); ++i) {
|
||||
if (_container[i]->_ref == ref)
|
||||
return _container[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Sprite *Spare::take(int ref) {
|
||||
Sprite *spr = nullptr;
|
||||
if ((spr = locate(ref)) != nullptr) {
|
||||
for (uint i = 0; i < _container.size(); ++i) {
|
||||
if (spr == _container[i]) {
|
||||
_container.remove_at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return spr;
|
||||
}
|
||||
|
||||
void Spare::takeScene(int cav) {
|
||||
int bakRef = cav << 8;
|
||||
Common::Array<Sprite*> tempCont = _container;
|
||||
for (uint i = 0; i < tempCont.size(); ++i) {
|
||||
Sprite *spr = tempCont[i];
|
||||
int c = spr->_scene;
|
||||
if ((c == _vm->_now || c == 0) && spr->_ref != bakRef) {
|
||||
spr = locate(spr->_ref);
|
||||
_vm->_vga->_showQ->insert(spr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spare::store(Sprite *spr) {
|
||||
_container.push_back(spr);
|
||||
}
|
||||
|
||||
void Spare::update(Sprite *spr) {
|
||||
Sprite *sp = locate(spr->_ref);
|
||||
if (sp == nullptr)
|
||||
store(spr);
|
||||
else {
|
||||
sp->contract();
|
||||
*sp = *spr;
|
||||
}
|
||||
}
|
||||
|
||||
void Spare::dispose(Sprite *spr) {
|
||||
if (spr) {
|
||||
_vm->_vga->_showQ->remove(spr);
|
||||
update(spr->contract());
|
||||
}
|
||||
}
|
||||
|
||||
void Spare::dispose(int ref) {
|
||||
dispose(_vm->_vga->_showQ->locate(ref));
|
||||
}
|
||||
|
||||
void Spare::dispose() {
|
||||
for (uint i = 0; i < _container.size(); ++i) {
|
||||
if (_container[i]->_ref > 255)
|
||||
dispose(_container[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
55
engines/cge2/spare.h
Normal file
55
engines/cge2/spare.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_SPARE_H
|
||||
#define CGE2_SPARE_H
|
||||
|
||||
#include "cge2/vga13h.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class Spare {
|
||||
CGE2Engine *_vm;
|
||||
Common::Array<Sprite*> _container;
|
||||
public:
|
||||
Spare(CGE2Engine *vm) : _vm(vm) {}
|
||||
~Spare() { clear(); }
|
||||
void store(Sprite *spr);
|
||||
Sprite *locate(int ref);
|
||||
Sprite *take(int ref);
|
||||
void takeScene(int cav);
|
||||
void update(Sprite *spr);
|
||||
void dispose(Sprite *spr);
|
||||
void dispose(int ref);
|
||||
void dispose();
|
||||
void sync(Common::Serializer &s);
|
||||
uint16 count() { return _container.size(); }
|
||||
void clear();
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_SPARE_H
|
||||
322
engines/cge2/talk.cpp
Normal file
322
engines/cge2/talk.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/general.h"
|
||||
#include "cge2/talk.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
void CGE2Engine::setAutoColors() {
|
||||
Dac def[4] = {
|
||||
{ 0, 0, 0 },
|
||||
{ 220 >> 2, 220 >> 2, 220 >> 2 },
|
||||
{ 190 >> 2, 190 >> 2, 190 >> 2 },
|
||||
{ 160 >> 2, 160 >> 2, 160 >> 2 },
|
||||
};
|
||||
Dac pal[kPalCount];
|
||||
_vga->getColors(pal);
|
||||
for (int i = 0; i < 4; i++)
|
||||
_font->_colorSet[kCBRel][i] = _vga->closest(pal, def[i]);
|
||||
}
|
||||
|
||||
Font::Font(CGE2Engine *vm) : _vm(vm) {
|
||||
_map = new uint8[kMapSize];
|
||||
_pos = new uint16[kPosSize];
|
||||
_widthArr = new uint8[kWidSize];
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
Font::~Font() {
|
||||
delete[] _map;
|
||||
delete[] _pos;
|
||||
delete[] _widthArr;
|
||||
}
|
||||
|
||||
void Font::load() {
|
||||
char path[10];
|
||||
Common::strcpy_s(path, "CGE.CFT");
|
||||
if (!_vm->_resman->exist(path))
|
||||
error("Missing Font file! %s", path);
|
||||
|
||||
EncryptedStream fontFile(_vm->_resman, path);
|
||||
assert(!fontFile.err());
|
||||
|
||||
fontFile.read(_widthArr, kWidSize);
|
||||
assert(!fontFile.err());
|
||||
|
||||
uint16 p = 0;
|
||||
for (uint16 i = 0; i < kPosSize; i++) {
|
||||
_pos[i] = p;
|
||||
p += _widthArr[i];
|
||||
}
|
||||
fontFile.read(_map, p);
|
||||
|
||||
Common::strcpy_s(path, "CGE.TXC");
|
||||
if (!_vm->_resman->exist(path))
|
||||
error("Missing Color file! %s", path);
|
||||
|
||||
// Reading in _colorSet:
|
||||
EncryptedStream colorFile(_vm->_resman, path);
|
||||
assert(!colorFile.err());
|
||||
|
||||
char tmpStr[kLineMax + 1];
|
||||
int n = 0;
|
||||
|
||||
for (Common::String line = colorFile.readLine(); !colorFile.eos(); line = colorFile.readLine()){
|
||||
if (line.empty())
|
||||
continue;
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
_colorSet[n][0] = _vm->number(tmpStr);
|
||||
|
||||
for (int i = 1; i < 4; i++)
|
||||
_colorSet[n][i] = _vm->number(nullptr);
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 Font::width(const char *text) {
|
||||
uint16 w = 0;
|
||||
if (!text)
|
||||
return 0;
|
||||
while (*text)
|
||||
w += _widthArr[(unsigned char)*(text++)];
|
||||
return w;
|
||||
}
|
||||
|
||||
Talk::Talk(CGE2Engine *vm, const char *text, TextBoxStyle mode, ColorBank color, bool wideSpace)
|
||||
: Sprite(vm), _mode(mode), _created(false), _wideSpace(wideSpace), _vm(vm) {
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled_speech"))
|
||||
ttsMan->say(text);
|
||||
|
||||
_color = _vm->_font->_colorSet[color];
|
||||
|
||||
if (color == kCBRel)
|
||||
_vm->setAutoColors();
|
||||
update(text);
|
||||
}
|
||||
|
||||
Talk::Talk(CGE2Engine *vm, ColorBank color)
|
||||
: Sprite(vm), _mode(kTBPure), _created(false), _wideSpace(false), _vm(vm) {
|
||||
_color = _vm->_font->_colorSet[color];
|
||||
|
||||
if (color == kCBRel)
|
||||
_vm->setAutoColors();
|
||||
}
|
||||
|
||||
uint8 *Talk::box(V2D siz) {
|
||||
uint16 n, r = (_mode == kTBRound) ? kTextRoundCorner : 0;
|
||||
const byte lt = _color[1], bg = _color[2], dk = _color[3];
|
||||
|
||||
if (siz.x < 8)
|
||||
siz.x = 8;
|
||||
if (siz.y < 8)
|
||||
siz.y = 8;
|
||||
uint8 *b = new uint8[n = siz.area()];
|
||||
memset(b, bg, n);
|
||||
|
||||
if (_mode) {
|
||||
uint8 *p = b;
|
||||
uint8 *q = b + n - siz.x;
|
||||
memset(p, lt, siz.x);
|
||||
memset(q, dk, siz.x);
|
||||
while (p < q) {
|
||||
p += siz.x;
|
||||
*(p - 1) = dk;
|
||||
*p = lt;
|
||||
}
|
||||
p = b;
|
||||
for (int i = 0; i < r; i++) {
|
||||
int j = 0;
|
||||
for (; j < r - i; j++) {
|
||||
p[j] = kPixelTransp;
|
||||
p[siz.x - j - 1] = kPixelTransp;
|
||||
q[j] = kPixelTransp;
|
||||
q[siz.x - j - 1] = kPixelTransp;
|
||||
}
|
||||
p[j] = lt;
|
||||
p[siz.x - j - 1] = dk;
|
||||
q[j] = lt;
|
||||
q[siz.x - j - 1] = dk;
|
||||
p += siz.x;
|
||||
q -= siz.x;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
void Talk::update(const char *text) {
|
||||
const uint16 vmarg = (_mode) ? kTextVMargin : 0;
|
||||
const uint16 hmarg = (_mode) ? kTextHMargin : 0;
|
||||
uint16 mw;
|
||||
uint16 mh;
|
||||
uint16 ln = vmarg;
|
||||
uint8 *m;
|
||||
uint8 *map;
|
||||
uint8 fg = _color[0];
|
||||
|
||||
if (_created) {
|
||||
mw = _ext->_shpList->_w;
|
||||
mh = _ext->_shpList->_h;
|
||||
delete _ext->_shpList;
|
||||
} else {
|
||||
uint16 k = 2 * hmarg;
|
||||
mh = 2 * vmarg + kFontHigh;
|
||||
mw = 0;
|
||||
for (const char *p = text; *p; p++) {
|
||||
if ((*p == '|') || (*p == '\n')) {
|
||||
mh += kFontHigh + kTextLineSpace;
|
||||
if (k > mw)
|
||||
mw = k;
|
||||
k = 2 * hmarg;
|
||||
} else if ((*p == 0x20) && (_vm->_font->_widthArr[(unsigned char)*p] > 4) && (!_wideSpace))
|
||||
k += _vm->_font->_widthArr[(unsigned char)*p] - 2;
|
||||
else
|
||||
k += _vm->_font->_widthArr[(unsigned char)*p];
|
||||
}
|
||||
if (k > mw)
|
||||
mw = k;
|
||||
|
||||
_created = true;
|
||||
}
|
||||
|
||||
V2D sz(_vm, mw, mh);
|
||||
map = box(sz);
|
||||
|
||||
m = map + ln * mw + hmarg;
|
||||
|
||||
while (*text) {
|
||||
if ((*text == '|') || (*text == '\n'))
|
||||
m = map + (ln += kFontHigh + kTextLineSpace) * mw + hmarg;
|
||||
else {
|
||||
int cw = _vm->_font->_widthArr[(unsigned char)*text];
|
||||
uint8 *f = _vm->_font->_map + _vm->_font->_pos[(unsigned char)*text];
|
||||
|
||||
// Handle properly space size, after it was enlarged to display properly
|
||||
// 'F1' text.
|
||||
int8 fontStart = 0;
|
||||
if ((*text == 0x20) && (cw > 4) && (!_wideSpace))
|
||||
fontStart = 2;
|
||||
|
||||
for (int i = fontStart; i < cw; i++) {
|
||||
uint8 *pp = m;
|
||||
uint16 n;
|
||||
uint16 b = *(f++);
|
||||
for (n = 0; n < kFontHigh; n++) {
|
||||
if (b & 1)
|
||||
*pp = fg;
|
||||
b >>= 1;
|
||||
pp += mw;
|
||||
}
|
||||
m++;
|
||||
}
|
||||
}
|
||||
text++;
|
||||
}
|
||||
BitmapPtr b = new Bitmap[1];
|
||||
b[0] = Bitmap(_vm, sz.x, sz.y, map);
|
||||
delete[] map;
|
||||
setShapeList(b, 1);
|
||||
}
|
||||
|
||||
InfoLine::InfoLine(CGE2Engine *vm, uint16 w, ColorBank color)
|
||||
: Talk(vm), _oldText(nullptr), _newText(nullptr), _realTime(false), _vm(vm) {
|
||||
_wideSpace = false;
|
||||
BitmapPtr b = new Bitmap[1];
|
||||
if (color == kCBRel)
|
||||
_vm->setAutoColors();
|
||||
_color = _vm->_font->_colorSet[color];
|
||||
V2D siz = V2D(_vm, w, kFontHigh);
|
||||
b[0] = Bitmap(_vm, siz.x, siz.y, _color[2]);
|
||||
setShapeList(b, 1);
|
||||
}
|
||||
|
||||
void InfoLine::update(const char *text) {
|
||||
if (!_realTime && (text == _oldText))
|
||||
return;
|
||||
|
||||
_oldText = text;
|
||||
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (text && ttsMan != nullptr && ConfMan.getBool("tts_enabled_objects"))
|
||||
ttsMan->say(text);
|
||||
|
||||
uint16 w = _ext->_shpList->_w;
|
||||
uint16 h = _ext->_shpList->_h;
|
||||
uint8 *v = _ext->_shpList->_v;
|
||||
uint16 dsiz = w >> 2; // data size (1 plane line size)
|
||||
uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap
|
||||
uint16 psiz = h * lsiz; // - last gape, but + plane trailer
|
||||
uint16 size = 4 * psiz; // whole map size
|
||||
uint8 fg = _color[0];
|
||||
uint8 bg = _color[2];
|
||||
|
||||
// clear whole rectangle
|
||||
memset(v + 2, bg, dsiz); // data bytes
|
||||
for (byte *pDest = v + lsiz; pDest < (v + psiz); pDest += lsiz) {
|
||||
Common::copy(v, v + lsiz, pDest);
|
||||
}
|
||||
*(uint16 *)(v + psiz - 2) = TO_LE_16(kBmpEOI); // plane trailer uint16
|
||||
for (byte *pDest = v + psiz; pDest < (v + 4 * psiz); pDest += psiz) {
|
||||
Common::copy(v, v + psiz, pDest);
|
||||
}
|
||||
|
||||
// paint text line
|
||||
if (_newText) {
|
||||
uint8 *p = v + 2, *q = p + size;
|
||||
|
||||
while (*text) {
|
||||
uint16 cw = _vm->_font->_widthArr[(unsigned char)*text];
|
||||
uint8 *fp = _vm->_font->_map + _vm->_font->_pos[(unsigned char)*text];
|
||||
|
||||
// Handle properly space size, after it was enlarged to display properly
|
||||
// 'F1' text.
|
||||
int8 fontStart = 0;
|
||||
if ((*text == 0x20) && (cw > 4) && (!_wideSpace))
|
||||
fontStart = 2;
|
||||
|
||||
for (int i = fontStart; i < cw; i++) {
|
||||
uint16 b = fp[i];
|
||||
for (uint16 n = 0; n < kFontHigh; n++) {
|
||||
if (b & 1)
|
||||
*p = fg;
|
||||
b >>= 1;
|
||||
p += lsiz;
|
||||
}
|
||||
if (p >= q)
|
||||
p = p - size + 1;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
93
engines/cge2/talk.h
Normal file
93
engines/cge2/talk.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_TALK_H
|
||||
#define CGE2_TALK_H
|
||||
|
||||
#include "cge2/general.h"
|
||||
#include "cge2/vga13h.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kTextHMargin (6&~1) // EVEN horizontal margins!
|
||||
#define kTextVMargin 5 // vertical margins
|
||||
#define kTextLineSpace 2 // line spacing
|
||||
#define kTextRoundCorner 3 // rounded corners
|
||||
#define kWidSize 256
|
||||
#define kPosSize 256
|
||||
#define kMapSize (256*8)
|
||||
#define kFontHigh 8
|
||||
#define kCaptionSide 24
|
||||
#define kInfName 101
|
||||
#define kSayName 102
|
||||
|
||||
class Font {
|
||||
void load();
|
||||
CGE2Engine *_vm;
|
||||
public:
|
||||
uint8 *_widthArr;
|
||||
uint16 *_pos;
|
||||
uint8 *_map;
|
||||
uint8 _colorSet[kColorNum][4];
|
||||
Font(CGE2Engine *vm);
|
||||
~Font();
|
||||
uint16 width(const char *text);
|
||||
};
|
||||
|
||||
enum TextBoxStyle { kTBPure, kTBRect, kTBRound };
|
||||
|
||||
class Talk : public Sprite {
|
||||
protected:
|
||||
TextBoxStyle _mode;
|
||||
bool _created;
|
||||
uint8 *box(V2D siz);
|
||||
bool _wideSpace;
|
||||
public:
|
||||
uint8 *_color;
|
||||
|
||||
Talk(CGE2Engine *vm, const char *text, TextBoxStyle mode = kTBPure, ColorBank color = kCBStd, bool wideSpace = false);
|
||||
Talk(CGE2Engine *vm, ColorBank color = kCBStd);
|
||||
|
||||
void update(const char *text);
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
};
|
||||
|
||||
class InfoLine : public Talk {
|
||||
const char *_oldText, *_newText;
|
||||
public:
|
||||
bool _realTime;
|
||||
InfoLine(CGE2Engine *vm, uint16 wid, ColorBank color = kCBStd);
|
||||
void update(const char *text);
|
||||
void update() { update(_newText); }
|
||||
void setText(const char *txt) { _newText = txt; }
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_TALK_H
|
||||
197
engines/cge2/text.cpp
Normal file
197
engines/cge2/text.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "cge2/text.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
Text::Text(CGE2Engine *vm, const char *fname) : _vm(vm) {
|
||||
_vm->mergeExt(_fileName, fname, kSayExt);
|
||||
if (!_vm->_resman->exist(_fileName))
|
||||
error("No talk (%s)", _fileName);
|
||||
_txtCount = count();
|
||||
if (_txtCount == -1)
|
||||
error("Unable to read dialog file %s", _fileName);
|
||||
|
||||
_txtCount += 2;
|
||||
_cache = new Handler[_txtCount];
|
||||
for (_size = 0; _size < _txtCount; _size++) {
|
||||
_cache[_size]._ref = 0;
|
||||
_cache[_size]._text = nullptr;
|
||||
}
|
||||
load();
|
||||
|
||||
_cache[_txtCount - 1]._ref = -1;
|
||||
_cache[_txtCount - 1]._text = new char[3];
|
||||
_cache[_txtCount - 1]._text[0] = '\0';
|
||||
}
|
||||
|
||||
Text::~Text() {
|
||||
clear();
|
||||
delete[] _cache;
|
||||
}
|
||||
|
||||
int16 Text::count() {
|
||||
EncryptedStream tf(_vm->_resman, _fileName);
|
||||
if (tf.err())
|
||||
return -1;
|
||||
|
||||
Common::String line;
|
||||
char tmpStr[kLineMax + 1];
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) {
|
||||
char *s;
|
||||
assert(line.size() <= 513);
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
if ((s = strtok(tmpStr, " =,;/\t\n")) == nullptr)
|
||||
continue;
|
||||
if (!Common::isDigit(*s))
|
||||
continue;
|
||||
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
void Text::clear() {
|
||||
for (int i = 0; i < _txtCount; i++) {
|
||||
if (_cache[i]._ref) {
|
||||
_cache[i]._ref = 0;
|
||||
delete[] _cache[i]._text;
|
||||
_cache[i]._text = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Text::load() {
|
||||
EncryptedStream tf(_vm->_resman, _fileName);
|
||||
assert(!tf.err());
|
||||
|
||||
Common::String line;
|
||||
char tmpStr[kLineMax + 1];
|
||||
int idx;
|
||||
|
||||
for (idx = 0, line = tf.readLine(); !tf.eos(); line = tf.readLine()) {
|
||||
int n = line.size();
|
||||
char *s;
|
||||
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
|
||||
if ((s = strtok(tmpStr, " =,;/\t\n")) == nullptr)
|
||||
continue;
|
||||
if (!Common::isDigit(*s))
|
||||
continue;
|
||||
|
||||
int r = _vm->number(s);
|
||||
|
||||
s += strlen(s);
|
||||
if (s < tmpStr + n)
|
||||
++s;
|
||||
|
||||
_cache[idx]._ref = r;
|
||||
size_t ln = strlen(s) + 1;
|
||||
_cache[idx]._text = new char[ln];
|
||||
Common::strcpy_s(_cache[idx]._text, ln, s);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
char *Text::getText(int ref) {
|
||||
int i;
|
||||
for (i = 0; (i < _size) && (_cache[i]._ref != ref); i++)
|
||||
;
|
||||
|
||||
if (i < _size)
|
||||
return _cache[i]._text;
|
||||
|
||||
warning("getText: Unable to find ref %d:%d", ref / 256, ref % 256);
|
||||
return _cache[0]._text;
|
||||
}
|
||||
|
||||
void Text::say(const char *text, Sprite *spr) {
|
||||
_vm->killText();
|
||||
|
||||
_vm->_talk = new Talk(_vm, text, kTBRound, kCBSay);
|
||||
|
||||
Speaker *speaker = new Speaker(_vm);
|
||||
|
||||
bool east = spr->_flags._east;
|
||||
V2D d(_vm, 20, spr->_siz.y - 2);
|
||||
if (!east)
|
||||
d.x = -d.x;
|
||||
if (_vm->isHero(spr))
|
||||
d = d.scale(spr->_pos3D._z.trunc());
|
||||
V2D pos = spr->_pos2D + d;
|
||||
uint16 sw = (speaker->_siz.x >> 1);
|
||||
if (!east)
|
||||
sw = -sw;
|
||||
|
||||
if (east) {
|
||||
if (pos.x + sw + kTextRoundCorner + kCaptionSide >= kScrWidth)
|
||||
east = false;
|
||||
} else if (pos.x <= kCaptionSide + kTextRoundCorner - sw)
|
||||
east = true;
|
||||
|
||||
if (east != (d.x > 0)) {
|
||||
d.x = -d.x;
|
||||
sw = -sw;
|
||||
}
|
||||
pos.x = spr->_pos2D.x + d.x + sw;
|
||||
|
||||
_vm->_talk->_flags._kill = true;
|
||||
_vm->_talk->setName(getText(kSayName));
|
||||
_vm->_talk->gotoxyz(pos.x, pos.y + speaker->_siz.y - 1, 0);
|
||||
|
||||
speaker->gotoxyz(pos.x, _vm->_talk->_pos3D._y.trunc() - speaker->_siz.y + 1, 0);
|
||||
speaker->_flags._slav = true;
|
||||
speaker->_flags._kill = true;
|
||||
speaker->setName(getText(kSayName));
|
||||
speaker->step(east);
|
||||
|
||||
_vm->_vga->_showQ->append(_vm->_talk);
|
||||
_vm->_vga->_showQ->append(speaker);
|
||||
}
|
||||
|
||||
void CGE2Engine::inf(const char *text, ColorBank col) {
|
||||
killText();
|
||||
_talk = new Talk(this, text, kTBRect, col, true);
|
||||
_talk->_flags._kill = true;
|
||||
_talk->setName(_text->getText(kInfName));
|
||||
_talk->center();
|
||||
_vga->_showQ->append(_talk);
|
||||
}
|
||||
|
||||
void Text::sayTime(Sprite *spr) {
|
||||
TimeDate curTime;
|
||||
_vm->_system->getTimeAndDate(curTime);
|
||||
|
||||
char t[6];
|
||||
snprintf(t, 6, "%d:%02d", curTime.tm_hour, curTime.tm_min);
|
||||
say(t, spr);
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
67
engines/cge2/text.h
Normal file
67
engines/cge2/text.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_TEXT_H
|
||||
#define CGE2_TEXT_H
|
||||
|
||||
#include "cge2/talk.h"
|
||||
#include "cge2/cge2.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kSayExt ".SAY"
|
||||
#define kSysTextMax 1000
|
||||
#define kTextNoMouse 95
|
||||
#define kInfName 101
|
||||
#define kSayName 102
|
||||
#define kInfRef 301
|
||||
#define kSayRef 302
|
||||
|
||||
|
||||
class Text {
|
||||
struct Handler {
|
||||
int _ref;
|
||||
char *_text;
|
||||
} *_cache;
|
||||
int _size;
|
||||
int16 _txtCount;
|
||||
char _fileName[kPathMax];
|
||||
void load();
|
||||
int16 count();
|
||||
public:
|
||||
Text(CGE2Engine *vm, const char *fname);
|
||||
~Text();
|
||||
void clear();
|
||||
char *getText(int ref);
|
||||
void say(const char *text, Sprite *spr);
|
||||
void sayTime(Sprite *spr);
|
||||
private:
|
||||
CGE2Engine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_TEXT_H
|
||||
223
engines/cge2/toolbar.cpp
Normal file
223
engines/cge2/toolbar.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "sound.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/events.h"
|
||||
#include "cge2/vmenu.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/cge2_main.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kSoundNumToStateRate 25.7
|
||||
// == 257 / 10; where 10 equals to the volume switches' number of states [0..9]
|
||||
// and ScummVM has a scale of 257 different values for setting sounds.
|
||||
|
||||
#define kSoundStateToNumRate 28.45
|
||||
// == 256 / 9 + 0.1; where 256 is the positive range of numbers we can set the volume to
|
||||
// and the 10 states of a switch cut this range up to 9 equally big parts.
|
||||
// We don't take into account 0 regarding the 256 different values (it would be the 257th),
|
||||
// since 0 * x == 0.
|
||||
// 0.1 is only for correct rounding at the 10th state.
|
||||
|
||||
void CGE2Engine::optionTouch(int opt, uint16 mask) {
|
||||
bool notMuted = !ConfMan.getBool("mute");
|
||||
switch (opt) {
|
||||
case 1:
|
||||
if (mask & kMouseLeftUp)
|
||||
switchColorMode();
|
||||
break;
|
||||
case 2:
|
||||
if ((mask & kMouseLeftUp) && notMuted)
|
||||
switchMusic();
|
||||
break;
|
||||
case 3:
|
||||
if (mask & kMouseLeftUp)
|
||||
quit();
|
||||
break;
|
||||
case 4:
|
||||
if ((mask & (kMouseLeftUp | kMouseRightUp)) && notMuted)
|
||||
setVolume(opt - 4, (mask & kMouseLeftUp) ? 1 : -1);
|
||||
break;
|
||||
case 5:
|
||||
if ((mask & (kMouseLeftUp | kMouseRightUp)) && notMuted)
|
||||
setVolume(opt - 4, (mask & kMouseLeftUp) ? 1 : -1);
|
||||
break;
|
||||
case 8:
|
||||
if ((mask & kMouseLeftUp) && notMuted)
|
||||
switchCap();
|
||||
break;
|
||||
case 9:
|
||||
if ((mask & kMouseLeftUp) && notMuted)
|
||||
switchVox();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::switchColorMode() {
|
||||
_commandHandlerTurbo->addCommand(kCmdSeq, 121, _vga->_mono = !_vga->_mono, nullptr);
|
||||
ConfMan.setBool("enable_color_blind", _vga->_mono);
|
||||
ConfMan.flushToDisk();
|
||||
keyClick();
|
||||
_vga->setColors(_vga->_sysPal, 64);
|
||||
}
|
||||
|
||||
void CGE2Engine::switchMusic() {
|
||||
_music = !_music;
|
||||
_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, !_music);
|
||||
_commandHandlerTurbo->addCommand(kCmdSeq, kMusicRef, _music, nullptr);
|
||||
keyClick();
|
||||
_commandHandlerTurbo->addCommand(kCmdMidi, -1, _music ? (_now << 8) : -1, nullptr);
|
||||
}
|
||||
|
||||
void CGE2Engine::quit() {
|
||||
if (_commandHandler->idle()) {
|
||||
if (VMenu::_addr) {
|
||||
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, VMenu::_addr);
|
||||
ReturnToGameChoice rqsChoice(this);
|
||||
rqsChoice.proc();
|
||||
} else {
|
||||
Common::Array<Choice *> quitMenu; // Deleted in VMenu's destructor.
|
||||
quitMenu.push_back(new ExitGameChoice(this));
|
||||
quitMenu.push_back(new ReturnToGameChoice(this));
|
||||
(new VMenu(this, quitMenu, V2D(this, -1, -1), kCBMnu))->setName(_text->getText(kQuitTitle));
|
||||
_commandHandlerTurbo->addCommand(kCmdSeq, kPowerRef, 0, nullptr);
|
||||
keyClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::setVolume(int idx, int cnt) {
|
||||
if (cnt && _vol[idx]) {
|
||||
int p = _vol[idx]->_seqPtr + cnt;
|
||||
if ((p >= 0) && (p < _vol[idx]->_seqCnt)) {
|
||||
_vol[idx]->step(p);
|
||||
int newVolume = (int)(p * kSoundStateToNumRate);
|
||||
switch (idx) {
|
||||
case 0:
|
||||
_oldSfxVolume = ConfMan.getInt("sfx_volume");
|
||||
ConfMan.setInt("sfx_volume", newVolume);
|
||||
break;
|
||||
case 1:
|
||||
_oldMusicVolume = ConfMan.getInt("music_volume");
|
||||
ConfMan.setInt("music_volume", newVolume);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::checkVolumeSwitches() {
|
||||
int musicVolume = ConfMan.getInt("music_volume");
|
||||
if (musicVolume != _oldMusicVolume)
|
||||
_vol[1]->step((int)(musicVolume / kSoundNumToStateRate));
|
||||
|
||||
int sfxVolume = ConfMan.getInt("sfx_volume");
|
||||
if (sfxVolume != _oldSfxVolume)
|
||||
_vol[0]->step((int)(sfxVolume / kSoundNumToStateRate));
|
||||
}
|
||||
|
||||
void CGE2Engine::switchCap() {
|
||||
_sayCap = !_sayCap;
|
||||
if (!_sayCap)
|
||||
_sayVox = true;
|
||||
keyClick();
|
||||
switchSay();
|
||||
}
|
||||
|
||||
void CGE2Engine::switchVox() {
|
||||
_sayVox = !_sayVox;
|
||||
_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, _sayVox);
|
||||
if (!_sayVox)
|
||||
_sayCap = true;
|
||||
keyClick();
|
||||
switchSay();
|
||||
}
|
||||
|
||||
void CGE2Engine::switchSay() {
|
||||
_commandHandlerTurbo->addCommand(kCmdSeq, 129, _sayVox, nullptr);
|
||||
_commandHandlerTurbo->addCommand(kCmdSeq, 128, _sayCap, nullptr);
|
||||
}
|
||||
|
||||
void CGE2Engine::initToolbar() {
|
||||
selectPocket(-1);
|
||||
|
||||
_commandHandlerTurbo->addCommand(kCmdSeq, kMusicRef, _music, nullptr);
|
||||
if (!_music)
|
||||
_midiPlayer->killMidi();
|
||||
|
||||
switchSay();
|
||||
|
||||
_infoLine->gotoxyz(V3D(kInfoX, kInfoY, 0));
|
||||
_infoLine->setText(nullptr);
|
||||
_vga->_showQ->insert(_infoLine);
|
||||
|
||||
_gamePhase = kPhaseInGame;
|
||||
_mouse->center();
|
||||
_mouse->off();
|
||||
_mouse->on();
|
||||
|
||||
_keyboard->setClient(_sys);
|
||||
_commandHandler->addCommand(kCmdSeq, kPowerRef, 1, nullptr);
|
||||
|
||||
_busyPtr = _vga->_showQ->locate(kBusyRef);
|
||||
|
||||
_vol[0] = _vga->_showQ->locate(kDvolRef);
|
||||
_vol[1] = _vga->_showQ->locate(kMvolRef);
|
||||
|
||||
if (_vol[0]) {
|
||||
int val = ConfMan.getInt("sfx_volume");
|
||||
initVolumeSwitch(_vol[0], val);
|
||||
}
|
||||
|
||||
if (_vol[1]) {
|
||||
int val = ConfMan.getInt("music_volume");
|
||||
initVolumeSwitch(_vol[1], val);
|
||||
}
|
||||
}
|
||||
|
||||
void CGE2Engine::initVolumeSwitch(Sprite *volSwitch, int val) {
|
||||
int state = 0;
|
||||
state = (int)(val / kSoundNumToStateRate);
|
||||
volSwitch->step(state);
|
||||
}
|
||||
|
||||
void CGE2Engine::checkMute() {
|
||||
bool mute = ConfMan.getBool("mute");
|
||||
if (mute != _muteAll) {
|
||||
switchMusic();
|
||||
switchVox();
|
||||
_muteAll = mute;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
1224
engines/cge2/vga13h.cpp
Normal file
1224
engines/cge2/vga13h.cpp
Normal file
File diff suppressed because it is too large
Load Diff
306
engines/cge2/vga13h.h
Normal file
306
engines/cge2/vga13h.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_VGA13H_H
|
||||
#define CGE2_VGA13H_H
|
||||
|
||||
#include "common/serializer.h"
|
||||
#include "common/events.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "cge2/general.h"
|
||||
#include "cge2/bitmap.h"
|
||||
#include "cge2/snail.h"
|
||||
#include "cge2/spare.h"
|
||||
#include "cge2/cge2.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
#define kFadeStep 2
|
||||
#define kVgaColDark 207
|
||||
#define kVgaColDarkGray 225 /*219*/
|
||||
#define kVgaColGray 231
|
||||
#define kVgaColLightGray 237
|
||||
#define kPixelTransp 0xFE
|
||||
#define kNoSeq (-1)
|
||||
#define kNoPtr ((uint8)-1)
|
||||
#define kSprExt ".SPR"
|
||||
#define kPalCount 256
|
||||
#define kPalSize (kPalCount * 3)
|
||||
|
||||
class FXP {
|
||||
int32 v;
|
||||
public:
|
||||
FXP(void) : v(0) {}
|
||||
FXP (int i0, int f0 = 0) : v((i0 * 256) + ((i0 < 0) ? -f0 : f0)) {}
|
||||
FXP &operator=(const int &x) { v = x << 8; return *this; }
|
||||
FXP operator+(const FXP &x) const { FXP y; y.v = v + x.v; return y; }
|
||||
FXP operator-(const FXP &x) const { FXP y; y.v = v - x.v; return y; }
|
||||
FXP operator*(const FXP &x) const;
|
||||
FXP operator/(const FXP &x) const;
|
||||
|
||||
friend int &operator+=(int &a, const FXP &b) { return a += b.trunc(); }
|
||||
friend int &operator-=(int &a, const FXP &b) { return a -= b.trunc(); }
|
||||
friend FXP &operator+=(FXP &a, const int &b) { a.v += b << 8; return a; }
|
||||
friend FXP &operator-=(FXP &a, const int &b) { a.v -= b << 8; return a; }
|
||||
friend bool operator==(const FXP &a, const FXP &b) { return a.v == b.v; }
|
||||
friend bool operator!=(const FXP &a, const FXP &b) { return a.v != b.v; }
|
||||
friend bool operator<(const FXP &a, const FXP &b) { return a.v < b.v; }
|
||||
friend bool operator>(const FXP &a, const FXP &b) { return a.v > b.v; }
|
||||
int trunc(void) const { return v >> 8; }
|
||||
int round(void) const { return (v + 0x80) >> 8; }
|
||||
bool empty() const { return v == 0; }
|
||||
void sync(Common::Serializer &s);
|
||||
};
|
||||
|
||||
class V3D {
|
||||
public:
|
||||
FXP _x, _y, _z;
|
||||
V3D() { }
|
||||
V3D(FXP x, FXP y, FXP z = 0) : _x(x), _y(y), _z(z) { }
|
||||
V3D operator+(const V3D &p) const { return V3D(_x + p._x, _y + p._y, _z + p._z); }
|
||||
V3D operator-(const V3D &p) const { return V3D(_x - p._x, _y - p._y, _z - p._z); }
|
||||
V3D operator*(long n) const { return V3D(_x * n, _y * n, _z * n); }
|
||||
V3D operator/ (long n) const { return V3D(_x / n, _y / n, _z / n); }
|
||||
bool operator==(const V3D &p) const { return _x == p._x && _y == p._y && _z == p._z; }
|
||||
bool operator!=(const V3D &p) const { return _x != p._x || _y != p._y || _z != p._z; }
|
||||
V3D& operator+=(const V3D &x) { return *this = *this + x; }
|
||||
V3D& operator-=(const V3D &x) { return *this = *this - x; }
|
||||
void sync(Common::Serializer &s);
|
||||
};
|
||||
|
||||
class V2D : public Common::Point {
|
||||
CGE2Engine *_vm;
|
||||
public:
|
||||
V2D &operator=(const V3D &p3) {
|
||||
FXP m = _vm->_eye->_z / (p3._z - _vm->_eye->_z);
|
||||
FXP posx = _vm->_eye->_x + (_vm->_eye->_x - p3._x) * m;
|
||||
x = posx.round();
|
||||
FXP posy = _vm->_eye->_y + (_vm->_eye->_y - p3._y) * m;
|
||||
y = posy.round();
|
||||
return *this;
|
||||
}
|
||||
V2D(CGE2Engine *vm) : _vm(vm) { }
|
||||
V2D(CGE2Engine *vm, const V3D &p3) : _vm(vm) { *this = p3; }
|
||||
V2D(CGE2Engine *vm, int posx, int posy) : _vm(vm), Common::Point(posx, posy) { }
|
||||
bool operator<(const V2D &p) const { return (x < p.x) && (y < p.y); }
|
||||
bool operator<=(const V2D &p) const { return (x <= p.x) && (y <= p.y); }
|
||||
bool operator>(const V2D &p) const { return (x > p.x) && (y > p.y); }
|
||||
bool operator>=(const V2D &p) const { return (x >= p.x) && (y >= p.y); }
|
||||
V2D operator+(const V2D &p) const { return V2D(_vm, x + p.x, y + p.y); }
|
||||
V2D operator-(const V2D &p) const { return V2D(_vm, x - p.x, y - p.y); }
|
||||
bool operator==(const V3D &p) const { V3D tmp(x, y); return tmp._x == p._x && tmp._y == p._y && tmp._z == p._z; }
|
||||
bool operator!=(const V3D &p) const { V3D tmp(x, y); return tmp._x != p._x || tmp._y != p._y || tmp._z == p._z; }
|
||||
bool operator==(const V2D &p) const { return x == p.x && y == p.y; }
|
||||
uint16 area() { return x * y; }
|
||||
bool limited(const V2D &p) {
|
||||
return ((x < p.x) && (y < p.y));
|
||||
}
|
||||
V2D scale (int z) {
|
||||
FXP m = _vm->_eye->_z / (_vm->_eye->_z - z);
|
||||
FXP posx = m * x;
|
||||
FXP posy = m * y;
|
||||
return V2D(_vm, posx.trunc(), posy.trunc());
|
||||
}
|
||||
};
|
||||
|
||||
struct Seq {
|
||||
uint8 _now;
|
||||
uint8 _next;
|
||||
int8 _dx;
|
||||
int8 _dy;
|
||||
int8 _dz;
|
||||
int _dly;
|
||||
};
|
||||
|
||||
class SprExt {
|
||||
public:
|
||||
V2D _p0;
|
||||
V2D _p1;
|
||||
BitmapPtr _b0;
|
||||
BitmapPtr _b1;
|
||||
BitmapPtr _shpList;
|
||||
int _location;
|
||||
Seq *_seq;
|
||||
char *_name;
|
||||
CommandHandler::Command *_actions[kActions];
|
||||
SprExt(CGE2Engine *vm);
|
||||
};
|
||||
|
||||
class Sprite {
|
||||
protected:
|
||||
SprExt *_ext;
|
||||
CGE2Engine *_vm;
|
||||
public:
|
||||
int _ref;
|
||||
signed char _scene;
|
||||
struct Flags {
|
||||
bool _hide; // general visibility switch
|
||||
bool _drag; // sprite is moveable
|
||||
bool _hold; // sprite is held with mouse
|
||||
bool _trim; // Trim flag
|
||||
bool _slav; // slave object
|
||||
bool _kill; // dispose memory after remove
|
||||
bool _xlat; // 2nd way display: xlat table
|
||||
bool _port; // portable
|
||||
bool _kept; // kept in pocket
|
||||
bool _frnt; // stay in front of sprite
|
||||
bool _east; // talk to east (in opposite to west)
|
||||
bool _near; // Near action lock
|
||||
bool _shad; // shadow
|
||||
bool _back; // 'send to background' request
|
||||
bool _zmov; // sprite needs Z-update in queue
|
||||
bool _tran; // transparent (untouchable)
|
||||
} _flags;
|
||||
V2D _pos2D;
|
||||
V3D _pos3D;
|
||||
V2D _siz;
|
||||
uint16 _time;
|
||||
struct { byte _ptr, _cnt; } _actionCtrl[kActions];
|
||||
int _seqPtr;
|
||||
int _seqCnt;
|
||||
int _shpCnt;
|
||||
char _file[kMaxFile];
|
||||
// Following trailer is not saved with the game:
|
||||
Sprite *_prev;
|
||||
Sprite *_next;
|
||||
static byte _constY;
|
||||
static byte _follow;
|
||||
static Seq _stdSeq8[];
|
||||
|
||||
bool works(Sprite *spr);
|
||||
bool seqTest(int n);
|
||||
inline bool active() {
|
||||
return _ext != nullptr;
|
||||
}
|
||||
Sprite(CGE2Engine *vm);
|
||||
Sprite(CGE2Engine *vm, BitmapPtr shp, int cnt);
|
||||
virtual ~Sprite();
|
||||
BitmapPtr getShp();
|
||||
void setShapeList(BitmapPtr shp, int cnt);
|
||||
void moveShapesHi();
|
||||
void moveShapesLo();
|
||||
int labVal(Action snq, int lab);
|
||||
virtual Sprite *expand();
|
||||
virtual Sprite *contract();
|
||||
void backShow();
|
||||
void setName(char *newName);
|
||||
inline char *name() {
|
||||
return (_ext) ? _ext->_name : nullptr;
|
||||
}
|
||||
void gotoxyz(int x, int y, int z = 0);
|
||||
void gotoxyz();
|
||||
void gotoxyz(V2D pos);
|
||||
void gotoxyz_(V2D pos);
|
||||
void gotoxyz(V3D pos);
|
||||
void center();
|
||||
void show(uint16 pg);
|
||||
void hide(uint16 pg);
|
||||
void show();
|
||||
void hide();
|
||||
BitmapPtr ghost();
|
||||
void step(int nr = -1);
|
||||
Seq *setSeq(Seq *seq);
|
||||
CommandHandler::Command *snList(Action type);
|
||||
virtual void touch(uint16 mask, V2D pos);
|
||||
virtual void tick();
|
||||
virtual void setScene(int c);
|
||||
void clrHide() { if (_ext) _ext->_b0 = nullptr; }
|
||||
|
||||
void sync(Common::Serializer &s);
|
||||
|
||||
static void (*notify) ();
|
||||
};
|
||||
|
||||
class Queue {
|
||||
Sprite *_head;
|
||||
Sprite *_tail;
|
||||
public:
|
||||
Queue(bool show);
|
||||
|
||||
void append(Sprite *spr);
|
||||
void insert(Sprite *spr, Sprite *nxt);
|
||||
void insert(Sprite *spr);
|
||||
Sprite *remove(Sprite *spr);
|
||||
Sprite *first() {
|
||||
return _head;
|
||||
}
|
||||
Sprite *last() {
|
||||
return _tail;
|
||||
}
|
||||
Sprite *locate(int ref);
|
||||
bool locate(Sprite *spr);
|
||||
void clear() { _head = _tail = nullptr; }
|
||||
};
|
||||
|
||||
class Vga {
|
||||
CGE2Engine *_vm;
|
||||
bool _setPal;
|
||||
Dac *_oldColors;
|
||||
Dac *_newColors;
|
||||
const char *_msg;
|
||||
const char *_name;
|
||||
|
||||
void updateColors();
|
||||
void setColors();
|
||||
void waitVR();
|
||||
uint8 closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB);
|
||||
|
||||
public:
|
||||
uint32 _frmCnt;
|
||||
Queue *_showQ;
|
||||
bool _mono;
|
||||
Graphics::Surface *_page[4];
|
||||
Dac *_sysPal;
|
||||
struct { uint8 _org, _len, _cnt, _dly; } _rot;
|
||||
|
||||
Vga(CGE2Engine *vm);
|
||||
~Vga();
|
||||
|
||||
uint8 *glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB);
|
||||
void getColors(Dac *tab);
|
||||
void setColors(Dac *tab, int lum);
|
||||
void clear(uint8 color);
|
||||
void copyPage(uint16 d, uint16 s);
|
||||
void sunrise(Dac *tab);
|
||||
void sunset();
|
||||
void show();
|
||||
void update();
|
||||
void rotate();
|
||||
uint8 closest(Dac *pal, Dac x);
|
||||
|
||||
void palToDac(const byte *palData, Dac *tab);
|
||||
void dacToPal(const Dac *tab, byte *palData);
|
||||
};
|
||||
|
||||
class Speaker: public Sprite {
|
||||
CGE2Engine *_vm;
|
||||
public:
|
||||
Speaker(CGE2Engine *vm);
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_VGA13H_H
|
||||
174
engines/cge2/vmenu.cpp
Normal file
174
engines/cge2/vmenu.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/text-to-speech.h"
|
||||
#include "cge2/text.h"
|
||||
#include "cge2/vmenu.h"
|
||||
#include "cge2/events.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
Choice::Choice(CGE2Engine *vm) : _vm(vm), _text(nullptr) {}
|
||||
|
||||
ExitGameChoice::ExitGameChoice(CGE2Engine *vm) : Choice(vm) {
|
||||
_text = _vm->_text->getText(kQuitText);
|
||||
}
|
||||
|
||||
void ExitGameChoice::proc() {
|
||||
_vm->switchScene(-1);
|
||||
}
|
||||
|
||||
ReturnToGameChoice::ReturnToGameChoice(CGE2Engine *vm) : Choice(vm) {
|
||||
_text = _vm->_text->getText(kNoQuitText);
|
||||
}
|
||||
|
||||
void ReturnToGameChoice::proc() {
|
||||
_vm->_commandHandlerTurbo->addCommand(kCmdSeq, kPowerRef, 1, nullptr);
|
||||
_vm->keyClick();
|
||||
}
|
||||
|
||||
MenuBar::MenuBar(CGE2Engine *vm, uint16 w, byte *c) : Talk(vm) {
|
||||
_color = c;
|
||||
int h = kFontHigh + 2 * kMenuBarVerticalMargin, i = (w += 2 * kMenuBarHorizontalMargin) * h;
|
||||
uint8 *p = new uint8[i];
|
||||
uint8 *p1;
|
||||
uint8 *p2;
|
||||
uint8 lt = _color[kLt];
|
||||
uint8 rb = _color[kRb];
|
||||
BitmapPtr b;
|
||||
|
||||
memset(p + w, kPixelTransp, i - 2 * w);
|
||||
memset(p, lt, w);
|
||||
memset(p + i - w, rb, w);
|
||||
p1 = p;
|
||||
p2 = p + i - 1;
|
||||
for (i = 0; i < h; i++) {
|
||||
*p1 = lt;
|
||||
*p2 = rb;
|
||||
p1 += w;
|
||||
p2 -= w;
|
||||
}
|
||||
b = new Bitmap[1];
|
||||
b[0] = Bitmap(vm, w, h, p);
|
||||
delete[] p;
|
||||
setShapeList(b, 1);
|
||||
_flags._slav = true;
|
||||
_flags._tran = true;
|
||||
_flags._kill = true;
|
||||
}
|
||||
|
||||
VMenu *VMenu::_addr = nullptr;
|
||||
|
||||
VMenu::VMenu(CGE2Engine *vm, Common::Array<Choice *> list, V2D pos, ColorBank col)
|
||||
: Talk(vm, vmGather(list), kTBRect, col), _menu(list), _bar(nullptr), _items(list.size()), _vm(vm), _lastN(2) {
|
||||
delete[] _vmgt; // Lefotver of vmGather.
|
||||
|
||||
_addr = this;
|
||||
_recent = -1;
|
||||
_flags._kill = true;
|
||||
|
||||
if (pos.x < 0 || pos.y < 0)
|
||||
center();
|
||||
else
|
||||
gotoxyz(V2D(_vm, pos.x - _siz.x / 2, pos.y - (kTextVMargin + kFontHigh / 2)));
|
||||
|
||||
_vm->_vga->_showQ->append(this);
|
||||
_bar = new MenuBar(_vm, _siz.x - 2 * kTextHMargin, _color);
|
||||
_bar->gotoxyz(V2D(_vm, _pos2D.x, _pos2D.y + kTextVMargin - kMenuBarVerticalMargin));
|
||||
_vm->_vga->_showQ->append(_bar);
|
||||
}
|
||||
|
||||
char *VMenu::vmGather(Common::Array<Choice *> list) {
|
||||
int len = 0;
|
||||
int h = 0;
|
||||
|
||||
for (uint i = 0; i < list.size(); i++) {
|
||||
len += strlen(list[i]->_text);
|
||||
++h;
|
||||
}
|
||||
len += h;
|
||||
_vmgt = new char[len];
|
||||
|
||||
if (len) {
|
||||
*_vmgt = '\0';
|
||||
for (uint i = 0; i < list.size(); i++) {
|
||||
if (*_vmgt)
|
||||
Common::strcat_s(_vmgt, len, "|");
|
||||
Common::strcat_s(_vmgt, len, list[i]->_text);
|
||||
++h;
|
||||
}
|
||||
}
|
||||
|
||||
return _vmgt;
|
||||
}
|
||||
|
||||
|
||||
VMenu::~VMenu() {
|
||||
_addr = nullptr;
|
||||
|
||||
for (uint i = 0; i < _menu.size(); i++) {
|
||||
delete _menu[i];
|
||||
}
|
||||
}
|
||||
|
||||
void VMenu::touch(uint16 mask, V2D pos) {
|
||||
if (_items) {
|
||||
Sprite::touch(mask, pos);
|
||||
|
||||
int n = 0;
|
||||
bool ok = false;
|
||||
int h = kFontHigh + kTextLineSpace;
|
||||
pos.y -= kTextVMargin - 1;
|
||||
if (pos.y >= 0) {
|
||||
if (pos.x < 0)
|
||||
pos.x = -pos.x;
|
||||
n = pos.y / h;
|
||||
if (n < _items)
|
||||
ok = (pos.x <= (_siz.x >> 1) - kTextHMargin);
|
||||
else
|
||||
n = _items - 1;
|
||||
}
|
||||
|
||||
_bar->gotoxyz(V2D(_vm, _pos2D.x, _pos2D.y + kTextVMargin + n * h - kMenuBarVerticalMargin));
|
||||
n = _items - 1 - n;
|
||||
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (_lastN != n) {
|
||||
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled_objects"))
|
||||
ttsMan->say(_menu[n]->_text, Common::TextToSpeechManager::INTERRUPT);
|
||||
_lastN = n;
|
||||
}
|
||||
|
||||
if (ok && (mask & kMouseLeftUp)) {
|
||||
_items = 0;
|
||||
_vm->_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, this);
|
||||
_menu[_recent = n]->proc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace CGE2
|
||||
89
engines/cge2/vmenu.h
Normal file
89
engines/cge2/vmenu.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Sfinx source code
|
||||
* Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
|
||||
*/
|
||||
|
||||
#ifndef CGE2_VMENU_H
|
||||
#define CGE2_VMENU_H
|
||||
|
||||
#define kMenuBarVerticalMargin 1
|
||||
#define kMenuBarHorizontalMargin 3
|
||||
#define kLt 3
|
||||
#define kRb 1
|
||||
|
||||
#include "cge2/cge2.h"
|
||||
#include "cge2/talk.h"
|
||||
|
||||
namespace CGE2 {
|
||||
|
||||
class Choice {
|
||||
protected:
|
||||
CGE2Engine *_vm;
|
||||
public:
|
||||
char *_text;
|
||||
|
||||
virtual void proc() = 0;
|
||||
|
||||
Choice(CGE2Engine *vm);
|
||||
virtual ~Choice() {};
|
||||
};
|
||||
|
||||
class ExitGameChoice : public Choice {
|
||||
public:
|
||||
ExitGameChoice(CGE2Engine *vm);
|
||||
void proc() override;
|
||||
};
|
||||
|
||||
class ReturnToGameChoice : public Choice {
|
||||
public:
|
||||
ReturnToGameChoice(CGE2Engine *vm);
|
||||
void proc() override;
|
||||
};
|
||||
|
||||
class MenuBar : public Talk {
|
||||
public:
|
||||
MenuBar(CGE2Engine *vm, uint16 w, byte *c);
|
||||
};
|
||||
|
||||
class VMenu : public Talk {
|
||||
CGE2Engine *_vm;
|
||||
|
||||
uint16 _items;
|
||||
Common::Array<Choice *> _menu;
|
||||
public:
|
||||
char *_vmgt;
|
||||
static VMenu *_addr;
|
||||
int _recent;
|
||||
int _lastN;
|
||||
MenuBar *_bar;
|
||||
|
||||
VMenu(CGE2Engine *vm, Common::Array<Choice *> list, V2D pos, ColorBank col);
|
||||
~VMenu() override;
|
||||
void touch(uint16 mask, V2D pos) override;
|
||||
char *vmGather(Common::Array<Choice *> list);
|
||||
};
|
||||
|
||||
} // End of namespace CGE2
|
||||
|
||||
#endif // CGE2_VMENU_H
|
||||
Reference in New Issue
Block a user