Initial commit

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

View File

@@ -0,0 +1,3 @@
engines/teenagent/detection.cpp
engines/teenagent/resources.cpp
engines/teenagent/metaengine.cpp

151
engines/teenagent/actor.cpp Normal file
View File

@@ -0,0 +1,151 @@
/* 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 "teenagent/actor.h"
#include "teenagent/objects.h"
#include "teenagent/resources.h"
#include "teenagent/teenagent.h"
#include "common/random.h"
#include "common/textconsole.h"
namespace TeenAgent {
Actor::Actor(TeenAgentEngine *vm) : _vm(vm), headIndex(0), idleType(0) {}
Common::Rect Actor::renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, uint zoom, Common::RandomSource &rnd) {
if (index == 0) {
idleType = rnd.getRandomNumber(2);
debugC(kDebugActor, "switched to idle animation %u", idleType);
}
byte *framesIdle;
do {
framesIdle = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_idleAnimationListPtr + idleType * 2)) + index;
index += deltaFrame;
if (*framesIdle == 0) {
idleType = rnd.getRandomNumber(2);
debugC(kDebugActor, "switched to idle animation %u[loop]", idleType);
index = 3; //put 4th frame (base 1) if idle animation loops
}
} while (*framesIdle == 0);
bool mirror = orientation == kActorLeft;
Surface *s = frames + *framesIdle - 1;
//TODO: remove copy-paste here and below
int xp = position.x - s->w * zoom / 512 - s->x, yp = position.y - 62 * zoom / 256 - s->y; //hardcoded in original game
return s->render(surface, xp, yp, mirror, Common::Rect(), zoom);
}
Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, bool renderHead, uint zoom) {
const uint8 framesLeftRight[] = {0, 1, 2, 3, 4, 5, /* step */ 6, 7, 8, 9};
const uint8 framesUp[] = {18, 19, 20, 21, 22, 23, 24, 25, };
const uint8 framesDown[] = {10, 11, 12, 13, 14, 15, 16, 17, };
const uint8 framesHeadLeftRight[] = {
0x27, 0x1a, 0x1b,
0x27, 0x1c, 0x1d,
0x27, 0x1a,
0x27, 0x1e, 0x1f,
0x27, 0x1a, 0x1b,
0x27, 0x1c,
0x27, 0x1e,
0x27, 0x1a,
};
const uint8 framesHeadUp[] = {
0x29, 0x25, 0x29, 0x29,
0x26, 0x29, 0x26, 0x29,
0x29, 0x25, 0x29, 0x25,
0x29, 0x29, 0x29, 0x25,
0x25, 0x29, 0x29, 0x26
};
const uint8 framesHeadDown[] = {
0x20, 0x21, 0x22, 0x23,
0x28, 0x24, 0x28, 0x28,
0x24, 0x28, 0x20, 0x21,
0x20, 0x23, 0x28, 0x20,
0x28, 0x28, 0x20, 0x28
};
Surface *s = NULL, *head = NULL;
bool mirror = orientation == kActorLeft;
index += deltaFrame;
switch (orientation) {
case kActorLeft:
case kActorRight:
if (renderHead) {
if (headIndex >= ARRAYSIZE(framesHeadLeftRight))
headIndex = 0;
head = frames + framesHeadLeftRight[headIndex];
++headIndex;
}
if (index >= ARRAYSIZE(framesLeftRight))
index = 1;
s = frames + framesLeftRight[index];
break;
case kActorUp:
if (renderHead) {
if (headIndex >= ARRAYSIZE(framesHeadUp))
headIndex = 0;
head = frames + framesHeadUp[headIndex];
++headIndex;
}
if (index >= ARRAYSIZE(framesUp))
index = 1;
s = frames + framesUp[index];
break;
case kActorDown:
if (renderHead) {
if (headIndex >= ARRAYSIZE(framesHeadDown))
headIndex = 0;
head = frames + framesHeadDown[headIndex];
++headIndex;
}
if (index >= ARRAYSIZE(framesDown))
index = 1;
s = frames + framesDown[index];
break;
default:
return Common::Rect();
}
Common::Rect dirty;
Common::Rect clip(0, 0, s->w, s->h);
if (head != NULL)
clip.top = head->h;
int xp = position.x - s->w * zoom / 512 - s->x, yp = position.y - s->h * zoom / 256 - s->y;
dirty = s->render(surface, xp, yp + clip.top * zoom / 256, mirror, clip, zoom);
if (head != NULL)
dirty.extend(head->render(surface, xp, yp, orientation == kActorLeft, Common::Rect(), zoom));
return dirty;
}
} // End of namespace TeenAgent

47
engines/teenagent/actor.h Normal file
View 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 "teenagent/animation.h"
#include "common/rect.h"
namespace Common {
class RandomSource;
}
namespace TeenAgent {
class TeenAgentEngine;
class Actor : public Animation {
private:
TeenAgentEngine *_vm;
uint headIndex;
uint idleType;
public:
Actor(TeenAgentEngine *vm);
Common::Rect render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, bool renderHead, uint zoom);
Common::Rect renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, uint zoom, Common::RandomSource &rnd);
};
} // End of namespace TeenAgent

View File

@@ -0,0 +1,209 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "teenagent/teenagent.h"
#include "teenagent/animation.h"
#include "common/endian.h"
#include "common/textconsole.h"
namespace TeenAgent {
Animation::Animation() : id(0), x(0), y(0), loop(true), paused(false), ignore(false), data(0), dataSize(0), framesCount(0), frames(0), index(0) {
}
Animation::~Animation() {
free();
}
Surface *Animation::firstFrame() {
if (frames == NULL || framesCount == 0)
return NULL;
Surface *r = frames;
uint16 pos = READ_LE_UINT16(data + 1);
if (pos != 0) {
r->x = pos % kScreenWidth;
r->y = pos / kScreenWidth;
}
return r;
}
Surface *Animation::currentFrame(int dt) {
if (paused)
return firstFrame();
if (frames == NULL || framesCount == 0)
return NULL;
Surface *r;
if (data != NULL) {
uint32 frame = 3 * index;
debugC(2, kDebugAnimation, "%u/%u", index, dataSize / 3);
index += dt;
if (!loop && index >= dataSize / 3) {
return NULL;
}
if (data[frame] - 1 >= framesCount) {
warning("invalid frame %u(0x%x) (max %u) index %u, mod %u", frame, frame, framesCount, index - 1, dataSize / 3);
return NULL;
}
r = frames + data[frame] - 1;
uint16 pos = READ_LE_UINT16(data + frame + 1);
index %= (dataSize / 3);
if (pos != 0) {
x = r->x = pos % kScreenWidth;
y = r->y = pos / kScreenWidth;
}
} else {
debugC(2, kDebugAnimation, "index %u", index);
r = frames + index;
index += dt;
index %= framesCount;
}
return r;
}
void Animation::restart() {
paused = false;
ignore = false;
index = 0;
}
void Animation::free() {
id = 0;
x = y = 0;
loop = true;
paused = false;
ignore = false;
delete[] data;
data = NULL;
dataSize = 0;
framesCount = 0;
delete[] frames;
frames = NULL;
index = 0;
}
void Animation::load(Common::SeekableReadStream &s, Type type) {
//FIXME: do not reload the same animation each time
free();
if (s.size() <= 1) {
debugC(1, kDebugAnimation, "empty animation");
return;
}
uint16 pos = 0;
int off = 0;
switch (type) {
case kTypeLan:
dataSize = s.readUint16LE();
if (s.eos()) {
debugC(1, kDebugAnimation, "empty animation");
return;
}
dataSize -= 2;
data = new byte[dataSize];
dataSize = s.read(data, dataSize);
for (int i = 0; i < dataSize; ++i)
debugC(2, kDebugAnimation, "%02x ", data[i]);
debugC(2, kDebugAnimation, ", %u frames", dataSize / 3);
framesCount = s.readByte();
debugC(1, kDebugAnimation, "%u physical frames", framesCount);
if (framesCount == 0)
return;
frames = new Surface[framesCount];
s.skip(framesCount * 2 - 2); //sizes
pos = s.readUint16LE();
debugC(3, kDebugAnimation, "pos?: 0x%04x", pos);
for (uint16 i = 0; i < framesCount; ++i) {
frames[i].load(s, Surface::kTypeLan);
frames[i].x = 0;
frames[i].y = 0;
}
break;
case kTypeInventory: {
dataSize = 3 * s.readByte();
data = new byte[dataSize];
framesCount = 0;
for (byte i = 0; i < dataSize / 3; ++i) {
int idx = i * 3;
byte unk = s.readByte();
debugC(3, kDebugAnimation, "unk?: 0x%02x", unk);
data[idx] = s.readByte();
if (data[idx] == 0)
data[idx] = 1; //fixme: investigate
if (data[idx] > framesCount)
framesCount = data[idx];
data[idx + 1] = 0;
data[idx + 2] = 0;
debugC(2, kDebugAnimation, "frame #%u", data[idx]);
}
frames = new Surface[framesCount];
for (uint16 i = 0; i < framesCount; ++i) {
frames[i].load(s, Surface::kTypeOns);
}
}
break;
case kTypeVaria:
framesCount = s.readByte();
debugC(1, kDebugAnimation, "loading varia resource, %u physical frames", framesCount);
uint16 offset[255];
for (byte i = 0; i < framesCount; ++i) {
offset[i] = s.readUint16LE();
debugC(0, kDebugAnimation, "%u: %04x", i, offset[i]);
}
frames = new Surface[framesCount];
for (uint16 i = 0; i < framesCount; ++i) {
debugC(0, kDebugAnimation, "%04x", offset[i]);
s.seek(offset[i] + off);
frames[i].load(s, Surface::kTypeOns);
}
break;
default:
break;
}
debugC(2, kDebugAnimation, "%u frames", dataSize / 3);
}
} // End of namespace TeenAgent

View File

@@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TEENAGENT_ANIMATION_H
#define TEENAGENT_ANIMATION_H
#include "common/stream.h"
#include "teenagent/surface.h"
namespace TeenAgent {
class Animation {
public:
uint16 id, x, y;
bool loop, paused, ignore;
enum Type {kTypeLan, kTypeVaria, kTypeInventory};
Animation();
~Animation();
void load(Common::SeekableReadStream &, Type type = kTypeLan);
void free();
Surface *firstFrame();
Surface *currentFrame(int dt);
uint16 currentIndex() const { return index; }
void resetIndex() { index = 0; }
bool empty() const { return frames == NULL; }
void restart();
//uint16 width() const { return frames? frames[0].w: 0; }
//uint16 height() const { return frames? frames[0].h: 0; }
protected:
byte *data;
uint16 dataSize;
uint16 framesCount;
Surface *frames;
uint16 index;
};
} // End of namespace TeenAgent
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine teenagent "Teen Agent" yes

View File

@@ -0,0 +1,202 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "teenagent/console.h"
#include "teenagent/resources.h"
#include "teenagent/teenagent.h"
namespace TeenAgent {
Console::Console(TeenAgentEngine *engine) : _engine(engine) {
registerCmd("enable_object", WRAP_METHOD(Console, enableObject));
registerCmd("disable_object", WRAP_METHOD(Console, enableObject));
registerCmd("set_ons", WRAP_METHOD(Console, setOns));
registerCmd("set_music", WRAP_METHOD(Console, setMusic));
registerCmd("animation", WRAP_METHOD(Console, playAnimation));
registerCmd("actor_animation", WRAP_METHOD(Console, playActorAnimation));
registerCmd("call", WRAP_METHOD(Console, call));
registerCmd("playSound", WRAP_METHOD(Console, playSound));
registerCmd("playVoice", WRAP_METHOD(Console, playVoice));
}
bool Console::enableObject(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s object_id [scene_id]\n", argv[0]);
return true;
}
int id = atoi(argv[1]);
if (id < 0) {
debugPrintf("object id %d is invalid\n", id);
return true;
}
int scene_id = 0;
if (argc > 2) {
scene_id = atoi(argv[2]);
if (scene_id < 0) {
debugPrintf("scene id %d is invalid\n", scene_id);
return true;
}
}
if (strcmp(argv[0], "disable_object") == 0)
_engine->disableObject(id, scene_id);
else
_engine->enableObject(id, scene_id);
return true;
}
bool Console::setOns(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("usage: %s index(0-3) value [scene_id]\n", argv[0]);
return true;
}
int index = atoi(argv[1]);
if (index < 0 || index > 3) {
debugPrintf("index %d is invalid\n", index);
return true;
}
int value = 0;
value = atoi(argv[2]);
if (value < 0) {
debugPrintf("invalid value\n");
return true;
}
int scene_id = 0;
if (argc > 3) {
scene_id = atoi(argv[3]);
if (scene_id < 0) {
debugPrintf("scene id %d is invalid\n", scene_id);
return true;
}
}
_engine->setOns(index, value, scene_id);
return true;
}
bool Console::setMusic(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s index(1-11)\n", argv[0]);
return true;
}
int index = atoi(argv[1]);
if (index <= 0 || index > 11) {
debugPrintf("invalid value\n");
return true;
}
_engine->setMusic(index);
return true;
}
bool Console::playAnimation(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("usage: %s id slot(0-3)\n", argv[0]);
return true;
}
int id = atoi(argv[1]);
int slot = atoi(argv[2]);
if (id < 0 || slot < 0 || slot > 3) {
debugPrintf("invalid slot or animation id\n");
return true;
}
_engine->playAnimation(id, slot);
return true;
}
bool Console::playActorAnimation(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s id\n", argv[0]);
return true;
}
int id = atoi(argv[1]);
if (id < 0) {
debugPrintf("invalid animation id\n");
return true;
}
_engine->playActorAnimation(id);
return true;
}
bool Console::call(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s 0xHEXADDR\n", argv[0]);
return true;
}
uint addr;
if (sscanf(argv[1], "0x%x", &addr) != 1) {
debugPrintf("invalid address\n");
return true;
}
if (!_engine->processCallback(addr))
debugPrintf("calling callback %04x failed\n", addr);
return true;
}
bool Console::playSound(int argc, const char **argv) {
uint32 fileCount = _engine->res->sam_sam.fileCount();
if (argc < 2) {
debugPrintf("usage: %s index(1-%d)\n", argv[0], fileCount);
return true;
}
uint index = atoi(argv[1]);
if (index <= 0 || index > fileCount) {
debugPrintf("invalid value\n");
return true;
}
_engine->playSoundNow(&_engine->res->sam_sam, index);
return true;
}
bool Console::playVoice(int argc, const char **argv) {
uint32 fileCount = _engine->res->voices.fileCount();
if (argc < 2) {
debugPrintf("usage: %s index(1-%d)\n", argv[0], fileCount);
return true;
}
uint index = atoi(argv[1]);
if (index <= 0 || index > fileCount) {
debugPrintf("invalid value\n");
return true;
}
_engine->playSoundNow(&_engine->res->voices, index);
return true;
}
}

