scummvm/engines/cge2/cge2_main.cpp

752 lines
16 KiB
C++
Raw Normal View History

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This code is based on original Sfinx source code
* Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon
*/
#include "sound.h"
#include "cge2/cge2_main.h"
#include "cge2/cge2.h"
#include "cge2/vga13h.h"
#include "cge2/text.h"
#include "cge2/snail.h"
#include "cge2/hero.h"
#include "cge2/spare.h"
#include "cge2/events.h"
namespace CGE2 {
2014-05-26 07:37:52 +00:00
System::System(CGE2Engine *vm) : Sprite(vm), _vm(vm) {
warning("STUB: System::System()");
}
void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) {
warning("STUB: System::touch()");
}
void System::tick() {
warning("STUB: System::tick()");
}
int CGE2Engine::number(char *s) { // TODO: Rework it later to include the preceding token() call!
int r = atoi(s);
char *pp = strchr(s, ':');
if (pp)
r = (r << 8) + atoi(pp + 1);
return r;
}
char *CGE2Engine::token(char *s) {
return strtok(s, " =\t,;/()");
}
char *CGE2Engine::tail(char *s) {
if (s && (*s == '='))
s++;
return s;
}
int CGE2Engine::takeEnum(const char **tab, const char *text) {
if (text) {
for (const char **e = tab; *e; e++) {
if (scumm_stricmp(text, *e) == 0) {
return e - tab;
}
}
}
return -1;
}
ID CGE2Engine::ident(const char *s) {
return ID(takeEnum(EncryptedStream::kIdTab, s));
}
bool CGE2Engine::testBool(char *s) {
return number(s) != 0;
}
void CGE2Engine::badLab(const char *fn) {
error("Misplaced label in %s!", fn);
}
void CGE2Engine::loadSprite(const char *fname, int ref, int scene, V3D &pos) {
int shpcnt = 0;
int seqcnt = 0;
int cnt[kActions];
for (int i = 0; i < kActions; i++)
cnt[i] = 0;
ID section = kIdPhase;
bool frnt = true;
bool east = false;
bool port = false;
bool tran = false;
Hero *h;
ID id;
char tmpStr[kLineMax + 1];
mergeExt(tmpStr, fname, kSprExt);
if (_resman->exist(tmpStr)) { // sprite description file exist
EncryptedStream sprf(this, tmpStr);
if (sprf.err())
error("Bad SPR [%s]", tmpStr);
int label = kNoByte;
Common::String line;
for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()){
if (line.size() == 0)
continue;
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
char *p;
p = token(tmpStr);
if (*p == '@') {
if (label != kNoByte)
badLab(fname);
label = atoi(p + 1);
continue;
}
id = ident(p);
switch (id) {
case kIdName: // will be taken in Expand routine
if (label != kNoByte)
badLab(fname);
break;
case kIdType:
if (label != kNoByte)
badLab(fname);
break;
case kIdNear:
case kIdMTake:
case kIdFTake:
case kIdPhase:
case kIdSeq:
if (label != kNoByte)
badLab(fname);
section = id;
break;
case kIdFront:
if (label != kNoByte)
badLab(fname);
p = token(nullptr);
frnt = testBool(p);
break;
case kIdEast:
if (label != kNoByte)
badLab(fname);
p = token(nullptr);
east = testBool(p);
break;
case kIdPortable:
if (label != kNoByte)
badLab(fname);
p = token(nullptr);
port = testBool(p);
break;
case kIdTransparent:
if (label != kNoByte)
badLab(fname);
p = token(nullptr);
tran = testBool(p);
break;
default:
if (id >= kIdNear)
break;
switch (section) {
case kIdNear:
case kIdMTake:
case kIdFTake:
if (_commandHandler->com(p) >= 0)
++cnt[section];
else
error("Bad line %d [%s]", sprf.getLineCount(), tmpStr);
break;
case kIdPhase:
if (label != kNoByte)
badLab(fname);
++shpcnt;
break;
case kIdSeq:
if (label != kNoByte)
badLab(fname);
++seqcnt;
break;
}
break;
}
label = kNoByte;
}
if (!shpcnt) {
error("No shapes - %s", fname);
}
} else // No sprite description: mono-shaped sprite with only .BMP file.
++shpcnt;
// Make sprite of choosen type:
char c = *fname | 0x20;
if (c >= 'a' && c <= 'z' && fname[1] == '0' && fname[2] == '\0') {
h = new Hero(this);
if (h) {
h->gotoxyz(pos);
_sprite = h;
}
} else {
if (_sprite)
delete _sprite;
_sprite = new Sprite(this);
if (_sprite)
_sprite->gotoxyz(pos);
}
if (_sprite) {
_sprite->_ref = ref;
2014-05-19 15:56:19 +00:00
_sprite->_scene = scene;
_sprite->_flags._frnt = frnt;
_sprite->_flags._east = east;
_sprite->_flags._port = port;
_sprite->_flags._tran = tran;
_sprite->_flags._kill = true;
// Extract the filename, without the extension
Common::strlcpy(_sprite->_file, fname, sizeof(_sprite->_file));
char *p = strchr(_sprite->_file, '.');
if (p)
*p = '\0';
_sprite->_shpCnt = shpcnt;
_sprite->_seqCnt = seqcnt;
for (int i = 0; i < kActions; i++)
_sprite->_actionCtrl[i]._cnt = cnt[i];
}
}
void CGE2Engine::loadScript(const char *fname) {
EncryptedStream scrf(this, fname);
if (scrf.err())
return;
bool ok = true;
int lcnt = 0;
char tmpStr[kLineMax + 1];
Common::String line;
for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) {
if (line.size() == 0)
continue;
char *p;
lcnt++;
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
ok = false; // not OK if break
V3D P;
// sprite ident number
if ((p = token(tmpStr)) == NULL)
break;
int SpI = number(p);
// sprite file name
char *SpN;
if ((SpN = token(nullptr)) == NULL)
break;
// sprite scene
if ((p = token(nullptr)) == NULL)
break;
int SpA = number(p);
// sprite column
if ((p = token(nullptr)) == NULL)
break;
P._x = number(p);
// sprite row
if ((p = token(nullptr)) == NULL)
break;
P._y = number(p);
// sprite Z pos
if ((p = token(nullptr)) == NULL)
break;
P._z = number(p);
// sprite life
if ((p = token(nullptr)) == NULL)
break;
bool BkG = number(p) == 0;
ok = true; // no break: OK
_sprite = NULL;
loadSprite(SpN, SpI, SpA, P);
if (_sprite) {
if (BkG)
_sprite->_flags._back = true;
int n = _spare->count();
if (_spare->locate(_sprite->_ref) == nullptr)
_spare->store(_sprite);
_sprite = nullptr;
if (_spare->count() == n)
error("Durplicated reference! %s", SpN);
}
}
if (!ok)
error("Bad INI line %d [%s]", scrf.getLineCount(), fname);
}
void CGE2Engine::movie(const char *ext) {
assert(ext);
if (_quitFlag)
return;
char fn[12];
sprintf(fn, "CGE.%s", (*ext == '.') ? ext + 1 : ext);
if (_resman->exist(fn)) {
int now = _now;
2014-05-13 07:27:50 +00:00
_now = atoi(ext + 2);
loadScript(fn);
2014-05-13 07:27:50 +00:00
caveUp(_now);
while (!_commandHandler->idle() && !_quitFlag)
mainLoop();
warning("STUB: CGE2Engine::movie()");
_commandHandler->addCommand(kCmdClear, -1, 0, nullptr);
_commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr);
_vga->_showQ->clear();
_spare->clear();
_now = now;
}
}
2014-05-13 07:27:50 +00:00
void CGE2Engine::caveUp(int cav) {
_now = cav;
int bakRef = _now << 8;
if (_music)
_midiPlayer->loadMidi(bakRef);
showBak(bakRef);
_eye = _eyeTab[_now];
_mouseTop = V2D(this, V3D(0, 1, kScrDepth)).y;
2014-05-19 15:56:19 +00:00
_spare->takeCave(_now);
openPocket();
warning("STUB: CGE2Engine::caveUp()");
// TODO: Implement "Hero" things here!
_sound->stop();
_fx->clear();
selectPocket(-1);
_infoLine->setText(nullptr);
busy(false);
if (!_dark)
_vga->sunset();
_vga->show();
_vga->copyPage(1, 0);
_vga->show();
2014-05-13 07:27:50 +00:00
2014-05-19 16:02:30 +00:00
_sprite = _vga->_showQ->first();
2014-05-29 16:19:01 +00:00
_vga->sunrise(_vga->_sysPal);
2014-05-19 21:24:23 +00:00
feedSnail(_vga->_showQ->locate(bakRef + 255), kNear, _heroTab[_sex]->_ptr);
//setDrawColors(); - It's only for debugging purposes. Can be left out for now.
2014-05-13 07:27:50 +00:00
}
void CGE2Engine::switchCave(int cav) {
warning("STUB: CGE2Engine::switchCave()");
}
2014-05-13 07:27:50 +00:00
void CGE2Engine::showBak(int ref) {
Sprite *spr = _spare->locate(ref);
if (spr != nullptr) {
_bitmapPalette = _vga->_sysPal;
spr->expand();
_bitmapPalette = NULL;
2014-05-15 13:03:59 +00:00
spr->show(2);
2014-05-13 07:27:50 +00:00
_vga->copyPage(1, 2);
_spare->dispose(spr);
}
}
void CGE2Engine::mainLoop() {
_vga->show();
_commandHandlerTurbo->runCommand();
_commandHandler->runCommand();
// Handle a delay between game frames
handleFrame();
// Handle any pending events
//_eventManager->poll();
warning("STUB: CGE2Engine::mainLoop() - Event handling is missing!");
// Check shouldQuit()
_quitFlag = shouldQuit();
}
void CGE2Engine::handleFrame() {
// Game frame delay
uint32 millis = g_system->getMillis();
while (!_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) {
// Handle any pending events
//_eventManager->poll();
warning("STUB: CGE2Engine::handleFrame() - Event handling is missing!");
if (millis >= (_lastTick + kGameTickDelay)) {
// Dispatch the tick to any active objects
tick();
_lastTick = millis;
}
// Slight delay
g_system->delayMillis(5);
millis = g_system->getMillis();
}
_lastFrame = millis;
if (millis >= (_lastTick + kGameTickDelay)) {
// Dispatch the tick to any active objects
tick();
_lastTick = millis;
}
}
Sprite *CGE2Engine::locate(int ref) {
_taken = false;
Sprite *spr = _vga->_showQ->locate(ref);
if (!spr) {
spr = _spare->locate(ref);
if (spr)
_taken = true;
}
return spr;
}
2014-05-24 07:04:41 +00:00
bool CGE2Engine::isHero(Sprite *spr) {
return spr && spr->_ref / 10 == 14;
}
void CGE2Engine::tick() {
for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) {
if (spr->_time) {
2014-05-24 06:59:06 +00:00
if (--spr->_time == 0)
spr->tick();
}
if (_waitRef) {
if (_waitRef == _sprite->_ref)
if (spr->seqTest(_waitSeq))
_waitRef = 0;
}
}
2014-05-24 06:59:06 +00:00
//Mouse->Tick();
warning("STUB: CGE2Engine::tick() - Mouse");
}
void CGE2Engine::loadMap(int cav) {
warning("STUB: CGE2Engine::loadMap()");
}
void CGE2Engine::openPocket() {
2014-05-19 14:08:24 +00:00
warning("STUB: CGE2Engine::openPocket()");
}
void CGE2Engine::selectPocket(int n) {
warning("STUB: CGE2Engine::selectPocket()");
}
void CGE2Engine::busy(bool on) {
warning("STUB: CGE2Engine::selectPocket()");
}
void CGE2Engine::runGame() {
2014-05-26 08:33:47 +00:00
if (_quitFlag)
return;
selectPocket(-1);
loadUser();
_commandHandlerTurbo->addCommand(kCmdSeq, kMusicRef, _music, nullptr);
if (!_music)
_midiPlayer->killMidi();
checkSaySwitch();
_infoLine->gotoxyz(V3D(kInfoX, kInfoY, 0));
_infoLine->setText(nullptr);
//_vga->_showQ->insert(_infoLine);
warning("STUB: CGE2Engine::runGame() - Info Line is missing!");
2014-05-26 08:33:47 +00:00
caveUp(_now);
_startupMode = 0;
_mouse->center();
_mouse->off();
_mouse->on();
_keyboard->setClient(_sys);
_commandHandler->addCommand(kCmdSeq, kPowerRef, 1, nullptr);
_busyPtr = _vga->_showQ->locate(kBusyRef);
_vol[0] = _vga->_showQ->locate(kDvolRef);
_vol[1] = _vga->_showQ->locate(kMvolRef);
// these sprites are loaded with SeqPtr==0 (why?!)
if (_vol[0])
_vol[0]->step((/*(int)SNDDrvInfo.VOL4.DL * */ _vol[0]->_seqCnt + _vol[0]->_seqCnt / 2) >> 4);
if (_vol[1])
_vol[1]->step((/*(int)SNDDrvInfo.VOL4.ML * */ _vol[1]->_seqCnt + _vol[1]->_seqCnt / 2) >> 4);
// TODO: Recheck these! ^
// main loop
while (!_endGame && !_quitFlag) {
if (_flag[3]) // Flag FINIS
_commandHandler->addCallback(kCmdExec, -1, 0, kQGame);
mainLoop();
}
// If finishing game due to closing ScummVM window, explicitly save the game
if (!_endGame && canSaveGameStateCurrently())
qGame();
_keyboard->setClient(nullptr);
_commandHandler->addCommand(kCmdClear, -1, 0, nullptr);
_commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr);
_mouse->off();
_vga->_showQ->clear();
_vga->_spareQ->clear();
}
void CGE2Engine::loadUser() {
2014-05-26 14:09:49 +00:00
warning("STUB: CGE2Engine::loadUser()");
// Missing loading from file. TODO: Implement it with the saving/loading!
loadScript("CGE.INI");
loadPos();
}
void CGE2Engine::loadPos() {
if (_resman->exist("CGE.HXY")) {
for (int cav = 0; cav < kCaveMax; cav++)
_heroTab[1]->_posTab[cav] = new V2D(this, 180, 10);
EncryptedStream file(this, "CGE.HXY");
for (int cav = 0; cav < kCaveMax; cav++) {
_heroTab[0]->_posTab[cav]->x = file.readSint16LE();
_heroTab[0]->_posTab[cav]->y = file.readSint16LE();
}
for (int cav = 0; cav < 41; cav++) { // (564 - 400) / 4 = 41
_heroTab[1]->_posTab[cav]->x = file.readSint16LE();
_heroTab[1]->_posTab[cav]->y = file.readSint16LE();
}
} else
error("Missing file: CGE.HXY");
2014-05-26 08:33:47 +00:00
}
void CGE2Engine::checkSaySwitch() {
warning("STUB: CGE2Engine::checkSaySwitch()");
}
void CGE2Engine::qGame() {
warning("STUB: CGE2Engine::qGame()");
_endGame = true;
}
void CGE2Engine::loadTab() {
setEye(_text->getText(240));
for (int i = 0; i < kCaveMax; i++)
_eyeTab[i] == _eye;
2014-05-15 23:04:01 +00:00
if (_resman->exist(kTabName)) {
EncryptedStream f(this, kTabName);
for (int i = 0; i < kCaveMax; i++) {
for (int j = 0; j < 3; j++) {
signed b = f.readSint16BE();
unsigned a = f.readUint16BE();
2014-05-15 23:04:01 +00:00
uint16 round = uint16((long(a) << 16) / 100);
if (round > 0x7FFF)
b++;
switch (j) {
case 0:
_eyeTab[i]->_x = b;
break;
case 1:
_eyeTab[i]->_y = b;
break;
case 2:
_eyeTab[i]->_z = b;
break;
}
}
}
}
warning("STUB: CGE2Engine::loadTab() - Recheck this");
}
void CGE2Engine::cge2_main() {
warning("STUB: CGE2Engine::cge2_main()");
loadTab();
_mode++;
if (showTitle("WELCOME")) {
#if 0
if (_mode == 1)
movie(kIntroExt);
#endif
if (_text->getText(255) != NULL) {
runGame();
_startupMode = 2;
} else
_vga->sunset();
} else
_vga->sunset();
}
char *CGE2Engine::mergeExt(char *buf, const char *name, const char *ext) {
strcpy(buf, name);
char *dot = strrchr(buf, '.');
if (!dot)
strcat(buf, ext);
return buf;
}
2014-05-15 08:58:59 +00:00
void CGE2Engine::setEye(V3D &e) {
_eye = &e;
}
void CGE2Engine::setEye(const V2D& e2, int z) {
_eye->_x = e2.x;
_eye->_y = e2.y;
_eye->_z = z;
}
void CGE2Engine::setEye(const char *s) {
char tempStr[kLineMax];
strcpy(tempStr, s);
_eye->_x = atoi(token(tempStr));
_eye->_y = atoi(token(NULL));
_eye->_z = atoi(token(NULL));
}
2014-05-15 10:21:24 +00:00
int CGE2Engine::newRandom(int range) {
if (!range)
return 0;
return _randomSource.getRandomNumber(range - 1);
}
bool CGE2Engine::showTitle(const char *name) {
if (_quitFlag)
return false;
_bitmapPalette = _vga->_sysPal;
BitmapPtr *LB = new BitmapPtr[2];
LB[0] = new Bitmap(this, name);
LB[1] = NULL;
_bitmapPalette = NULL;
Sprite D(this, LB, 1);
D._flags._kill = true;
warning("STUB: Sprite::showTitle() - Flags changed compared to CGE1's Sprite type.");
D.gotoxyz(kScrWidth >> 1, -(kPanHeight >> 1));
_vga->sunset();
D.show(2);
_vga->copyPage(1, 2);
_vga->copyPage(0, 1);
_vga->sunrise(_vga->_sysPal);
_vga->update();
warning("STUB: CGE2Engine::showTitle()");
return true;
}
2014-05-19 21:24:23 +00:00
int CGE2Engine::freePockets(int sx) {
int n = 0;
for (int i = 0; i < kPocketMax; i++){
if (_heroTab[sx]->_pocket[i] == nullptr)
++n;
}
return n;
}
int CGE2Engine::findActivePocket(int ref) {
for (int i = 0; i < kPocketMax; i++) {
Sprite *spr = _heroTab[_sex]->_pocket[i];
if (ref >= 0) {
if (spr && spr->_ref == ref)
return i;
} else if (!spr)
return i;
}
return -1;
}
void CGE2Engine::pocFul() {
Hero *h = _heroTab[_sex]->_ptr;
h->park();
_commandHandler->addCommand(kCmdWait, -1, -1, h);
_commandHandler->addCommand(kCmdSound, -1, 2, h);
_commandHandler->addCommand(kCmdSay, -1, kPocketFull + _sex, h);
}
void CGE2Engine::killText() {
if (!_talk)
return;
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk);
_talk = NULL;
}
} // End of namespace CGE2