View File

@@ -0,0 +1,50 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TEENAGENT_CONSOLE_H
#define TEENAGENT_CONSOLE_H
#include "gui/debugger.h"
namespace TeenAgent {
class TeenAgentEngine;
class Console : public GUI::Debugger {
public:
Console(TeenAgentEngine *engine);
private:
bool enableObject(int argc, const char **argv);
bool setOns(int argc, const char **argv);
bool setMusic(int argc, const char **argv);
bool playAnimation(int argc, const char **argv);
bool playActorAnimation(int argc, const char **argv);
bool call(int argc, const char **argv);
bool playSound(int argc, const char **argv);
bool playVoice(int argc, const char **argv);
TeenAgentEngine *_engine;
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,4 @@
begin_section("TeenAgent");
add_person("Robert Megone", "sanguine", "Help with callback rewriting");
add_person("Vladimir Menshakov", "whoozle", "");
end_section();

View File

@@ -0,0 +1,249 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/algorithm.h"
#include "base/plugins.h"
#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "teenagent/teenagent.h"
#include "teenagent/detection.h"
static const DebugChannelDef debugFlagList[] = {
{TeenAgent::kDebugActor, "Actor", "Enable Actor Debug"},
{TeenAgent::kDebugAnimation, "Animation", "Enable Animation Debug"},
{TeenAgent::kDebugCallbacks, "Callbacks", "Enable Callbacks Debug"},
{TeenAgent::kDebugDialog, "Dialog", "Enable Dialog Debug"},
{TeenAgent::kDebugFont, "Font", "Enable Font Debug"},
{TeenAgent::kDebugInventory, "Inventory", "Enable Inventory Debug"},
{TeenAgent::kDebugMusic, "Music", "Enable Music Debug"},
{TeenAgent::kDebugObject, "Object", "Enable Object Debug"},
{TeenAgent::kDebugPack, "Pack", "Enable Pack Debug"},
{TeenAgent::kDebugScene, "Scene", "Enable Scene Debug"},
{TeenAgent::kDebugSurface, "Surface", "Enable Surface Debug"},
DEBUG_CHANNEL_END
};
static const PlainGameDescriptor teenAgentGames[] = {
{ "teenagent", "Teen Agent" },
{ 0, 0 }
};
static const ADGameDescription teenAgentGameDescriptions[] = {
{
"teenagent",
"",
{
{"off.res", 0, "c5263a726d038bb6780a40eb3b83cc87", 2720432},
{"on.res", 0, "a0d5e5bbf6fab4bdc7f4094ed85f9639", 153907},
{"ons.res", 0, "a7e2e8def1f0fb46644c20686af0d91a", 173077},
{"varia.res", 0, "b5ba6925029c7bc285283da8c2d3042d", 209315},
{"lan_000.res", 0, "06e409b0a43ff0ced014b93fb8f5dd5b", 535599},
{"lan_500.res", 0, "c098cc17cc27a1cad4319fb6789aa5a7", 9538457},
{"mmm.res", 0, "d25033d9bc88662d680b56825e892e5c", 42104},
{"sam_mmm.res", 0, "a0878ad9a1af39d515e2e0471222f080", 229636},
{"sam_sam.res", 0, "547a48cc1be9cf30744de8b0b47838f2", 769552},
{"sdr.res", 0, "434c62c1f43b7aa4def62ff276163edb", 14672},
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
},
{
"teenagent",
"Alt version",
{
{"off.res", 0, "c5263a726d038bb6780a40eb3b83cc87", 2720432},
{"on.res", 0, "a0d5e5bbf6fab4bdc7f4094ed85f9639", 153907},
{"ons.res", 0, "a7e2e8def1f0fb46644c20686af0d91a", 173077},
{"varia.res", 0, "b786c48e160e1981b496a30acd3deff9", 216683},
{"lan_000.res", 0, "06e409b0a43ff0ced014b93fb8f5dd5b", 535599},
{"lan_500.res", 0, "c098cc17cc27a1cad4319fb6789aa5a7", 9538457},
{"mmm.res", 0, "d25033d9bc88662d680b56825e892e5c", 42104},
{"sam_mmm.res", 0, "a0878ad9a1af39d515e2e0471222f080", 229636},
{"sam_sam.res", 0, "547a48cc1be9cf30744de8b0b47838f2", 769552},
{"sdr.res", 0, "434c62c1f43b7aa4def62ff276163edb", 14665},
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
},
{ // Russian fan translation
"teenagent",
"",
{
{"off.res", 0, "c5263a726d038bb6780a40eb3b83cc87", 2720432},
{"on.res", 0, "a0d5e5bbf6fab4bdc7f4094ed85f9639", 153907},
{"ons.res", 0, "a7e2e8def1f0fb46644c20686af0d91a", 173077},
{"varia.res", 0, "b786c48e160e1981b496a30acd3deff9", 216683},
{"lan_000.res", 0, "06e409b0a43ff0ced014b93fb8f5dd5b", 535599},
{"lan_500.res", 0, "c098cc17cc27a1cad4319fb6789aa5a7", 9538457},
{"mmm.res", 0, "d25033d9bc88662d680b56825e892e5c", 42104},
{"sam_mmm.res", 0, "a0878ad9a1af39d515e2e0471222f080", 229636},
{"sam_sam.res", 0, "547a48cc1be9cf30744de8b0b47838f2", 769552},
{"cdlogo.res", 0, "d1aacbb7deb718f9d946ba9deec6271d", 64768},
{"sdr.res", 0, "434c62c1f43b7aa4def62ff276163edb", 14665},
AD_LISTEND
},
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
},
{ // Czech Floppy
"teenagent",
"",
{
{"off.res", 0, "c5263a726d038bb6780a40eb3b83cc87", 2720432},
{"on.res", 0, "a0d5e5bbf6fab4bdc7f4094ed85f9639", 153907},
{"ons.res", 0, "a7e2e8def1f0fb46644c20686af0d91a", 173077},
{"varia.res", 0, "b786c48e160e1981b496a30acd3deff9", 216683},
{"lan_000.res", 0, "06e409b0a43ff0ced014b93fb8f5dd5b", 535599},
{"lan_500.res", 0, "c098cc17cc27a1cad4319fb6789aa5a7", 9538457},
{"mmm.res", 0, "d25033d9bc88662d680b56825e892e5c", 42104},
{"sam_mmm.res", 0, "a0878ad9a1af39d515e2e0471222f080", 229636},
{"sam_sam.res", 0, "547a48cc1be9cf30744de8b0b47838f2", 769552},
{"sdr.res", 0, "434c62c1f43b7aa4def62ff276163edb", 14672},
AD_LISTEND
},
Common::CS_CZE,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
},
{ // Polish CD
"teenagent",
"CD",
{
{"off.res", 0, "aaac839a6ef639d68ebc97bc42faa42d", 2720432},
{"on.res", 0, "a0d5e5bbf6fab4bdc7f4094ed85f9639", 153907},
{"ons.res", 0, "a7e2e8def1f0fb46644c20686af0d91a", 173077},
{"varia.res", 0, "b786c48e160e1981b496a30acd3deff9", 216683},
{"lan_000.res", 0, "06e409b0a43ff0ced014b93fb8f5dd5b", 535599},
{"lan_500.res", 0, "c098cc17cc27a1cad4319fb6789aa5a7", 9538457},
{"sam_sam.res", 0, "547a48cc1be9cf30744de8b0b47838f2", 769552},
{"voices.res", 0, "955aa04517a2b0499adf17d9b7c6f4a1", 37306128},
{"cdlogo.res", 0, "6bf95a48f366bdf8af3a198c7b723c77", 64768},
{"sdr.res", 0, "d0b1398c78dc82571ddef5877c9a3a06", 14993},
AD_LISTEND
},
Common::PL_POL,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS)
},
{ // Polish Floppy
"teenagent",
"",
{
{"off.res", 0, "aaac839a6ef639d68ebc97bc42faa42d", 2720432},
{"on.res", 0, "a0d5e5bbf6fab4bdc7f4094ed85f9639", 153907},
{"ons.res", 0, "a7e2e8def1f0fb46644c20686af0d91a", 173077},
{"varia.res", 0, "b786c48e160e1981b496a30acd3deff9", 216683},
{"lan_000.res", 0, "06e409b0a43ff0ced014b93fb8f5dd5b", 535599},
{"lan_500.res", 0, "c098cc17cc27a1cad4319fb6789aa5a7", 9538457},
{"sam_sam.res", 0, "547a48cc1be9cf30744de8b0b47838f2", 769552},
{"voices.res", 0, "955aa04517a2b0499adf17d9b7c6f4a1", 19376128},
{"cdlogo.res", 0, "6bf95a48f366bdf8af3a198c7b723c77", 64768},
{"sdr.res", 0, "d0b1398c78dc82571ddef5877c9a3a06", 14993},
AD_LISTEND
},
Common::PL_POL,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS)
},
{ // Demo
"teenagent",
"Demo",
{
{"off.res", 0, "441b7dde82ca84a829fc7fe9743e9b78", 906956},
{"on.res", 0, "25dbb6eed0a80d98edff3c24f09f1ee0", 37654},
{"ons.res", 0, "394fbd9418e43942127c45a326d10ee1", 50596},
{"varia.res", 0, "b786c48e160e1981b496a30acd3deff9", 216683},
{"lan_000.res", 0, "c7241846ec67dd249fe02610cb9b8425", 91729},
{"lan_500.res", 0, "791e4058a4742abd7c03dc82272623a9", 2109796},
{"mmm.res", 0, "afbab0a454860f4ccf23005d8d2f4a70", 27073},
{"sam_mmm.res", 0, "85fcdd0d49062577acf4a9ddafc53283", 148783},
{"sam_sam.res", 0, "dc39c65ec57ed70612670b7e780f3408", 342219},
{"sdr.res", 0, "434c62c1f43b7aa4def62ff276163edb", 14672},
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
},
{ // Demo alt
"teenagent",
"Alt Demo",
{
{"off.res", 0, "441b7dde82ca84a829fc7fe9743e9b78", 906956},
{"on.res", 0, "25dbb6eed0a80d98edff3c24f09f1ee0", 37654},
{"ons.res", 0, "394fbd9418e43942127c45a326d10ee1", 50596},
{"varia.res", 0, "8ffe0a75b7299b44a34fdd3831cecacb", 217003},
{"lan_000.res", 0, "c7241846ec67dd249fe02610cb9b8425", 91729},
{"lan_500.res", 0, "791e4058a4742abd7c03dc82272623a9", 2109796},
{"mmm.res", 0, "afbab0a454860f4ccf23005d8d2f4a70", 27073},
{"sam_mmm.res", 0, "85fcdd0d49062577acf4a9ddafc53283", 148783},
{"sam_sam.res", 0, "dc39c65ec57ed70612670b7e780f3408", 342219},
{"sdr.res", 0, "434c62c1f43b7aa4def62ff276163edb", 14672},
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS_OBJECTS, GAMEOPTION_TTS_SPEECH)
},
AD_TABLE_END_MARKER,
};
class TeenAgentMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
TeenAgentMetaEngineDetection() : AdvancedMetaEngineDetection(teenAgentGameDescriptions, teenAgentGames) {
}
const char *getName() const override {
return "teenagent";
}
const char *getEngineName() const override {
return "TeenAgent";
}
const char *getOriginalCopyright() const override {
return "TEENAGENT (C) 1994 Metropolis";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(TEENAGENT_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, TeenAgentMetaEngineDetection);

View File

@@ -0,0 +1,28 @@
/* 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 TEENAGENT_DETECTION_H
#define TEENAGENT_DETECTION_H
#define GAMEOPTION_TTS_OBJECTS GUIO_GAMEOPTIONS1
#define GAMEOPTION_TTS_SPEECH GUIO_GAMEOPTIONS2
#endif // TEENAGENT_DETECTION_H

View File

@@ -0,0 +1,171 @@
/* 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 "teenagent/dialog.h"
#include "teenagent/resources.h"
#include "teenagent/scene.h"
#include "teenagent/teenagent.h"
namespace TeenAgent {
void Dialog::show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
uint32 addr = _vm->res->getDialogAddr(dialogNum);
// WORKAROUND: For Dialog 163, The usage of this in the engine overlaps the previous dialog i.e. the
// starting offset used is two bytes early, thus implicitly changing the first command of this dialog
// from NEW_LINE to CHANGE_CHARACTER.
// FIXME: Unsure if this is correct behaviour or if this is a regression from the original. Check this.
// Similar issue occurs with Dialog 190 which is used from dialogue stack at 0x7403, rather than start of 0x7405
// Similar issue occurs with Dialog 0 which is used from dialogue stack at 0x0001, rather than start of 0x0000
if (dialogNum == 163)
addr -= 2;
show(scene, addr, animation1, animation2, character1ID, character2ID, slot1, slot2);
}
void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
debugC(0, kDebugDialog, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2);
int n = 0;
uint16 voiceId = 0;
Common::String message;
byte color = characterDialogData[character1ID].textColor;
byte color1 = color;
byte color2 = characterDialogData[character2ID].textColor;
if (animation1 != 0) {
SceneEvent e1(SceneEvent::kPlayAnimation);
e1.animation = animation1;
e1.slot = 0xc0 | slot1; //looped, paused
scene->push(e1);
}
if (animation2 != 0) {
SceneEvent e2(SceneEvent::kPlayAnimation);
e2.animation = animation2;
e2.slot = 0xc0 | slot2; //looped, paused
scene->push(e2);
}
// Number of ANIM_WAIT (0xff) bytes.
// Used to correctly find voice index.
uint numOfAnimWaits = 0;
while (n < 4) {
byte c = _vm->res->eseg.get_byte(addr++);
debugC(1, kDebugDialog, "%02x: %c", c, c > 0x20? c: '.');
switch (c) {
case 0:
++n;
switch (n) {
case 1:
debugC(1, kDebugDialog, "new line\n");
if (!message.empty())
message += '\n';
break;
case 2:
debugC(1, kDebugDialog, "displaymessage %s", message.c_str());
if (color == color2) {
//pause animation in other slot
SceneEvent e1(SceneEvent::kPauseAnimation);
e1.slot = 0x80 | slot1;
scene->push(e1);
SceneEvent e2(SceneEvent::kPlayAnimation);
e2.animation = animation2;
e2.slot = 0x80 | slot2;
scene->push(e2);
} else if (color == color1) {
//pause animation in other slot
SceneEvent e2(SceneEvent::kPauseAnimation);
e2.slot = 0x80 | slot2;
scene->push(e2);
SceneEvent e1(SceneEvent::kPlayAnimation);
e1.animation = animation1;
e1.slot = 0x80 | slot1;
scene->push(e1);
}
message.trim();
voiceId = _vm->res->getVoiceIndex(addr - message.size() - numOfAnimWaits - 2); // -2 for '\n'
if (!message.empty()) {
SceneEvent em(SceneEvent::kMessage);
em.message = message;
em.color = color;
if (color == color1) {
em.slot = slot1;
em.characterID = character1ID;
}
if (color == color2) {
em.slot = slot2;
em.characterID = character2ID;
}
em.voiceId = voiceId;
scene->push(em);
message.clear();
numOfAnimWaits = 0;
}
break;
case 3:
color = (color == color1) ? color2 : color1;
debugC(1, kDebugDialog, "changing color to %02x", color);
break;
default:
break;
}
break;
case 0xff:
numOfAnimWaits++;
//FIXME : wait for the next cycle of the animation
break;
default:
message += c;
n = 0;
break;
}
}
SceneEvent ec(SceneEvent::kClearAnimations);
scene->push(ec);
}
uint16 Dialog::pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
debugC(0, kDebugDialog, "Dialog::pop(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2);
uint16 next;
do {
next = _vm->res->dseg.get_word(addr);
addr += 2;
} while (next == 0);
uint16 next2 = _vm->res->dseg.get_word(addr);
if (next2 != 0xffff)
_vm->res->dseg.set_word(addr - 2, 0);
// Dialog addresses popped from stack are relative
// to dialog start offset. So we add that offset first
uint32 dialogAddr = _vm->res->getDialogStartPos() + next;
show(scene, dialogAddr, animation1, animation2, character1ID, character2ID, slot1, slot2);
return next;
}
} // End of namespace TeenAgent

143
engines/teenagent/dialog.h Normal file
View File

@@ -0,0 +1,143 @@
/* 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 TEENAGENT_DIALOG_H
#define TEENAGENT_DIALOG_H
#include "common/endian.h"
#include "common/str.h"
namespace TeenAgent {
// Text Color Symbols
enum {
textColorJohnNoty = 0xd0,
textColorCampGuard = 0xd0,
textColorShockedCaptain = 0xd0,
textColorMark = 0xd1,
textColorCredits = 0xd1,
textColorBankGuard = 0xd7,
textColorGrandpa = 0xd8,
textColorMansionGuard = 0xd9,
textColorMarkEnd = 0xe3,
textColorProfessor = 0xe5,
textColorOldLady = 0xe5,
textColorAnne = 0xe5,
textColorWellEcho = 0xe5,
textColorSonny = 0xe5,
textColorEskimo = 0xe5,
textColorRGBBoss = 0xe7,
textColorGoldDriver = 0xe7,
textColorFortuneTeller = 0xeb,
textColorCaptain = 0xec,
textColorMike = 0xef,
textColorCook = 0xef,
textColorBarman = 0xef
};
enum CharacterID {
kMark = 0,
kGoldDriver = 1,
kBankGuard = 2,
kRGBBoss = 3,
kFortuneTeller = 4,
kCampGuard = 5,
kCaptain = 6,
kShockedCaptain = 7,
kBarman = 8,
kSonny = 9,
kGrandpa = 10,
kAnne = 11,
kWellEcho = 12,
kOldLady = 13,
kMansionGuard = 14,
kJohnNoty = 15,
kProfessor = 16,
kCook = 17,
kEskimo = 18,
kMike = 19,
kMarkEnd = 20,
kCreditsText = 21
};
struct CharacterDialogData {
int voiceID;
bool male;
byte textColor;
};
static const CharacterDialogData characterDialogData[] = {
{ 0, true, textColorMark },
{ 1, true, textColorGoldDriver },
{ 2, true, textColorBankGuard },
{ 3, true, textColorRGBBoss },
{ 0, false, textColorFortuneTeller },
{ 4, true, textColorCampGuard },
{ 5, true, textColorCaptain },
{ 5, true, textColorShockedCaptain }, // Same voice as captain
{ 6, true, textColorBarman },
{ 7, true, textColorSonny, },
{ 8, true, textColorGrandpa },
{ 1, false, textColorAnne },
{ 9, true, textColorWellEcho },
{ 2, false, textColorOldLady },
{ 10, true, textColorMansionGuard },
{ 11, true, textColorJohnNoty },
{ 12, true, textColorProfessor },
{ 13, true, textColorCook },
{ 14, true, textColorEskimo },
{ 15, true, textColorMike },
{ 0, true, textColorMarkEnd }, // Same voice as Mark
{ 0, true, textColorCredits } // Same voice as Mark
};
class Scene;
class TeenAgentEngine;
class Dialog {
public:
Dialog(TeenAgentEngine *vm) : _vm(vm) { }
uint16 pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2);
uint16 popMark(Scene *scene, uint16 addr) {
return pop(scene, addr, 0, 0, kMark, kMark, 0, 0);
}
void show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2);
void showMono(uint16 dialogNum, Scene *scene, uint16 animation, CharacterID characterID, byte slot) {
show(dialogNum, scene, animation, animation, characterID, characterID, slot, slot);
}
void showMark(uint16 dialogNum, Scene *scene) {
show(dialogNum, scene, 0, 0, kMark, kMark, 0, 0);
}
private:
TeenAgentEngine *_vm;
void show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2);
};
} // End of namespace TeenAgent
#endif

169
engines/teenagent/font.cpp Normal file
View File

@@ -0,0 +1,169 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "teenagent/font.h"
#include "teenagent/pack.h"
#include "teenagent/teenagent.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/ptr.h"
#include "graphics/surface.h"
namespace TeenAgent {
Font::Font() : _gridColor(0xd0), _shadowColor(0), _height(0), _widthPack(0), _data(0) {
}
Font::~Font() {
delete[] _data;
}
void Font::load(const Pack &pack, int id, byte height, byte widthPack) {
delete[] _data;
_data = NULL;
Common::ScopedPtr<Common::SeekableReadStream> s(pack.getStream(id));
if (!s)
error("loading font %d failed", id);
_data = new byte[s->size()];
s->read(_data, s->size());
debugC(0, kDebugFont, "font size: %d", (int)s->size());
_height = height;
_widthPack = widthPack;
}
uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) {
unsigned idx = (unsigned char)c;
if (idx < 0x20 || idx >= 0x81) {
debugC(0, kDebugFont, "unhandled char 0x%02x", idx);
return 0;
}
idx -= 0x20;
byte *glyph = _data + READ_LE_UINT16(_data + idx * 2);
int h = glyph[0], w = glyph[1];
if (surface == NULL || surface->getPixels() == NULL || y + h <= 0 || y >= kScreenHeight || x + w <= 0 || x >= kScreenWidth)
return w - _widthPack;
int i0 = 0, j0 = 0;
if (x < 0) {
j0 = -x;
x = 0;
}
if (y < 0) {
i0 = -y;
y = 0;
}
debugC(0, kDebugFont, "char %c, width: %dx%d", c, w, h);
glyph += 2;
glyph += i0 * w + j0;
byte *dst = (byte *)surface->getBasePtr(x, y);
byte *end = (byte *)surface->getBasePtr(0, surface->h);
for (int i = i0; i < h && dst < end; ++i) {
for (int j = j0; j < w; ++j) {
byte v = *glyph++;
switch (v) {
case 0:
break;
case 1:
dst[j] = _shadowColor;
break;
case 2:
dst[j] = color;
break;
default:
dst[j] = v;
}
}
dst += surface->pitch;
}
return w - _widthPack;
}
static uint findInStr(const Common::String &str, char c, uint pos = 0) {
while (pos < str.size() && str[pos] != c) ++pos;
return pos;
}
uint Font::render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool showGrid) {
if (surface != NULL) {
uint maxW = render(NULL, 0, 0, str, false);
if (showGrid)
grid(surface, x - 4, y - 2, maxW + 8, 8 + 6, _gridColor);
uint i = 0, j;
do {
j = findInStr(str, '\n', i);
Common::String line(str.c_str() + i, j - i);
debugC(0, kDebugFont, "line: %s", line.c_str());
if (y + (int)_height >= 0) {
uint w = render(NULL, 0, 0, line, false);
int xp = x + (maxW - w) / 2;
for (uint k = 0; k < line.size(); ++k) {
xp += render(surface, xp, y, line[k], color);
}
} else if (y >= kScreenHeight)
break;
y += _height;
i = j + 1;
} while (i < str.size());
return maxW;
} else {
// surface == NULL;
uint w = 0, maxW = 0;
for (uint i = 0; i < str.size(); ++i) {
char c = str[i];
if (c == '\n') {
y += _height;
if (w > maxW)
maxW = w;
w = 0;
continue;
}
w += render(NULL, 0, 0, c, color);
}
if (w > maxW)
maxW = w;
return maxW;
}
}
void Font::grid(Graphics::Surface *surface, int x, int y, int w, int h, byte color) {
byte *dst = (byte *)surface->getBasePtr(x, y);
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
if (((i ^ j) & 1) == 0)
dst[j] = color;
}
dst += surface->pitch;
}
}
} // End of namespace TeenAgent

53
engines/teenagent/font.h Normal file
View File

@@ -0,0 +1,53 @@
/* 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 TEENAGENT_FONT_H
#define TEENAGENT_FONT_H
#include "common/str.h"
#include "graphics/surface.h"
namespace TeenAgent {
class Pack;
class Font {
public:
Font();
~Font();
void load(const Pack &pack, int id, byte height, byte widthPack);
uint render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool showGrid = false);
uint render(Graphics::Surface *surface, int x, int y, char c, byte color);
static void grid(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
byte getHeight() { return _height; }
void setShadowColor(byte color) { _shadowColor = color; }
private:
byte *_data;
byte _gridColor, _shadowColor;
byte _height, _widthPack;
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,372 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/memstream.h"
#include "common/ptr.h"
#include "common/textconsole.h"
#include "teenagent/inventory.h"
#include "teenagent/resources.h"
#include "teenagent/objects.h"
#include "teenagent/teenagent.h"
#include "teenagent/scene.h"
namespace TeenAgent {
Inventory::Inventory(TeenAgentEngine *vm) : _vm(vm) {
_active = false;
FilePack varia;
varia.open("varia.res");
Common::ScopedPtr<Common::SeekableReadStream> s(varia.getStream(3));
if (!s)
error("no inventory background");
debugC(0, kDebugInventory, "loading inventory background...");
_background.load(*s, Surface::kTypeOns);
uint32 itemsSize = varia.getSize(4);
if (itemsSize == 0)
error("invalid inventory items size");
debugC(0, kDebugInventory, "loading items, size: %u", itemsSize);
_items = new byte[itemsSize];
varia.read(4, _items, itemsSize);
byte offsets = _items[0];
assert(offsets == kNumInventoryItems);
for (byte i = 0; i < offsets; ++i) {
_offset[i] = READ_LE_UINT16(_items + i * 2 + 1);
}
_offset[kNumInventoryItems] = itemsSize;
InventoryObject ioBlank;
_objects.push_back(ioBlank);
for (byte i = 0; i < kNumInventoryItems; ++i) {
InventoryObject io;
uint32 objAddr = vm->res->getItemAddr(i);
io.load(vm->res->eseg.ptr(objAddr));
_objects.push_back(io);
}
_inventory = vm->res->dseg.ptr(dsAddr_inventory);
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 6; ++x) {
int i = y * 6 + x;
_graphics[i]._rect.left = 28 + 45 * x - 1;
_graphics[i]._rect.top = 23 + 31 * y - 1;
_graphics[i]._rect.right = _graphics[i]._rect.left + 40;
_graphics[i]._rect.bottom = _graphics[i]._rect.top + 26;
}
}
varia.close();
_hoveredObj = _selectedObj = NULL;
}
Inventory::~Inventory() {
delete[] _items;
}
bool Inventory::has(byte item) const {
for (int i = 0; i < kInventorySize; ++i) {
if (_inventory[i] == item)
return true;
}
return false;
}
void Inventory::remove(byte item) {
debugC(0, kDebugInventory, "removing %u from inventory", item);
int i;
for (i = 0; i < kInventorySize; ++i) {
if (_inventory[i] == item) {
break;
}
}
for (; i < (kInventorySize - 1); ++i) {
_inventory[i] = _inventory[i + 1];
_graphics[i].free();
}
_inventory[kInventorySize - 1] = kInvItemNoItem;
_graphics[kInventorySize - 1].free();
}
void Inventory::clear() {
debugC(0, kDebugInventory, "clearing inventory");
for (int i = 0; i < kInventorySize; ++i) {
_inventory[i] = kInvItemNoItem;
_graphics[i].free();
}
}
void Inventory::reload() {
for (int i = 0; i < kInventorySize; ++i) {
_graphics[i].free();
uint item = _inventory[i];
if (item != kInvItemNoItem)
_graphics[i].load(this, item);
}
}
void Inventory::add(byte item) {
if (has(item))
return;
debugC(0, kDebugInventory, "adding %u to inventory", item);
for (int i = 0; i < kInventorySize; ++i) {
if (_inventory[i] == kInvItemNoItem) {
_inventory[i] = item;
return;
}
}
error("no room for item %u", item);
}
bool Inventory::tryObjectCallback(InventoryObject *obj) {
byte objId = obj->id;
for (uint i = 0; i < 7; ++i) {
byte tableId = _vm->res->dseg.get_byte(dsAddr_objCallbackTablePtr + (3 * i));
uint16 callbackAddr = _vm->res->dseg.get_word(dsAddr_objCallbackTablePtr + (3 * i) + 1);
if (tableId == objId) {
resetSelectedObject();
activate(false);
if (_vm->processCallback(callbackAddr))
return true;
}
}
return false;
}
bool Inventory::processEvent(const Common::Event &event) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
if (!_active) {
if (event.mouse.y < 5)
activate(true);
_mouse = event.mouse;
return false;
}
if (event.mouse.x < 17 || event.mouse.x >= 303 || (event.mouse.y - _mouse.y > 0 && event.mouse.y >= 153)) {
activate(false);
_mouse = event.mouse;
return false;
}
_mouse = event.mouse;
_hoveredObj = NULL;
for (int i = 0; i < kInventorySize; ++i) {
byte item = _inventory[i];
if (item == kInvItemNoItem)
continue;
_graphics[i]._hovered = _graphics[i]._rect.in(_mouse);
if (_graphics[i]._hovered)
_hoveredObj = &_objects[item];
}
return true;
case Common::EVENT_LBUTTONDOWN: {
//check combine
if (!_active)
return false;
if (_hoveredObj == NULL)
return true;
debugC(0, kDebugInventory, "lclick on %u:%s", _hoveredObj->id, _hoveredObj->name.c_str());
if (_selectedObj == NULL) {
if (tryObjectCallback(_hoveredObj))
return true;
//activate(false);
int w = _vm->res->font7.render(NULL, 0, 0, _hoveredObj->description, textColorMark);
uint16 voiceIndex = _vm->res->getVoiceIndex(_vm->res->getItemAddr(_hoveredObj->id - 1));
_vm->scene->displayMessage(_hoveredObj->description, voiceIndex, textColorMark, Common::Point((kScreenWidth - w) / 2, 162));
return true;
}
int id1 = _selectedObj->id;
int id2 = _hoveredObj->id;
if (id1 == id2)
return true;
debugC(0, kDebugInventory, "combine(%u, %u)!", id1, id2);
for (uint i = 0; i < kNumCombinations; i++) {
uint32 addr = _vm->res->getCombinationAddr(i);
byte *table = _vm->res->eseg.ptr(addr);
if ((id1 == table[0] && id2 == table[1]) || (id2 == table[0] && id1 == table[1])) {
byte newObj = table[2];
if (newObj != 0) {
remove(id1);
remove(id2);
debugC(0, kDebugInventory, "adding object %u", newObj);
add(newObj);
_vm->playSoundNow(&_vm->res->sam_sam, 69);
}
Common::String msg = Object::parseDescription((const char *)(table + 3));
_vm->displayMessage(msg, _vm->res->getVoiceIndex(addr));
activate(false);
resetSelectedObject();
return true;
}
}
_vm->displayMessage(_vm->res->getMessageAddr(kObjCombineErrorMsg));
activate(false);
resetSelectedObject();
return true;
}
case Common::EVENT_RBUTTONDOWN:
if (!_active)
return false;
if (_hoveredObj != NULL) {
debugC(0, kDebugInventory, "rclick object %u:%s", _hoveredObj->id, _hoveredObj->name.c_str());
// do not process callback for banknote on r-click
if (_hoveredObj->id != kInvItemBanknote && tryObjectCallback(_hoveredObj))
return true;
}
_selectedObj = _hoveredObj;
if (_selectedObj)
debugC(0, kDebugInventory, "selected object %s", _selectedObj->name.c_str());
return true;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (_active && event.customType == kActionCloseInventory) {
activate(false);
return true;
}
if (event.customType == kActionToggleInventory) {
activate(!_active);
return true;
}
if (event.customType == kActionSkipDialog)
return true;
return false;
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONUP:
return _active;
default:
return false;
}
}
void Inventory::Item::free() {
_animation.free();
_surface.free();
}
void Inventory::Item::backgroundEffect(Graphics::Surface *s) {
uint w = _rect.right - _rect.left, h = _rect.bottom - _rect.top;
byte *line = (byte *)s->getBasePtr(_rect.left, _rect.top);
for (uint y = 0; y < h; ++y, line += s->pitch) {
byte *dst = line;
for (uint x = 0; x < w; ++x, ++dst) {
*dst = (*dst == 232) ? 214 : 224;
}
}
}
void Inventory::Item::load(Inventory *inventory, uint itemId) {
InventoryObject *obj = &inventory->_objects[itemId];
if (obj->animated) {
if (_animation.empty()) {
debugC(0, kDebugInventory, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]);
Common::MemoryReadStream s(inventory->_items + inventory->_offset[obj->id - 1], inventory->_offset[obj->id] - inventory->_offset[obj->id - 1]);
_animation.load(s, Animation::kTypeInventory);
}
} else {
if (_surface.empty()) {
debugC(0, kDebugInventory, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]);
Common::MemoryReadStream s(inventory->_items + inventory->_offset[obj->id - 1], inventory->_offset[obj->id] - inventory->_offset[obj->id - 1]);
_surface.load(s, Surface::kTypeOns);
}
}
}
void Inventory::Item::render(Inventory *inventory, uint itemId, Graphics::Surface *dst, int delta) {
InventoryObject *obj = &inventory->_objects[itemId];
backgroundEffect(dst);
_rect.render(dst, _hovered ? 233 : 234);
load(inventory, itemId);
if (obj->animated) {
if (_hovered) {
Surface *s = _animation.currentFrame(delta);
if (_animation.currentIndex() == 0)
s = _animation.currentFrame(1); //force index to be 1 here
if (s != NULL)
s->render(dst, _rect.left + 1, _rect.top + 1);
} else {
Surface *s = _animation.firstFrame();
if (s != NULL)
s->render(dst, _rect.left + 1, _rect.top + 1);
}
} else {
_surface.render(dst, _rect.left + 1, _rect.top + 1);
}
Common::String name;
if (inventory->_selectedObj) {
name = inventory->_selectedObj->name;
name += " & ";
}
if (inventory->_selectedObj != inventory->_hoveredObj)
name += obj->name;
if (_hovered && inventory->_vm->scene->getMessage().empty()) {
int w = inventory->_vm->res->font7.render(NULL, 0, 0, name, textColorMark, true);
inventory->_vm->res->font7.render(dst, (kScreenWidth - w) / 2, 180, name, textColorMark, true);
inventory->_vm->sayText(name);
} else if (!inventory->_hoveredObj && inventory->_vm->scene->getMessage().empty()) {
inventory->_vm->_previousSaid.clear();
}
}
void Inventory::render(Graphics::Surface *surface, int delta) {
if (!_active)
return;
debugC(0, kDebugInventory, "Inventory::render()");
_background.render(surface);
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 6; x++) {
int idx = x + 6 * y;
byte item = _inventory[idx];
if (item != 0) {
debugC(0, kDebugInventory, "\t(x, y): %d,%d -> item: %u", x, y, item);
_graphics[idx].render(this, item, surface, delta);
}
}
}
}
} // End of namespace TeenAgent

View 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/>.
*
*/
#ifndef TEENAGENT_INVENTORY_H
#define TEENAGENT_INVENTORY_H
#include "teenagent/surface.h"
#include "teenagent/animation.h"
#include "common/events.h"
#include "common/array.h"
#include "teenagent/objects.h"
namespace TeenAgent {
struct InventoryObject;
class TeenAgentEngine;
// Maximum number of items found within game
const uint8 kNumInventoryItems = 92;
const uint kNumCombinations = 34;
// Inventory Item Ids
enum {
kInvItemNoItem = 0, // No item i.e. empty inventory slot
kInvItemFeather = 1,
kInvItemShotgun = 2,
kInvItemToolboxFull = 3, // Contains Car Jack and Spanner
kInvItemToolboxHalfEmpty = 4, // Contains Spanner
kInvItemSpanner = 5,
kInvItemComb = 6,
kInvItemFan = 7,
kInvItemBrokenPaddle = 8,
kInvItemPaddle = 9, // Repaired - BrokenPaddle combined with Branch (with Glue)
kInvItemFirstFlower = 10, // Smells nice
kInvItemSecondFlower = 11, // Really beautiful
kInvItemFeatherDusterClean = 12,
kInvItemChainsaw = 13, // Unfueled
kInvItemDrunkenChainsaw = 14, // Fueled with Whisky (Chainsaw combined with Whiskey)
kInvItemBranch = 15,
kInvItemWhisky = 16,
kInvItemNeedle = 17,
kInvItemWrapper = 18,
kInvItemChocCandy = 19,
kInvItemPotato = 20,
kInvItemRakeBroken = 21,
kInvItemHeartShapedCandy = 22,
kInvItemWrappedCandy = 23, // HeartShapedCandy combined with Wrapper
kInvItemRibbon = 24,
kInvItemRakeFixed = 25, // Rake combined with Ribbon
kInvItemNut = 26,
kInvItemPlasticApple = 27,
kInvItemCone = 28,
kInvItemSuperGlue = 29,
kInvItemConeAndNeedle = 30, // Cone combined with Needle
kInvItemConeAndFeather = 31, // Cone combined with Feather
kInvItemDart = 32, // Needle combined with ConeAndFeather or Feather combined with ConeAndNeedle
kInvItemFeatherDusterDirty = 33,
kInvItemPaintedPotato = 34, // Potato combined with Dirty Feather Duster (Soot)
kInvItemCarJack = 35,
kInvItemBone = 36,
kInvItemShovelAct2 = 37,
kInvItemRopeAct2 = 38,
kInvItemMask = 39,
kInvItemFins = 40,
kInvItemDiveEquipment = 41, // Mask combined with Fins
kInvItemAnchor = 42,
kInvItemGrapplingHook = 43,
kInvItemSickleBlunt = 44,
kInvItemCheese = 45,
kInvItemSickleSharp = 46,
kInvItemHandkerchief = 47,
kInvItemMouse = 48,
kInvItemRock = 49,
kInvItemNugget = 50,
kInvItemBanknote = 51,
kInvItemDictaphoneNoBatteries = 52,
kInvItemPolaroidCamera = 53,
kInvItemVideoTape = 54,
kInvItemSheetOfPaper = 55,
kInvItemCognac = 56,
kInvItemRemoteControl = 57,
kInvItemIceTongs = 58,
kInvItemCork = 59,
kInvItemWrappedCork = 60, // Cork combined with Sheet Of Paper
kInvItemPhoto = 61,
kInvItemChilliWithLabel = 62,
kInvItemPastryRoller = 63,
kInvItemFakeChilli = 64,
kInvItemLabel = 65,
kInvItemBatteries = 66,
kInvItemDictaphoneWithBatteries = 67, // Dictaphone combined with Batteries
kInvItemBurningPaper = 68,
kInvItemMeat = 69,
kInvItemPlasticBag = 70,
kInvItemSocks = 71,
kInvItemTimePills = 72,
kInvItemHandle = 73,
kInvItemChilliNoLabel = 74,
kInvItemPass = 75,
kInvItemBulb = 76,
kInvItemJailKey = 77,
kInvItemDelicatePlant = 78,
kInvItemSwissArmyKnife = 79,
kInvItemSpring = 80,
kInvItemShovelAct1 = 81,
kInvItemKaleidoscope = 82,
kInvItemSoldierNews = 83,
kInvItemGrenade = 84,
kInvItemMug = 85, // Empty
kInvItemMugOfMud = 86, // Full of mud
kInvItemCrumbs = 87,
kInvItemRopeAct1 = 88,
kInvItemRopeAndGrenade = 89, // Rope combined with Grenade
kInvItemMedicine = 90,
kInvItemDruggedFood = 91, // Crumbs combined with Medicine
kInvItemBird = 92
};
// Maximum number of inventory items held by Ego (Mark)
const uint8 kInventorySize = 24;
class Inventory {
public:
Inventory(TeenAgentEngine *vm);
~Inventory();
void render(Graphics::Surface *surface, int delta);
void clear();
void reload();
void add(byte item);
bool has(byte item) const;
void remove(byte item);
void activate(bool a) { _active = a; }
bool active() const { return _active; }
bool processEvent(const Common::Event &event);
InventoryObject *selectedObject() { return _selectedObj; }
void resetSelectedObject() { _selectedObj = NULL; }
private:
TeenAgentEngine *_vm;
Surface _background;
byte *_items;
uint _offset[kNumInventoryItems+1];
Common::Array<InventoryObject> _objects;
byte *_inventory;
struct Item {
Animation _animation;
Surface _surface;
Rect _rect;
bool _hovered;
Item() : _hovered(false) {}
void free();
void load(Inventory *inventory, uint itemId);
void backgroundEffect(Graphics::Surface *s);
void render(Inventory *inventory, uint itemId, Graphics::Surface *surface, int delta);
};
Item _graphics[kInventorySize];
bool _active;
Common::Point _mouse;
bool tryObjectCallback(InventoryObject *obj);
InventoryObject *_hoveredObj;
InventoryObject *_selectedObj;
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,252 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "common/savefile.h"
#include "common/algorithm.h"
#include "common/translation.h"
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "teenagent/resources.h"
#include "teenagent/teenagent.h"
#include "teenagent/detection.h"
#include "graphics/thumbnail.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
enum {
MAX_SAVES = 20
};
#ifdef USE_TTS
static const ADExtraGuiOptionsMap optionsList[] = {
{
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
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
#endif
class TeenAgentMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "teenagent";
}
bool hasFeature(MetaEngineFeature f) const override {
switch (f) {
case kSupportsListSaves:
case kSupportsDeleteSave:
case kSupportsLoadingDuringStartup:
case kSavesSupportMetaInfo:
case kSavesSupportThumbnail:
return true;
default:
return false;
}
}
#ifdef USE_TTS
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
#endif
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
*engine = new TeenAgent::TeenAgentEngine(syst, desc);
return Common::kNoError;
}
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
if (!target)
target = getName();
if (saveGameIdx == kSavegameFilePattern)
return Common::String::format("%s.##", target);
else
return Common::String::format("%s.%02d", target, saveGameIdx);
}
SaveStateList listSaves(const char *target) const override {
Common::String pattern = target;
pattern += ".##";
Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
int slot = atoi(file->c_str() + file->size() - 2);
if (slot >= 0 && slot < MAX_SAVES) {
Common::ScopedPtr<Common::InSaveFile> in(g_system->getSavefileManager()->openForLoading(*file));
if (!in)
continue;
char buf[25];
in->seek(0);
in->read(buf, 24);
buf[24] = 0;
saveList.push_back(SaveStateDescriptor(this, slot, buf));
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int getMaximumSaveSlot() const override {
return MAX_SAVES - 1;
}
bool removeSaveState(const char *target, int slot) const override {
return g_system->getSavefileManager()->removeSavefile(getSavegameFile(slot, target));
}
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override {
Common::String filename = getSavegameFile(slot, target);
Common::ScopedPtr<Common::InSaveFile> in(g_system->getSavefileManager()->openForLoading(filename));
if (!in)
return SaveStateDescriptor();
char buf[25];
in->seek(0);
in->read(buf, 24);
buf[24] = 0;
Common::String desc = buf;
in->seek(TeenAgent::saveStateSize);
uint32 tag = in->readUint32BE();
if (tag == MKTAG('T', 'N', 'G', 'T')) {
// Skip save version
in->skip(1);
// Skip scene object data
uint32 size = in->readUint32LE();
in->skip(size);
} else {
in->seek(-4, SEEK_CUR);
}
if (!Graphics::checkThumbnailHeader(*in))
return SaveStateDescriptor(this, slot, desc);
SaveStateDescriptor ssd(this, slot, desc);
//checking for the thumbnail
Graphics::Surface *thumbnail;
if (!Graphics::loadThumbnail(*in, thumbnail)) {
return SaveStateDescriptor();
}
ssd.setThumbnail(thumbnail);
return ssd;
}
Common::KeymapArray initKeymaps(const char *target) const override;
};
Common::KeymapArray TeenAgentMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace TeenAgent;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "teenagent-default", _("Default keymappings"));
Common::Action *act;
act = new Common::Action(kStandardActionLeftClick, _("Move / Examine"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Common::Action(kStandardActionRightClick, _("Interact"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Common::Action("SKIPDLG", _("Skip dialog"));
act->setCustomEngineActionEvent(kActionSkipDialog);
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("SPACE");
act->addDefaultInputMapping("JOY_Y");
engineKeyMap->addAction(act);
act = new Common::Action("CLOSEINV", _("Close inventory"));
act->setCustomEngineActionEvent(kActionCloseInventory);
act->addDefaultInputMapping("ESCAPE");
engineKeyMap->addAction(act);
act = new Common::Action("TOGGLEINV", _("Toggle inventory"));
act->setCustomEngineActionEvent(kActionToggleInventory);
act->addDefaultInputMapping("RETURN");
act->addDefaultInputMapping("JOY_X");
engineKeyMap->addAction(act);
act = new Common::Action("SKIPINTRO", _("Skip intro"));
act->setCustomEngineActionEvent(kActionSkipIntro);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
// I18N: Speeds up the game to twice its normal speed
act = new Common::Action("FASTMODE", _("Toggle fast mode"));
act->setCustomEngineActionEvent(kActionFastMode);
act->addDefaultInputMapping("C+f");
act->addDefaultInputMapping("JOY_UP");
engineKeyMap->addAction(act);
return Keymap::arrayOf(engineKeyMap);
}
#if PLUGIN_ENABLED_DYNAMIC(TEENAGENT)
REGISTER_PLUGIN_DYNAMIC(TEENAGENT, PLUGIN_TYPE_ENGINE, TeenAgentMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TEENAGENT, PLUGIN_TYPE_ENGINE, TeenAgentMetaEngine);
#endif

View File

@@ -0,0 +1,31 @@
MODULE := engines/teenagent
MODULE_OBJS := \
actor.o \
animation.o \
callbacks.o \
console.o \
dialog.o \
font.o \
inventory.o \
metaengine.o \
music.o \
objects.o \
pack.o \
resources.o \
scene.o \
segment.o \
surface.o \
surface_list.o \
teenagent.o
# This module can be built as a plugin
ifeq ($(ENABLE_TEENAGENT), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

152
engines/teenagent/music.cpp Normal file
View File

@@ -0,0 +1,152 @@
/* 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 "teenagent/music.h"
#include "teenagent/resources.h"
#include "teenagent/teenagent.h"
#include "common/debug.h"
#include "common/ptr.h"
#include "common/textconsole.h"
namespace TeenAgent {
static const uint32 noteToPeriod[3][12] = {
{855, 807, 761, 720, 678, 640, 604, 569, 537, 508, 480, 453},
{428, 404, 381, 360, 338, 320, 301, 285, 269, 254, 239, 226},
{214, 201, 189, 179, 170, 160, 151, 143, 135, 127, 120, 113}
};
MusicPlayer::MusicPlayer(TeenAgentEngine *vm) : Paula(false, 44100, 5000), _vm(vm), _id(0), _currRow(0) {
}
MusicPlayer::~MusicPlayer() {
stop();
}
bool MusicPlayer::load(int id) {
debugC(0, kDebugMusic, "MusicPlayer::load(%d)", id);
Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->res->mmm.getStream(id));
if (!stream)
return false;
char header[4];
stream->read(header, 4);
// check header?
Common::StackLock lock(_mutex);
// Load the samples
byte sampleCount = stream->readByte();
debugC(0, kDebugMusic, "sampleCount = %d", sampleCount);
for (byte currSample = 0; currSample < sampleCount; currSample++) {
byte sample = stream->readByte();
// Load the sample data
byte sampleResource = ((sample >> 4) & 0x0f) * 10 + (sample & 0x0f);
debugC(0, kDebugMusic, "currSample = %d, sample = 0x%02x, resource: %d", currSample, sample, sampleResource);
uint32 sampleSize = _vm->res->sam_mmm.getSize(sampleResource);
if (sampleSize == 0) {
warning("load: invalid sample %d (0x%02x)", sample, sample);
_samples[sample].clear();
continue;
}
_samples[sample].resize(sampleSize);
_vm->res->sam_mmm.read(sampleResource, _samples[sample].data, sampleSize);
}
// Load the music data
_rows.clear();
Row row;
while (!stream->eos()) {
byte cmd = stream->readByte();
if (cmd < 0x40) {
row.channels[0].note = cmd;
row.channels[1].note = stream->readByte();
row.channels[2].note = stream->readByte();
_rows.push_back(row);
} else if ((cmd & 0xf0) == 0x50) {
byte sample = stream->readByte();
debugC(1, kDebugMusic, "%02x: set sample %02x", cmd, sample);
row.channels[(cmd & 0x0f) - 1].sample = sample;
} else if ((cmd & 0xf0) == 0x40) {
byte vol = stream->readByte();
debugC(1, kDebugMusic, "%02x: set volume %02x -> %02x", cmd, row.channels[(cmd & 0x0f) - 1].volume, vol);
// channel volume 0x40 * music volume 0x40 mixed with high bytes
row.channels[(cmd & 0x0f) - 1].volume = vol * 16;
} else {
debugC(0, kDebugMusic, "unhandled music command %02x", cmd);
}
}
_currRow = 0;
_id = id;
return true;
}
void MusicPlayer::start() {
_currRow = 0;
startPaula();
}
void MusicPlayer::stop() {
stopPaula();
}
void MusicPlayer::interrupt() {
if (_rows.empty())
return;
_currRow %= _rows.size();
Row *row = &_rows[_currRow];
for (int chn = 0; chn < 3; ++chn) {
setChannelVolume(chn, row->channels[chn].volume);
debugC(2, kDebugMusic, "row->channels[%d].volume = %d", chn, row->channels[chn].volume);
byte sample = (row->channels[chn].sample);
if (row->channels[chn].note != 0 && sample != 0) {
debugC(2, kDebugMusic, "row->channels[%d].note = %d", chn, row->channels[chn].note);
debugC(2, kDebugMusic, "row->channels[%d].sample = %d", chn, row->channels[chn].sample);
byte note = row->channels[chn].note;
if (_samples[sample].size == 0) {
warning("interrupt: invalid sample %u (0x%02x)", sample, sample);
continue;
}
setChannelData(chn, (const int8 *)_samples[sample].data, NULL, _samples[sample].size, 0);
setChannelPeriod(chn, noteToPeriod[((note >> 4) & 0x0f) - 1][(note & 0x0f)]);
}
}
debugC(2, kDebugMusic, "------------------------------------------------");
++_currRow;
}
} // End of namespace TeenAgent

85
engines/teenagent/music.h Normal file
View File

@@ -0,0 +1,85 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TEEN_MUSIC_H
#define TEEN_MUSIC_H
#include "audio/mods/paula.h"
#include "common/array.h"
namespace TeenAgent {
class TeenAgentEngine;
class MusicPlayer : public Audio::Paula {
public:
MusicPlayer(TeenAgentEngine *vm);
~MusicPlayer() override;
bool load(int id);
int getId() const { return _id; }
void start();
void stop();
private:
TeenAgentEngine *_vm;
int _id;
struct Row {
struct Channel {
byte sample;
byte volume;
byte note;
Channel(): sample(0), volume(0x40), note(0) {}
} channels[3];
};
struct Sample {
byte *data;
uint size;
Sample(): data(0), size(0) {}
~Sample() { delete[] data; }
void resize(uint s) {
if (s != size) {
delete[] data;
data = new byte[s];
size = s;
}
}
void clear() {
delete[] data;
data = 0;
size = 0;
}
} _samples[256];
Common::Array<Row> _rows;
uint _currRow;
void interrupt() override;
};
} // End of namespace Teen
#endif // TEEN_MUSIC_H

View File

@@ -0,0 +1,200 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/memstream.h"
#include "teenagent/objects.h"
#include "teenagent/resources.h"
#include "teenagent/teenagent.h"
namespace TeenAgent {
void Rect::load(byte *src) {
_base = src;
Common::MemoryReadStream ins(src, 8);
left = ins.readUint16LE();
top = ins.readUint16LE();
right = ins.readUint16LE();
bottom = ins.readUint16LE();
}
void Rect::save() const {
assert(_base != NULL);
Common::MemoryWriteStream outs(_base, 8);
outs.writeUint16LE(left);
outs.writeUint16LE(top);
outs.writeUint16LE(right);
outs.writeUint16LE(bottom);
}
void Rect::render(Graphics::Surface *surface, uint8 color) const {
surface->hLine(left, bottom, right, color);
surface->vLine(left, bottom, top, color);
surface->hLine(left, top, right, color);
surface->vLine(right, bottom, top, color);
}
void Object::load(byte *src, byte sceneId) {
_base = src;
_addr = src - g_engine->res->eseg.ptr(0);
id = *src++;
rect.load(src);
src += 8;
actorRect.load(src);
src += 8;
actorOrientation = *src++;
enabled = *src++;
name = (const char *)src;
_nameSize = name.size() + 1;
src += _nameSize;
bool hasRealName = (sceneId == 6 && id == 4) ||
(sceneId == 23 && id == 2) ||
(sceneId == 20 && id == 13) ||
(sceneId == 32 && id == 1);
// Skip free space (if any) made for objects that have newName
if (hasRealName) {
while (*src == 0)
src++;
}
if (*src == 1)
_hasDefaultDescription = true;
description = parseDescription((const char *)src);
if (hasRealName) {
src += description.size() + 2;
_realName = (const char *)src;
}
}
void Object::save() const {
assert(_base != NULL);
rect.save();
actorRect.save();
_base[17] = actorOrientation;
_base[18] = enabled;
}
void Object::setRealName() {
assert(_base != 0);
Common::strcpy_s((char *)(_base + 19), _nameSize, _realName.c_str());
name = _realName;
}
void Object::dump(int level) const {
debugC(level, kDebugObject, "object: %u %u [%u,%u,%u,%u], actor: [%u,%u,%u,%u], orientation: %u, name: %s", id, enabled,
rect.left, rect.top, rect.right, rect.bottom,
actorRect.left, actorRect.top, actorRect.right, actorRect.bottom,
actorOrientation, name.c_str()
);
}
Common::String Object::parseDescription(const char *desc) {
if (*desc == 0)
return Common::String();
Common::String result;
while (*desc != 1 && *desc != 0) {
Common::String line;
while (*desc != 1 && *desc != 0) {
debugC(2, kDebugObject, "%02x ", *desc);
line += *desc++;
}
if (line.empty())
break;
++desc;
result += line;
result += '\n';
}
if (!result.empty())
result.deleteLastChar();
else
result = g_engine->parseMessage(g_engine->res->getMessageAddr(kCoolMsg));
return result;
}
void InventoryObject::load(byte *src) {
_base = src;
id = *src++;
animated = *src++;
name = (const char *)src;
description = Object::parseDescription((const char *)src);
}
void UseHotspot::load(byte *src) {
Common::MemoryReadStream in(src, 9);
inventoryId = in.readByte();
objectId = in.readByte();
orientation = in.readByte();
actorX = in.readUint16LE();
actorY = in.readUint16LE();
callback = in.readUint16LE();
}
void UseHotspot::dump(int level) const {
debugC(level, kDebugObject,
"hotspot: invId: %02x, objId: %02x, orientation: %02x, actor position: (%d,%d), callback: %04x",
inventoryId, objectId, orientation, actorX, actorY, callback
);
}
void Walkbox::dump(int level) const {
debugC(level, kDebugObject, "walkbox %02x %02x [%d, %d, %d, %d] top: %u, right: %u, bottom: %u, left: %u",
type, orientation,
rect.left, rect.top, rect.right, rect.bottom,
sideHint[0], sideHint[1], sideHint[2], sideHint[3]);
}
void Walkbox::load(byte *src) {
_base = src;
type = *src++;
orientation = *src++;
rect.load(src);
src += 8;
for (byte i = 0; i < 4; ++i)
sideHint[i] = *src++;
}
void Walkbox::save() const {
assert(_base != NULL);
_base[0] = type;
_base[1] = orientation;
rect.save();
}
void FadeType::load(byte *src) {
rect.load(src);
src += 8;
value = *src;
}
} // End of namespace TeenAgent

246
engines/teenagent/objects.h Normal file
View File

@@ -0,0 +1,246 @@
/* 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 TEENAGENT_OBJECTS_H
#define TEENAGENT_OBJECTS_H
#include "common/rect.h"
#include "graphics/surface.h"
#include "teenagent/teenagent.h"
namespace TeenAgent {
enum {kActorUp = 1, kActorRight = 2, kActorDown = 3, kActorLeft = 4 };
struct Rect {
int16 left, top, right, bottom;
inline Rect() : left(0), top(0), right(0), bottom(0), _base(NULL) {}
inline Rect(const Common::Rect &r) : left(r.left), top(r.top), right(r.right), bottom(r.bottom), _base(NULL) {}
inline Rect(uint16 l, uint16 t, uint16 r, uint16 b) : left(l), top(t), right(r), bottom(b), _base(NULL) {}
inline bool in(const Common::Point &point) const {
return point.x >= left && point.x <= right && point.y >= top && point.y <= bottom;
}
inline Common::Point center() const {
return Common::Point((right + left) / 2, (bottom + top) / 2);
}
inline bool valid() const {
return left >= 0 && left < kScreenWidth && right >= 0 && right < kScreenWidth && top >= 0 && top < kScreenHeight && bottom >= 0 && bottom < kScreenHeight;
}
void render(Graphics::Surface *surface, uint8 color) const;
void dump(int level = 0) const {
debugC(level, kDebugObject, "rect[%u, %u, %u, %u]", left, top, right, bottom);
}
inline void clear() {
left = top = right = bottom = 0;
}
void load(byte *src); //8 bytes
void save() const;
inline bool intersects_hline(int x1, int x2, int y) const {
if (x1 > x2)
SWAP(x1, x2);
return y >= top && y <= bottom && x1 <= right && x2 >= left;
}
inline bool intersects_vline(int x, int y1, int y2) const {
if (y1 > y2)
SWAP(y1, y2);
return x >= left && x <= right && y1 <= bottom && y2 >= top;
}
inline bool contains(const Rect &rect) const {
return rect.left >= left && rect.right <= right && rect.top >= top && rect.bottom <= bottom;
}
static inline bool inside(int x, int a, int b) {
if (a > b)
SWAP(a, b);
return x >= a && x <= b;
}
int intersects_line(const Common::Point &a, const Common::Point &b) const {
int dy = b.y - a.y, dx = b.x - a.x;
int mask = 0; //orientation bitmask: 1 - top, 2 - right, 3 - bottom, 4 - left
if (dx != 0) {
int yl = (left - a.x) * dy / dx + a.y;
if (yl > top && yl < bottom && inside(yl, a.y, b.y) && inside(left, a.x, b.x)) {
//c[idx++] = Common::Point(left, yl);
mask |= 8;
}
int yr = (right - a.x) * dy / dx + a.y;
if (yr > top && yr < bottom && inside(yr, a.y, b.y) && inside(right, a.x, b.x)) {
//c[idx++] = Common::Point(right, yr);
mask |= 2;
}
}
if (dy != 0) {
int xt = (top - a.y) * dx / dy + a.x;
if (xt > left && xt < right && inside(xt, a.x, b.x) && inside(top, a.y, b.y)) {
//assert(idx < 2);
//c[idx++] = Common::Point(xt, top);
mask |= 1;
}
int xb = (bottom - a.y) * dx / dy + a.x;
if (xb > left && xb < right && inside(xb, a.x, b.x) && inside(bottom, a.y, b.y)) {
//assert(idx < 2);
//c[idx++] = Common::Point(xb, bottom);
mask |= 4;
}
}
return mask;
}
void side(Common::Point &p1, Common::Point &p2, int o, const Common::Point &nearest) const {
switch (o) {
case kActorLeft:
p1 = Common::Point(left, top);
p2 = Common::Point(left, bottom);
break;
case kActorRight:
p1 = Common::Point(right, top);
p2 = Common::Point(right, bottom);
break;
case kActorUp:
p1 = Common::Point(left, top);
p2 = Common::Point(right, top);
break;
case kActorDown:
p1 = Common::Point(left, bottom);
p2 = Common::Point(right, bottom);
break;
default:
p1 = Common::Point();
p2 = Common::Point();
}
if (p1.sqrDist(nearest) >= p2.sqrDist(nearest))
SWAP(p1, p2);
}
protected:
byte *_base;
};
struct Object {
byte id; //0
Rect rect; //1
Rect actorRect; //9
byte actorOrientation; //17
byte enabled; //18
//19
Common::String name, description;
Object(): _base(NULL), _nameSize(0) { id = 0; actorOrientation = 0; enabled = 0; _hasDefaultDescription = false; }
void dump(int level = 0) const;
void setRealName();
void load(byte *addr, byte sceneId = 0);
void save() const;
bool hasDefaultDescription() { return _hasDefaultDescription; }
uint32 getAddr() { return _addr; };
static Common::String parseDescription(const char *desc);
protected:
byte *_base;
size_t _nameSize;
// New name that will be set when certain event is triggered
Common::String _realName;
bool _hasDefaultDescription;
uint32 _addr = 0; // Address inside eseg
};
struct InventoryObject {
byte id;
byte animated;
Common::String name, description;
InventoryObject(): id(0), animated(0), _base(0) {}
void load(byte *addr);
protected:
byte *_base;
};
struct UseHotspot {
byte inventoryId;
byte objectId;
byte orientation;
uint16 actorX, actorY;
uint16 callback;
void load(byte *src);
void dump(int level = 0) const;
};
struct Walkbox {
byte type;
byte orientation;
Rect rect;
byte sideHint[4];
Walkbox() {
_base = nullptr;
type = 0;
orientation = 0;
// rect cleared by Rect constructor
for (uint i = 0; i < ARRAYSIZE(sideHint); i++) {
sideHint[i] = 0;
}
}
void dump(int level = 0) const;
void load(byte *src);
void save() const;
protected:
byte *_base;
};
struct FadeType {
Rect rect;
byte value;
void load(byte *src);
};
//\todo move it to util.h?
template<typename T> inline T SIGN(T x) { return (x > 0) ? 1 : ((x < 0) ? -1 : 0); }
} // End of namespace TeenAgent
#endif

202
engines/teenagent/pack.cpp Normal file
View File

@@ -0,0 +1,202 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "teenagent/pack.h"
#include "teenagent/teenagent.h"
#include "common/util.h"
#include "common/debug.h"
#include "common/memstream.h"
#include "common/substream.h"
namespace TeenAgent {
FilePack::FilePack() : offsets(0) {}
FilePack::~FilePack() {
close();
}
void FilePack::close() {
delete[] offsets;
offsets = NULL;
file.close();
}
bool FilePack::open(const Common::Path &filename) {
if (!file.exists(filename) || !file.open(filename))
return false;
_fileCount = file.readUint32LE();
debugC(0, kDebugPack, "opened %s, found %u entries", filename.toString().c_str(), _fileCount);
offsets = new uint32[_fileCount + 1];
for (uint32 i = 0; i <= _fileCount; ++i) {
offsets[i] = file.readUint32LE();
}
return true;
}
uint32 FilePack::getSize(uint32 id) const {
if (id < 1 || id > _fileCount)
return 0;
return offsets[id] - offsets[id - 1];
}
uint32 FilePack::read(uint32 id, byte *dst, uint32 size) const {
if (id < 1 || id > _fileCount)
return 0;
file.seek(offsets[id - 1]);
uint32 rsize = offsets[id] - offsets[id - 1];
uint32 r = file.read(dst, MIN(rsize, size));
debugC(0, kDebugPack, "read(%u, %u) = %u", id, size, r);
return r;
}
Common::SeekableReadStream *FilePack::getStream(uint32 id) const {
if (id < 1 || id > _fileCount)
return NULL;
debugC(0, kDebugPack, "stream: %04x-%04x", offsets[id - 1], offsets[id]);
return new Common::SeekableSubReadStream(&file, offsets[id - 1], offsets[id]);
}
TransientFilePack::TransientFilePack() : offsets(0) {}
TransientFilePack::~TransientFilePack() {
close();
}
void TransientFilePack::close() {
delete[] offsets;
offsets = NULL;
_filename.clear();
}
bool TransientFilePack::open(const Common::Path &filename) {
_filename = filename;
Common::File file;
if (!file.exists(filename) || !file.open(filename))
return false;
_fileCount = file.readUint32LE();
debugC(0, kDebugPack, "opened %s, found %u entries", filename.toString().c_str(), _fileCount);
offsets = new uint32[_fileCount + 1];
for (uint32 i = 0; i <= _fileCount; ++i) {
offsets[i] = file.readUint32LE();
}
file.close();
return true;
}
uint32 TransientFilePack::getSize(uint32 id) const {
if (id < 1 || id > _fileCount)
return 0;
return offsets[id] - offsets[id - 1];
}
uint32 TransientFilePack::read(uint32 id, byte *dst, uint32 size) const {
if (id < 1 || id > _fileCount)
return 0;
Common::File file;
if (!file.open(_filename))
return 0;
file.seek(offsets[id - 1]);
uint32 rsize = offsets[id] - offsets[id - 1];
uint32 r = file.read(dst, MIN(rsize, size));
file.close();
debugC(0, kDebugPack, "read(%u, %u) = %u", id, size, r);
return r;
}
Common::SeekableReadStream *TransientFilePack::getStream(uint32 id) const {
if (id < 1 || id > _fileCount)
return NULL;
debugC(0, kDebugPack, "stream: %04x-%04x", offsets[id - 1], offsets[id]);
Common::File file;
if (!file.open(_filename))
return NULL;
file.seek(offsets[id - 1]);
uint32 size = offsets[id] - offsets[id - 1];
byte *ptr = (byte *)malloc(size);
if (ptr == NULL)
return NULL;
uint32 r = file.read(ptr, size);
file.close();
return new Common::MemoryReadStream(ptr, r, DisposeAfterUse::YES);
}
void MemoryPack::close() {
chunks.clear();
}
bool MemoryPack::open(const Common::Path &filename) {
Common::File file;
if (!file.exists(filename) || !file.open(filename))
return false;
uint32 count = file.readUint32LE();
debugC(0, kDebugPack, "opened %s, found %u entries [memory]", filename.toString().c_str(), count);
for (uint32 i = 0; i < count; ++i) {
uint32 offset = file.readUint32LE();
int32 pos = file.pos();
uint32 next_offset = file.readUint32LE();
uint32 size = next_offset - offset;
Chunk chunk;
if (size != 0) {
file.seek(offset);
chunk.data = new byte[size];
chunk.size = size;
file.read(chunk.data, size);
file.seek(pos);
}
chunks.push_back(chunk);
}
file.close();
return true;
}
uint32 MemoryPack::getSize(uint32 id) const {
--id;
return id < chunks.size() ? chunks[id].size : 0;
}
uint32 MemoryPack::read(uint32 id, byte *dst, uint32 size) const {
--id;
if (id >= chunks.size())
return 0;
const Chunk &c = chunks[id];
memcpy(dst, c.data, c.size);
return c.size;
}
Common::SeekableReadStream *MemoryPack::getStream(uint32 id) const {
--id;
if (id >= chunks.size())
return 0;
const Chunk &c = chunks[id];
return new Common::MemoryReadStream(c.data, c.size, DisposeAfterUse::NO);
}
} // End of namespace TeenAgent

115
engines/teenagent/pack.h Normal file
View File

@@ -0,0 +1,115 @@
/* 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 TEENAGENT_PACK_H
#define TEENAGENT_PACK_H
#include "common/file.h"
#include "common/array.h"
namespace TeenAgent {
class Pack {
protected:
uint32 _fileCount;
public:
Pack(): _fileCount(0) {}
virtual ~Pack() {}
virtual bool open(const Common::Path &filename) = 0;
virtual void close() = 0;
virtual uint32 fileCount() const { return _fileCount; }
virtual uint32 getSize(uint32 id) const = 0;
virtual uint32 read(uint32 id, byte *dst, uint32 size) const = 0;
virtual Common::SeekableReadStream *getStream(uint32 id) const = 0;
};
///FilePack keeps opened file and returns substream for each request.
class FilePack : public Pack {
mutable Common::File file;
uint32 *offsets;
public:
FilePack();
~FilePack() override;
bool open(const Common::Path &filename) override;
void close() override;
uint32 getSize(uint32 id) const override;
uint32 read(uint32 id, byte *dst, uint32 size) const override;
Common::SeekableReadStream *getStream(uint32 id) const override;
};
/** Pack file which reopens file each request. Do not keep file descriptor open.
** Useful for minimizing file descriptors opened at the same time. Critical for PSP backend.
**/
class TransientFilePack : public Pack {
uint32 *offsets;
Common::Path _filename;
public:
TransientFilePack();
~TransientFilePack() override;
bool open(const Common::Path &filename) override;
void close() override;
uint32 getSize(uint32 id) const override;
uint32 read(uint32 id, byte *dst, uint32 size) const override;
Common::SeekableReadStream *getStream(uint32 id) const override;
};
///MemoryPack loads whole pack in memory, currently unused.
class MemoryPack : public Pack {
struct Chunk {
byte *data;
uint32 size;
inline Chunk(): data(0), size(0) {}
inline Chunk(const Chunk &c) : data(c.data), size(c.size) { c.reset(); }
inline Chunk &operator=(const Chunk &c) {
data = c.data;
size = c.size;
c.reset();
return *this;
}
inline ~Chunk() { delete[] data; }
inline void reset() const {
Chunk *c = const_cast<Chunk *>(this);
c->data = 0;
c->size = 0;
}
};
Common::Array<Chunk> chunks;
public:
bool open(const Common::Path &filename) override;
void close() override;
uint32 getSize(uint32 id) const override;
uint32 read(uint32 id, byte *dst, uint32 size) const override;
Common::SeekableReadStream *getStream(uint32 id) const override;
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,491 @@
/* 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 "engines/advancedDetector.h"
#include "teenagent/resources.h"
#include "teenagent/teenagent.h"
#include "common/debug.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "common/compression/deflate.h"
namespace TeenAgent {
Resources::Resources() {
_dialogsStartOffset = 0;
_sceneObjectsStartOffset = 0;
_sceneObjectsBlockSize = 0;
}
Resources::~Resources() {
off.close();
on.close();
ons.close();
lan000.close();
lan500.close();
mmm.close();
sam_mmm.close();
sam_sam.close();
voices.close();
}
/*
quick note on varia resources:
1: Mark's animations (with head)
2: Mark's idle animation
3: Inventory background
4: Inventory items
5: Metropolis palette
6: TEENAGENT logo (flames)
7: Small font
8: Bigger font
9: Metropolis software house
10: quit registered
11: quit shareware
*/
#define CSEG_SIZE 46000 // 0xb3b0
#define DSEG_SIZE 59280 // 0xe790
#define ESEG_SIZE 35810 // 0x8be2
void Resources::precomputeResourceOffsets(const ResourceInfo &resInfo, Common::Array<uint32> &offsets, uint numTerminators) {
offsets.push_back(resInfo._offset);
uint n = 0;
uint8 current, last = 0xff;
for (uint32 i = resInfo._offset; i < resInfo._offset + resInfo._size; i++) {
current = eseg.get_byte(i);
if (n == numTerminators) {
offsets.push_back(i);
n = 0;
}
if (current != 0x00 && last == 0x00)
n = 0;
if (current == 0x00)
n++;
last = current;
}
}
void Resources::precomputeDialogOffsets(const ResourceInfo &resInfo) {
precomputeResourceOffsets(resInfo, dialogOffsets, 4);
debug(1, "Resources::precomputeDialogOffsets() - Found %d dialogs", dialogOffsets.size());
for (uint i = 0; i < dialogOffsets.size(); i++)
debug(1, "\tDialog #%d: Offset 0x%04x", i, dialogOffsets[i]);
}
void Resources::precomputeCreditsOffsets(const ResourceInfo &resInfo) {
precomputeResourceOffsets(resInfo, creditsOffsets);
debug(1, "Resources::precomputeCreditsOffsets() - Found %d credits", creditsOffsets.size());
for (uint i = 0; i < creditsOffsets.size(); i++)
debug(1, "\tCredit #%d: Offset 0x%04x", i, creditsOffsets[i]);
}
void Resources::precomputeItemOffsets(const ResourceInfo &resInfo) {
precomputeResourceOffsets(resInfo, itemOffsets);
debug(1, "Resources::precomputeItemOffsets() - Found %d items", itemOffsets.size());
for (uint i = 0; i < itemOffsets.size(); i++)
debug(1, "\tItem #%d: Offset 0x%04x", i, itemOffsets[i]);
}
void Resources::precomputeMessageOffsets(const ResourceInfo &resInfo) {
precomputeResourceOffsets(resInfo, messageOffsets);
}
void Resources::precomputeCombinationOffsets(const ResourceInfo &resInfo) {
precomputeResourceOffsets(resInfo, combinationOffsets);
debug(1, "Resources::precomputeCombinationOffsets() - Found %d combination items", combinationOffsets.size());
for (uint i = 0; i < combinationOffsets.size(); i++)
debug(1, "\tCombination #%d: Offset 0x%04x", i, combinationOffsets[i]);
}
void Resources::readDialogStacks(byte *src) {
uint16 base = dsAddr_dialogStackPleadingToMansionGuard;
byte dialogStackWritten = 0;
uint i = 0;
while (dialogStackWritten < kNumDialogStacks) {
uint16 word = READ_LE_UINT16(src + i * 2);
dseg.set_word(base + i * 2, word);
if (word == 0xFFFF)
dialogStackWritten++;
i++;
}
}
void Resources::precomputeAllOffsets(const Common::Array<ResourceInfo> &resourceInfos) {
for (const auto &resInfo : resourceInfos) {
switch ((ResourceType)resInfo._id) {
case kResCombinations:
precomputeCombinationOffsets(resInfo);
break;
case kResCredits:
precomputeCreditsOffsets(resInfo);
break;
case kResDialogs:
_dialogsStartOffset = resInfo._offset;
precomputeDialogOffsets(resInfo);
break;
case kResItems:
precomputeItemOffsets(resInfo);
break;
case kResMessages:
precomputeMessageOffsets(resInfo);
break;
case kResSceneObjects:
_sceneObjectsStartOffset = resInfo._offset;
_sceneObjectsBlockSize = resInfo._size;
break;
case kResDialogStacks:
// fall through
default:
break;
}
}
}
bool Resources::isVoiceIndexEmpty(uint16 index) {
uint size = voices.getSize(index);
if (size == 4 || size == 5)
return true;
return false;
}
void Resources::precomputeVoiceIndices(const Common::Array<ResourceInfo>& resourceInfos) {
byte numTerminators = 0;
uint16 voiceIndex = 0;
for (auto &resInfo : resourceInfos) {
switch ((ResourceType)resInfo._id) {
case kResMessages:
voiceIndex = 1;
numTerminators = 2;
break;
case kResCombinations:
voiceIndex = 567;
numTerminators = 2;
break;
case kResItems:
voiceIndex = 592;
numTerminators = 2;
break;
case kResDialogs:
voiceIndex = 902;
numTerminators = 4;
break;
case kResCredits:
case kResDialogStacks:
case kResSceneObjects:
// There are no voiceovers for credits and dialog stacks.
// For scene objects, voice indices calculated separately
// in Scene::loadObjectData()
continue;
default:
break;
}
_addrToVoiceIndx[resInfo._offset] = voiceIndex++;
uint16 currentNum = 1;
uint n = 0; // number of consecutive zero bytes
byte current, last = 0xff;
bool setNoIMessage = false;
for (uint32 i = resInfo._offset; i < resInfo._offset + resInfo._size; i++) {
current = eseg.get_byte(i);
if (n == numTerminators) {
currentNum++;
n = 0;
if ((ResourceType)resInfo._id == kResCombinations) {
uint16 nthCombination = currentNum - 1;
// For dublicate combination messages don't increment voice index
if (nthCombination == 3 || nthCombination == 5 ||
nthCombination == 15 || nthCombination == 16 || nthCombination == 17 ||
nthCombination == 18 || nthCombination == 22 || nthCombination == 26) {
_addrToVoiceIndx[i] = voiceIndex - 1;
} else if (nthCombination == 28) {
_addrToVoiceIndx[i] = voiceIndex - 2;
} else {
_addrToVoiceIndx[i] = voiceIndex++;
}
} else if ((ResourceType)resInfo._id == kResDialogs) {
if (voiceIndex == 1416) {
// "Dzie= dobry, panie robocie." starts at 1418
voiceIndex += 2;
_addrToVoiceIndx[i] = voiceIndex++;
} else if (voiceIndex == 1864) {
// "Jak ju< powiedzia%em, nasza organizacja" starts at 1867
voiceIndex += 3;
_addrToVoiceIndx[i] = voiceIndex++;
} else if (isVoiceIndexEmpty(voiceIndex)) {
voiceIndex += 1;
if (current != 0x00)
_addrToVoiceIndx[i] = voiceIndex++;
} else if (voiceIndex == 1801) {
_addrToVoiceIndx[i] = 2041; // "]adna pogoda."
} else if (voiceIndex == 1809) {
_addrToVoiceIndx[i] = 2042; // "Sir, mamy sygna%y, <e..."
} else {
if (current != 0x00)
_addrToVoiceIndx[i] = voiceIndex++;
}
} else if ((ResourceType)resInfo._id == kResMessages) {
if (currentNum == 334) { // Combination error message
// HACK: Use most good sounding (sigh) version
// TODO: Find the correct voice index used in the original
_addrToVoiceIndx[i] = 1304;
} else
_addrToVoiceIndx[i] = voiceIndex++;
} else {
_addrToVoiceIndx[i] = voiceIndex++;
}
}
if (current != 0x00 && last == 0x00) {
if ((ResourceType)resInfo._id == kResDialogs) {
if (n == 2 || n == 3) {
// "...to czemu nie u<y^ dziwnych" at 1886
// "Sze$^ miesi#cy temu z%oto i got*wka" at 1921
if (voiceIndex == 1885 || voiceIndex == 1920 || isVoiceIndexEmpty(voiceIndex)) {
voiceIndex += 1;
_addrToVoiceIndx[i] = voiceIndex++;
} else if (voiceIndex == 1923 && !setNoIMessage) {
_addrToVoiceIndx[i] = 1885; // "No i?..."
setNoIMessage = true;
} else {
_addrToVoiceIndx[i] = voiceIndex++;
}
} else if (n == 1 && (voiceIndex == 1720 || voiceIndex == 1852)) {
// Because of the rare case with
// NEW_LINE at the beginning of dialogs 163, 190
// we have to assign voiceIndex here
_addrToVoiceIndx[i] = voiceIndex++;
}
}
n = 0;
}
if (current == 0x00)
n++;
last = current;
}
}
}
bool Resources::loadArchives(const ADGameDescription *gd) {
Common::File *dat_file = new Common::File();
Common::String filename = "teenagent.dat";
if (!dat_file->open(filename.c_str())) {
delete dat_file;
const char *msg = _s("Unable to locate the '%s' engine data file.");
Common::U32String errorMessage = Common::U32String::format(_(msg), filename.c_str());
warning(msg, filename.c_str());
GUIErrorMessage(errorMessage);
return false;
}
// teenagent.dat used to be compressed with zlib compression. The usage of
// zlib here is no longer needed, and it's maintained only for backwards
// compatibility.
Common::SeekableReadStream *dat = Common::wrapCompressedReadStream(dat_file);
byte tempBuffer[256];
dat->read(tempBuffer, 9);
tempBuffer[9] = '\0';
if (strcmp((char *)tempBuffer, "TEENAGENT") != 0) {
const char *msg = _s("The '%s' engine data file is corrupt.");
Common::U32String errorMessage = Common::U32String::format(_(msg), filename.c_str());
GUIErrorMessage(errorMessage);
warning(msg, filename.c_str());
return false;
}
byte version = dat->readByte();
if (version != TEENAGENT_DAT_VERSION) {
const char *msg = _s("Incorrect version of the '%s' engine data file found. Expected %d but got %d.");
Common::U32String errorMessage = Common::U32String::format(_(msg), filename.c_str(), TEENAGENT_DAT_VERSION, version);
GUIErrorMessage(errorMessage);
warning(msg, filename.c_str(), TEENAGENT_DAT_VERSION, version);
return false;
}
dat->skip(CSEG_SIZE);
dseg.read(dat, DSEG_SIZE);
// Locate the correct language block
bool found = false;
while (!found) {
dat->read(tempBuffer, 5);
if (tempBuffer[0] == 0xff) {
error("Could not locate correct language block");
}
if (gd->language == tempBuffer[0]) {
found = true;
uint32 dataOffset = READ_LE_UINT32(&tempBuffer[1]);
dat->seek(dataOffset);
}
}
Common::Array<ResourceInfo> resourceInfos(kNumResources);
uint32 allResourcesSize = 0;
for (auto &resInfo : resourceInfos) {
resInfo._id = dat->readByte();
resInfo._offset = dat->readUint32LE();
resInfo._size = dat->readUint32LE();
// Don't count Dialog stack's size
// since it will be stored in dseg, not eseg
if ((ResourceType)resInfo._id != kResDialogStacks)
allResourcesSize += resInfo._size;
}
// Dialog stack data
dat->read(tempBuffer, resourceInfos[(uint)kResDialogStacks]._size);
readDialogStacks((byte *)tempBuffer);
// Store rest of the resources to eseg
eseg.read(dat, allResourcesSize);
delete dat;
precomputeAllOffsets(resourceInfos);
FilePack varia;
varia.open("varia.res");
font7.load(varia, 7, 11, 1);
font8.load(varia, 8, 31, 0);
varia.close();
off.open("off.res");
on.open("on.res");
ons.open("ons.res");
lan000.open("lan_000.res");
lan500.open("lan_500.res");
mmm.open("mmm.res");
sam_mmm.open("sam_mmm.res");
sam_sam.open("sam_sam.res");
voices.open("voices.res");
if (gd->language == Common::PL_POL)
precomputeVoiceIndices(resourceInfos);
return true;
}
void Resources::loadOff(Graphics::Surface &surface, byte *palette, int id) {
uint32 size = off.getSize(id);
if (size == 0) {
error("invalid background %d", id);
return;
}
const uint bufferSize = 64768;
byte *buf = (byte *)malloc(bufferSize);
if (!buf)
error("[Resources::loadOff] Cannot allocate buffer");
off.read(id, buf, bufferSize);
byte *src = buf;
byte *dst = (byte *)surface.getPixels();
memcpy(dst, src, 64000);
memcpy(palette, buf + 64000, 768);
free(buf);
}
Common::SeekableReadStream *Resources::loadLan(uint32 id) const {
return id <= 500 ? loadLan000(id) : lan500.getStream(id - 500);
}
Common::SeekableReadStream *Resources::loadLan000(uint32 id) const {
switch (id) {
case 81:
if (dseg.get_byte(dsAddr_dogHasBoneFlag))
return lan500.getStream(160);
break;
case 137:
if (dseg.get_byte(dsAddr_mansionTVOnFlag) == 1) {
if (dseg.get_byte(dsAddr_mansionVCRPlayingTapeFlag) == 1)
return lan500.getStream(203);
else
return lan500.getStream(202);
}
break;
case 25:
if (dseg.get_byte(dsAddr_FirstActTrialState) == 2) {
return lan500.getStream(332);
}
break;
case 37:
if (dseg.get_byte(dsAddr_act1GuardState) == 1) {
return lan500.getStream(351);
} else if (dseg.get_byte(dsAddr_act1GuardState) == 2) {
return lan500.getStream(364);
}
break;
case 29:
if (dseg.get_byte(dsAddr_birdOnBarRadioAntennaFlag) == 1) {
return lan500.getStream(380);
}
break;
case 30:
if (dseg.get_byte(dsAddr_birdOnBarRadioAntennaFlag) == 1) {
return lan500.getStream(381);
}
break;
case 42:
if (dseg.get_byte(dsAddr_johnNotyOutsideMansionDoorFlag) == 1) {
return lan500.getStream(400);
}
break;
default:
break;
}
return lan000.getStream(id);
}
} // End of namespace TeenAgent

File diff suppressed because it is too large Load Diff

1310
engines/teenagent/scene.cpp Normal file

File diff suppressed because it is too large Load Diff

267
engines/teenagent/scene.h Normal file
View File

@@ -0,0 +1,267 @@
/* 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 TEENAGENT_SCENE_H
#define TEENAGENT_SCENE_H
#include "teenagent/surface.h"
#include "teenagent/actor.h"
#include "teenagent/objects.h"
#include "teenagent/surface.h"
#include "teenagent/surface_list.h"
#include "teenagent/teenagent.h"
#include "common/array.h"
#include "common/list.h"
namespace Common {
struct Event;
}
namespace TeenAgent {
class TeenAgentEngine;
struct SceneEvent {
enum Type {
kNone, //0
kMessage,
kWalk,
kPlayAnimation,
kPlayActorAnimation, //4
kPauseAnimation,
kClearAnimations,
kLoadScene,
kSetOn, //8
kSetLan,
kPlayMusic,
kPlaySound,
kEnableObject, //12
kHideActor,
kWaitForAnimation,
kWaitLanAnimationFrame,
kCreditsMessage, //16
kCredits,
kTimer,
kEffect,
kFade,
kWait,
kSetFlag,
kQuit
} type;
Common::String message;
uint16 voiceId;
byte color;
byte slot;
union {
uint16 animation;
uint16 callback;
};
uint16 timer;
byte orientation;
Common::Point dst;
byte scene; //fixme: put some of these to the union?
byte ons;
byte lan;
union {
byte music;
byte firstFrame;
};
union {
byte sound;
byte lastFrame;
};
byte object;
int characterID;
SceneEvent(Type type_) :
type(type_), message(), color(textColorMark), slot(0), animation(0), timer(0), orientation(0), dst(),
scene(0), ons(0), lan(0), music(0), sound(0), object(0) {}
void clear() {
type = kNone;
message.clear();
voiceId = 0;
color = textColorMark;
slot = 0;
orientation = 0;
animation = 0;
timer = 0;
dst.x = dst.y = 0;
scene = 0;
ons = 0;
lan = 0;
music = 0;
sound = 0;
object = 0;
}
inline bool empty() const {
return type == kNone;
}
void dump() const {
debugC(0, kDebugScene, "event[%d]: \"%s\"[%02x], slot: %d, animation: %u, timer: %u, dst: (%d, %d) [%u], scene: %u, ons: %u, lan: %u, object: %u, music: %u, sound: %u",
(int)type, message.c_str(), color, slot, animation, timer, dst.x, dst.y, orientation, scene, ons, lan, object, music, sound
);
}
};
class Scene {
public:
Scene(TeenAgentEngine *engine);
~Scene();
bool intro;
void init(int id, const Common::Point &pos);
bool render(bool tickGame, bool tickMark, uint32 messageDelta);
int getId() const { return _id; }
void warp(const Common::Point &point, byte orientation = 0);
void moveTo(const Common::Point &point, byte orientation = 0, bool validate = false);
Common::Point getPosition() const { return position; }
void displayMessage(const Common::String &str, uint16 voiceIndex, byte color = textColorMark, const Common::Point &pos = Common::Point());
void setOrientation(uint8 o) { orientation = o; }
void push(const SceneEvent &event);
byte peekFlagEvent(uint16 addr) const;
SceneEvent::Type last_event_type() const { return !events.empty() ? events.back().type : SceneEvent::kNone; }
bool processEvent(const Common::Event &event);
void clear();
byte *getOns(int id);
byte *getLans(int id);
bool eventRunning() const { return !currentEvent.empty(); }
Walkbox *getWalkbox(byte id) { return &walkboxes[_id - 1][id]; }
Object *getObject(int id, int sceneId = 0);
Object *findObject(const Common::Point &point);
void loadObjectData();
Animation *getAnimation(byte slot);
inline Animation *getActorAnimation() { return &actorAnimation; }
inline const Common::String &getMessage() const { return message; }
void setPalette(unsigned mul);
int lookupZoom(uint y) const;
private:
void loadOns();
void loadLans();
void playAnimation(byte idx, uint id, bool loop, bool paused, bool ignore);
void playActorAnimation(uint id, bool loop, bool ignore);
byte palette[3 * 256];
void paletteEffect(byte step);
byte findFade() const;
Common::Point messagePosition(const Common::String &str, Common::Point pos);
uint messageDuration(const Common::String &str);
bool processEventQueue();
inline bool nextEvent() {
currentEvent.clear();
return processEventQueue();
}
void clearMessage();
TeenAgentEngine *_vm;
int _id;
Graphics::Surface background;
SurfaceList on;
bool onEnabled;
Surface *ons;
uint32 _onsCount;
Animation actorAnimation, animation[4], customAnimation[4];
Common::Rect actorAnimationPosition, animationPosition[4];
Actor teenagent, teenagentIdle;
Common::Point position;
typedef Common::List<Common::Point> Path;
Path path;
uint8 orientation;
bool actorTalking;
bool findPath(Path &p, const Common::Point &src, const Common::Point &dst) const;
Common::Array<Common::Array<Object> > objects;
Common::Array<Common::Array<Walkbox> > walkboxes;
Common::Array<Common::Array<FadeType> > fades;
Common::String message;
Common::Point messagePos;
byte _messageColor;
uint messageTimer;
byte messageFirstFrame;
byte messageLastFrame;
Animation *messageAnimation;
uint16 _voiceId;
typedef Common::List<SceneEvent> EventList;
EventList events;
SceneEvent currentEvent;
bool hideActor;
uint16 callback, callbackTimer;
int _fadeTimer;
byte _fadeOld;
uint _idleTimer;
struct Sound {
byte id, delay;
Sound(byte i, byte d): id(i), delay(d) {}
};
typedef Common::List<Sound> Sounds;
Sounds sounds;
struct DebugFeatures {
enum {
kShowBack,
kShowLan,
kShowOns,
kShowOn,
kHidePath,
kMax
};
bool feature[kMax];
DebugFeatures() {
for (uint i = 0; i < kMax; ++i) {
feature[i] = true;
}
}
} debugFeatures;
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,39 @@
/* 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 "teenagent/segment.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace TeenAgent {
void Segment::read(Common::ReadStream *stream, uint32 s) {
_size = s;
_data = new byte[_size];
if (stream->read(_data, _size) != _size)
error("Segment::read: corrupted data");
}
Segment::~Segment() {
delete[] _data;
}
} // End of namespace TeenAgent

View File

@@ -0,0 +1,75 @@
/* 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 TEENAGENT_SEGMENT_H
#define TEENAGENT_SEGMENT_H
#include "common/stream.h"
#include "common/endian.h"
namespace TeenAgent {
class Segment {
uint32 _size;
byte *_data;
public:
Segment() : _size(0), _data(0) {}
~Segment();
void read(Common::ReadStream *s, uint32 _size);
inline byte get_byte(uint32 offset) const {
assert(offset < _size);
return _data[offset];
}
inline uint16 get_word(uint32 offset) const {
assert(offset + 1 < _size);
return READ_LE_UINT16(_data + offset);
}
inline void set_byte(uint32 offset, byte v) const {
assert(offset < _size);
_data[offset] = v;
}
inline void set_word(uint32 offset, uint16 v) const {
assert(offset + 1 < _size);
return WRITE_LE_UINT16(_data + offset, v);
}
const byte *ptr(uint32 addr) const {
assert(addr < _size);
return _data + addr;
}
byte *ptr(uint32 addr) {
assert(addr < _size);
return _data + addr;
}
uint size() const { return _size; }
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,124 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "teenagent/surface.h"
#include "teenagent/pack.h"
#include "teenagent/teenagent.h"
#include "common/stream.h"
#include "common/debug.h"
namespace TeenAgent {
Surface::Surface() : x(0), y(0) {
}
Surface::~Surface() {
free();
}
void Surface::load(Common::SeekableReadStream &stream, Type type) {
debugC(0, kDebugSurface, "load()");
free();
x = y = 0;
uint16 w_ = stream.readUint16LE();
uint16 h_ = stream.readUint16LE();
if (type != kTypeLan) {
uint16 pos = stream.readUint16LE();
x = pos % kScreenWidth;
y = pos / kScreenWidth;
}
debugC(0, kDebugSurface, "declared info: %ux%u (%04xx%04x) -> %u,%u", w_, h_, w_, h_, x, y);
if (stream.eos() || w_ == 0)
return;
if (w_ * h_ > stream.size()) {
debugC(0, kDebugSurface, "invalid surface %ux%u -> %u,%u", w_, h_, x, y);
return;
}
debugC(0, kDebugSurface, "creating surface %ux%u -> %u,%u", w_, h_, x, y);
create(w_, h_, Graphics::PixelFormat::createFormatCLUT8());
stream.read(pixels, w_ * h_);
}
Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mirror, Common::Rect srcRect, uint zoom) const {
if (srcRect.isEmpty()) {
srcRect = Common::Rect(0, 0, w, h);
}
Common::Rect dstRect(x + dx, y + dy, x + dx + zoom * srcRect.width() / 256, y + dy + zoom * srcRect.height() / 256);
if (dstRect.left < 0) {
srcRect.left = -dstRect.left;
dstRect.left = 0;
}
if (dstRect.right > surface->w) {
srcRect.right -= dstRect.right - surface->w;
dstRect.right = surface->w;
}
if (dstRect.top < 0) {
srcRect.top -= dstRect.top;
dstRect.top = 0;
}
if (dstRect.bottom > surface->h) {
srcRect.bottom -= dstRect.bottom - surface->h;
dstRect.bottom = surface->h;
}
if (srcRect.isEmpty() || dstRect.isEmpty())
return Common::Rect();
if (zoom == 256) {
const byte *src = (const byte *)getBasePtr(0, srcRect.top);
byte *dstBase = (byte *)surface->getBasePtr(dstRect.left, dstRect.top);
for (int i = srcRect.top; i < srcRect.bottom; ++i) {
byte *dst = dstBase;
for (int j = srcRect.left; j < srcRect.right; ++j) {
byte p = src[(mirror ? w - j - 1 : j)];
if (p != 0xff)
*dst++ = p;
else
++dst;
}
dstBase += surface->pitch;
src += pitch;
}
} else {
byte *dst = (byte *)surface->getBasePtr(dstRect.left, dstRect.top);
for (int i = 0; i < dstRect.height(); ++i) {
for (int j = 0; j < dstRect.width(); ++j) {
int px = j * 256 / zoom;
const byte *src = (const byte *)getBasePtr(srcRect.left + (mirror ? w - px - 1 : px), srcRect.top + i * 256 / zoom);
byte p = *src;
if (p != 0xff)
dst[j] = p;
}
dst += surface->pitch;
}
}
return dstRect;
}
} // End of namespace TeenAgent

View 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/>.
*
*/
#ifndef TEENAGENT_SURFACE_H
#define TEENAGENT_SURFACE_H
#include "common/rect.h"
#include "graphics/surface.h"
namespace Common {
class SeekableReadStream;
}
namespace TeenAgent {
class Surface : public Graphics::Surface {
public:
Surface();
~Surface();
enum Type {kTypeOns, kTypeLan};
void load(Common::SeekableReadStream &, Type type);
Common::Rect render(Graphics::Surface *surface, int dx = 0, int dy = 0, bool mirror = false, Common::Rect srcRect = Common::Rect(), uint zoom = 256) const;
bool empty() const { return pixels == NULL; }
uint16 x, y;
};
} // End of namespace TeenAgent
#endif

View File

@@ -0,0 +1,78 @@
/* 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 "teenagent/surface_list.h"
#include "teenagent/surface.h"
#include "teenagent/objects.h"
#include "teenagent/teenagent.h"
namespace TeenAgent {
SurfaceList::SurfaceList() : surfaces(NULL), surfacesCount(0) {}
SurfaceList::~SurfaceList() {
free();
}
void SurfaceList::load(Common::SeekableReadStream &stream, int subHack) {
free();
byte fn = stream.readByte();
if (stream.eos())
return;
surfacesCount = fn - subHack;
debugC(0, kDebugSurface, "loading %u surfaces from list (skip %d)", surfacesCount, subHack);
if (surfacesCount == 0)
return;
surfaces = new Surface[surfacesCount];
for (byte i = 0; i < surfacesCount; ++i) {
uint offset = stream.readUint16LE();
uint pos = stream.pos();
stream.seek(offset);
surfaces[i].load(stream, Surface::kTypeOns);
stream.seek(pos);
}
}
void SurfaceList::free() {
delete[] surfaces;
surfaces = NULL;
surfacesCount = 0;
}
void SurfaceList::render(Graphics::Surface *surface, const Common::Rect &clip) const {
for (uint i = 0; i < surfacesCount; ++i) {
const Surface &s = surfaces[i];
Common::Rect r(s.x, s.y, s.x + s.w, s.y + s.h);
if (r.bottom < clip.bottom || !clip.intersects(r))
continue;
r.clip(clip);
r.translate(-s.x, -s.y);
s.render(surface, r.left, r.top, false, r);
}
}
}

View 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/>.
*
*/
#ifndef TEENAGENT_SURFACE_LIST_H__
#define TEENAGENT_SURFACE_LIST_H__
#include "common/stream.h"
#include "graphics/surface.h"
namespace TeenAgent {
class Surface;
class SurfaceList {
public:
SurfaceList();
~SurfaceList();
void load(Common::SeekableReadStream &, int subHack = 0);
void free();
void render(Graphics::Surface *surface, const Common::Rect &clip) const;
protected:
Surface *surfaces;
uint surfacesCount;
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,238 @@
/* 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 TEENAGENT_TEENAGENT_H
#define TEENAGENT_TEENAGENT_H
#include "engines/engine.h"
#include "audio/mixer.h"
#include "common/random.h"
#include "common/array.h"
#include "gui/debugger.h"
#include "teenagent/console.h"
#include "teenagent/dialog.h"
struct ADGameDescription;
namespace Audio {
class AudioStream;
}
namespace Common {
struct Point;
}
/**
* This is the namespace of the TeenAgent engine.
*
* Status of this engine: Complete
*
* Games using this engine:
* - Teen Agent
*/
namespace TeenAgent {
struct Object;
struct UseHotspot;
class Scene;
class MusicPlayer;
class Resources;
class Inventory;
class Pack;
#define TEENAGENT_DAT_VERSION 6
#define TEENAGENT_SAVEGAME_VERSION 1
// Engine Debug Flags
enum {
kDebugActor = 1,
kDebugAnimation,
kDebugCallbacks,
kDebugDialog,
kDebugFont,
kDebugInventory,
kDebugMusic,
kDebugObject,
kDebugPack,
kDebugScene,
kDebugSurface,
};
enum TEENAGENTActions {
kActionSkipIntro,
kActionSkipDialog,
kActionCloseInventory,
kActionToggleInventory,
kActionFastMode,
};
const uint16 kScreenWidth = 320;
const uint16 kScreenHeight = 200;
class TeenAgentEngine : public Engine {
public:
TeenAgentEngine(OSystem *system, const ADGameDescription *gd);
~TeenAgentEngine();
Common::Error run() override;
Common::String getSaveStateName(int slot) const override;
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return true; }
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return !_sceneBusy; }
bool hasFeature(EngineFeature f) const override;
void init();
enum Action { kActionNone, kActionExamine, kActionUse };
void examine(const Common::Point &point, Object *object);
void use(Object *object);
inline void cancel() { _action = kActionNone; }
bool processCallback(uint16 addr);
inline Scene *getScene() { return scene; }
bool showLogo();
bool showCDLogo();
bool showMetropolis();
int skipEvents() const;
Common::String parseMessage(uint32 addr);
//event driven:
void displayMessage(uint32 addr, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
void displayMessage(const Common::String &str, uint16 voiceIndex, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
void displayAsyncMessage(uint32 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID = kMark);
void displayAsyncMessageInSlot(uint32 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark);
void displayCredits(uint32 addr, uint16 timer = 0);
void displayCutsceneMessage(uint32 addr, uint16 x, uint16 y);
void moveTo(const Common::Point &dst, byte o, bool warp = false);
void moveTo(uint16 x, uint16 y, byte o, bool warp = false);
void moveTo(Object *obj);
void moveRel(int16 x, int16 y, byte o, bool warp = false);
void playActorAnimation(uint16 id, bool async = false, bool ignore = false);
void playAnimation(uint16 id, byte slot, bool async = false, bool ignore = false, bool loop = false);
void loadScene(byte id, const Common::Point &pos, byte o = 0);
void loadScene(byte id, uint16 x, uint16 y, byte o = 0);
void enableOn(bool enable = true);
void setOns(byte id, byte value, byte sceneId = 0);
void setLan(byte id, byte value, byte sceneId = 0);
void setFlag(uint16 addr, byte value);
byte getFlag(uint16 addr);
void reloadLan();
void rejectMessage();
void bookColorMessage();
void playMusic(byte id); //schedules play
void playSound(byte id, byte skipFrames);
void playSoundNow(Pack *pack, uint32 id);
void playVoiceNow(Pack *pack, uint32 id);
void stopVoice();
void enableObject(byte id, byte sceneId = 0);
void disableObject(byte id, byte sceneId = 0);
void hideActor();
void showActor();
void waitAnimation();
void waitLanAnimationFrame(byte slot, uint16 frame);
void setTimerCallback(uint16 addr, uint16 frames);
void shakeScreen();
void displayCredits();
void fadeIn();
void fadeOut();
void wait(uint16 frames);
Common::RandomSource _rnd;
Resources *res;
Scene *scene;
Inventory *inventory;
MusicPlayer *music;
Dialog *dialog;
void setMusic(byte id);
void sayText(const Common::String &text, bool isSubtitle = false);
void stopTextToSpeech();
void setTTSVoice(CharacterID characterID) const;
#ifdef USE_TTS
Common::U32String convertText(const Common::String &text) const;
#endif
Common::String _previousSaid;
uint16 _previousVoiceId;
private:
void processObject();
bool trySelectedObject();
bool _sceneBusy;
Action _action;
Object *_dstObject;
Audio::AudioStream *_musicStream;
Audio::SoundHandle _musicHandle, _soundHandle, _voiceHandle;
const ADGameDescription *_gameDescription;
uint _markDelay, _gameDelay;
Common::Array<Common::Array<UseHotspot> > _useHotspots;
void fnIntro();
void fnPoleClimbFail();
void fnGotAnchor();
void fnGetOutOfLake();
void fnGuardDrinking();
void fnEgoDefaultPosition();
void fnEnterCave();
void fnEgoScaredBySpider();
void fnMoveToLadderAndLeaveCellar();
void fnLeaveCellar();
void fnPutRockInHole();
void fnEgoBottomRightTurn();
bool fnCheckingDrawers();
void fnDrawerOpenMessage();
bool fnRobotSafeAlreadyUnlockedCheck();
void fnRobotSafeUnlockCheck();
bool fnMansionIntrusionAttempt();
void fnSecondMansionIntrusion();
void fnThirdMansionIntrusion();
void fnFourthMansionIntrusion();
void fnFifthMansionIntrusion();
void fnSixthMansionIntrusion();
void fnTooDark();
bool fnIsCookGone();
void fnEgoSuspiciousPosition();
void fnGivingFlowerToOldLady();
void fnGiveAnotherFlowerToOldLady();
void fnGivingFlowerToAnne();
void fnGiveAnotherFlowerToAnne();
};
extern TeenAgentEngine *g_engine;
} // End of namespace TeenAgent
#endif