mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
433 lines
13 KiB
C++
433 lines
13 KiB
C++
/* 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 "graphics/cursorman.h"
|
|
|
|
#include "dgds/scene_op.h"
|
|
#include "dgds/dgds.h"
|
|
#include "dgds/debug_util.h"
|
|
#include "dgds/globals.h"
|
|
#include "dgds/inventory.h"
|
|
#include "dgds/scene.h"
|
|
#include "dgds/dragon_native.h"
|
|
#include "dgds/hoc_intro.h"
|
|
#include "dgds/minigames/dragon_arcade.h"
|
|
#include "dgds/minigames/china_train.h"
|
|
#include "dgds/minigames/china_tank.h"
|
|
|
|
namespace Dgds {
|
|
|
|
static Common::String _sceneOpCodeName(SceneOpCode code) {
|
|
code = static_cast<SceneOpCode>(code & ~kSceneOpHasConditionalOpsFlag);
|
|
switch (code) {
|
|
case kSceneOpNone: return "none";
|
|
case kSceneOpChangeScene: return "changeScene";
|
|
case kSceneOpNoop: return "noop";
|
|
case kSceneOpGlobal: return "global";
|
|
case kSceneOpSegmentStateOps: return "sceneOpSegmentStateOps";
|
|
case kSceneOpSetItemAttr: return "setItemAttr";
|
|
case kSceneOpSetDragItem: return "setDragItem";
|
|
case kSceneOpOpenInventory: return "openInventory";
|
|
case kSceneOpShowDlg: return "showdlg";
|
|
case kSceneOpShowInvButton: return "showInvButton";
|
|
case kSceneOpHideInvButton: return "hideInvButton";
|
|
case kSceneOpEnableTrigger: return "enabletrigger";
|
|
case kSceneOpChangeSceneToStored: return "changeSceneToStored";
|
|
case kSceneOpAddFlagToDragItem: return "addFlagToDragItem";
|
|
case kSceneOpMoveItemsBetweenScenes: return "moveItemsBetweenScenes";
|
|
case kSceneOpOpenInventoryZoom: return "openInventoryZoom";
|
|
case kSceneOpShowClock: return "showClock";
|
|
case kSceneOpHideClock: return "hideClock";
|
|
case kSceneOpShowMouse: return "showMouse";
|
|
case kSceneOpHideMouse: return "hideMouse";
|
|
case kSceneOpLoadTalkDataAndSetFlags: return "loadTalkDataAndSetFlags";
|
|
case kSceneOpClearVisibleTalkHeads: return "clearVisibleTalksHeads";
|
|
case kSceneOpLoadTalkData: return "loadTalkData";
|
|
case kSceneOpLoadDDSData: return "loadDDSData";
|
|
case kSceneOpFreeDDSData: return "freeDDSData";
|
|
case kSceneOpFreeTalkData: return "freeTalkData";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (DgdsEngine::getInstance()->getGameId() == GID_DRAGON) {
|
|
switch (code) {
|
|
case kSceneOpPasscode: return "passcode";
|
|
case kSceneOpMeanwhile: return "meanwhile";
|
|
case kSceneOpOpenGameOverMenu: return "openGameOverMenu";
|
|
case kSceneOpTiredDialog: return "openTiredDialog";
|
|
case kSceneOpArcadeTick: return "sceneOpArcadeTick";
|
|
case kSceneOpDrawDragonCountdown1: return "drawDragonCountdown1";
|
|
case kSceneOpDrawDragonCountdown2: return "drawDragonCountdown2";
|
|
case kSceneOpOpenPlaySkipIntroMenu: return "openPlaySkipIntroMovie";
|
|
case kSceneOpOpenBetterSaveGameMenu: return "openBetterSaveGameMenu";
|
|
default:
|
|
break;
|
|
}
|
|
} else if (DgdsEngine::getInstance()->getGameId() == GID_HOC) {
|
|
switch (code) {
|
|
case kSceneOpChinaTankInit: return "tankInit";
|
|
case kSceneOpChinaTankEnd: return "tankEnd";
|
|
case kSceneOpChinaTankTick: return "tankTick";
|
|
case kSceneOpChinaScrollLeft: return "scrollLeft";
|
|
case kSceneOpChinaScrollRight: return "scrollRight";
|
|
case kSceneOpShellGameInit: return "shellGameInit";
|
|
case kSceneOpShellGameEnd: return "shellGameEnd";
|
|
case kSceneOpShellGameTick: return "shellGameTick";
|
|
case kSceneOpChinaTrainInit: return "trainInit";
|
|
case kSceneOpChinaTrainEnd: return "trainEnd";
|
|
case kSceneOpChinaTrainTick: return "trainTick";
|
|
case kSceneOpChinaOpenGameOverMenu: return "gameOverMenu";
|
|
case kSceneOpChinaOpenSkipCreditsMenu: return "skipCreditsMenu";
|
|
case kSceneOpChinaOnIntroInit: return "chinaOnIntroInit";
|
|
case kSceneOpChinaOnIntroTick: return "chinaOnIntroTick";
|
|
case kSceneOpChinaOnIntroEnd: return "chinaOnIntroEnd";
|
|
default:
|
|
break;
|
|
}
|
|
} else if (DgdsEngine::getInstance()->getGameId() == GID_WILLY) {
|
|
switch (code) {
|
|
case kSceneOpOpenBeamishGameOverMenu: return "openGameOverMenu";
|
|
case kSceneOpOpenBeamishOpenSkipCreditsMenu: return "skipCreditsMenu";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Common::String::format("sceneOp%d", (int)code);
|
|
}
|
|
|
|
|
|
Common::String SceneOp::dump(const Common::String &indent) const {
|
|
Common::String argsStr;
|
|
if (_args.empty()) {
|
|
argsStr = "[]";
|
|
} else {
|
|
argsStr = "[";
|
|
for (uint i : _args)
|
|
argsStr += Common::String::format("%d ", i);
|
|
argsStr.setChar(']', argsStr.size() - 1);
|
|
}
|
|
|
|
const Common::String opName = _sceneOpCodeName(_opCode);
|
|
const char *isConditional = (_opCode & kSceneOpHasConditionalOpsFlag) ? "(cond)": "";
|
|
Common::String str = Common::String::format("%sSceneOp<op: %s%s args: %s", indent.c_str(), opName.c_str(), isConditional, argsStr.c_str());
|
|
|
|
str += DebugUtil::dumpStructList(indent, "conditionList", _conditionList);
|
|
if (!_conditionList.empty()) {
|
|
str += "\n";
|
|
str += indent;
|
|
}
|
|
str += ">";
|
|
return str;
|
|
}
|
|
|
|
bool SceneOp::runOp() const {
|
|
bool sceneChanged;
|
|
|
|
if (_opCode < 100) {
|
|
sceneChanged = runCommonOp();
|
|
} else {
|
|
// Game-specific opcode
|
|
switch (DgdsEngine::getInstance()->getGameId()) {
|
|
case GID_DRAGON:
|
|
sceneChanged = runDragonOp();
|
|
break;
|
|
case GID_HOC:
|
|
sceneChanged = runChinaOp();
|
|
break;
|
|
case GID_WILLY:
|
|
sceneChanged = runBeamishOp();
|
|
break;
|
|
default:
|
|
error("TODO: Implement game-specific scene op for this game");
|
|
}
|
|
}
|
|
return sceneChanged;
|
|
}
|
|
|
|
bool SceneOp::runCommonOp() const {
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
switch (_opCode) {
|
|
case kSceneOpChangeScene:
|
|
if (engine->changeScene(_args[0]))
|
|
return true;
|
|
break;
|
|
case kSceneOpNoop:
|
|
//
|
|
// This is run when the "quit willy beamish help" dialog item is called.
|
|
// There may be a nicer way to do this?
|
|
//
|
|
if (engine->getScene()->getNum() == 80 && engine->getGameId() == GID_WILLY) {
|
|
int16 sceneNo = engine->getGameGlobals()->getGlobal(0x61);
|
|
if (engine->changeScene(sceneNo))
|
|
return true;
|
|
}
|
|
break;
|
|
case kSceneOpGlobal:
|
|
// The globals are held by the GDS scene
|
|
engine->getGDSScene()->globalOps(_args);
|
|
break;
|
|
case kSceneOpSegmentStateOps:
|
|
SDSScene::segmentStateOps(_args);
|
|
break;
|
|
case kSceneOpSetItemAttr:
|
|
SDSScene::setItemAttrOp(_args);
|
|
break;
|
|
case kSceneOpSetDragItem:
|
|
SDSScene::setDragItemOp(_args);
|
|
break;
|
|
case kSceneOpOpenInventory:
|
|
engine->getInventory()->open();
|
|
// This implicitly changes scene num
|
|
break;
|
|
case kSceneOpShowDlg:
|
|
if (_args.size() == 1)
|
|
engine->getScene()->showDialog(0, _args[0]);
|
|
else if (_args.size() > 1)
|
|
engine->getScene()->showDialog(_args[0], _args[1]);
|
|
break;
|
|
case kSceneOpShowInvButton:
|
|
engine->getScene()->addInvButtonToHotAreaList();
|
|
break;
|
|
case kSceneOpHideInvButton:
|
|
engine->getScene()->removeInvButtonFromHotAreaList();
|
|
break;
|
|
case kSceneOpEnableTrigger:
|
|
if (_args.size() == 1)
|
|
engine->getScene()->enableTrigger(0, _args[0]);
|
|
else if (_args.size() > 1)
|
|
engine->getScene()->enableTrigger(_args[1], _args[0]);
|
|
break;
|
|
case kSceneOpChangeSceneToStored: {
|
|
int16 sceneNo = engine->getGameGlobals()->getGlobal(0x61);
|
|
if (engine->changeScene(sceneNo))
|
|
return true;
|
|
break;
|
|
}
|
|
case kSceneOpAddFlagToDragItem: {
|
|
GameItem *item = engine->getScene()->getDragItem();
|
|
if (item) {
|
|
item->_flags |= kItemStateDragging;
|
|
// TODO: Use hot x/y or just position?
|
|
Common::Point lastMouse = engine->getLastMouseMinusHot();
|
|
item->_rect.x = lastMouse.x;
|
|
item->_rect.y = lastMouse.y;
|
|
}
|
|
break;
|
|
}
|
|
case kSceneOpOpenInventoryZoom:
|
|
engine->getInventory()->setShowZoomBox(true);
|
|
engine->getInventory()->open();
|
|
return true;
|
|
case kSceneOpMoveItemsBetweenScenes: {
|
|
int16 fromScene = engine->getGameGlobals()->getGlobal(0x55);
|
|
int16 toScene = engine->getGameGlobals()->getGlobal(0x54);
|
|
for (auto &item : engine->getGDSScene()->getGameItems()) {
|
|
if (item._inSceneNum == fromScene)
|
|
item._inSceneNum = toScene;
|
|
}
|
|
break;
|
|
}
|
|
case kSceneOpShowClock:
|
|
engine->setShowClock(true);
|
|
break;
|
|
case kSceneOpHideClock:
|
|
engine->setShowClock(false);
|
|
break;
|
|
case kSceneOpShowMouse:
|
|
CursorMan.showMouse(true);
|
|
break;
|
|
case kSceneOpHideMouse:
|
|
CursorMan.showMouse(false);
|
|
break;
|
|
case kSceneOpLoadTalkDataAndSetFlags: // args: tdsnum to load, headnum
|
|
engine->getScene()->loadTalkDataAndSetFlags(_args[0], _args[1]);
|
|
break;
|
|
case kSceneOpClearVisibleTalkHeads: // args: none
|
|
engine->getScene()->clearVisibleTalkers();
|
|
break;
|
|
case kSceneOpLoadTalkData: // args: tds num to load
|
|
engine->getScene()->loadTalkData(_args[0]);
|
|
break;
|
|
case kSceneOpLoadDDSData: // args: dds num to load
|
|
if (_args[0])
|
|
engine->getScene()->loadDialogData(_args[0]);
|
|
break;
|
|
case kSceneOpFreeDDSData: // args: dds num to free
|
|
engine->getScene()->freeDialogData(_args[0]);
|
|
break;
|
|
case kSceneOpFreeTalkData: // args: tds num to free
|
|
engine->getScene()->freeTalkData(_args[0]);
|
|
break;
|
|
|
|
default:
|
|
warning("TODO: Implement generic scene op %d", _opCode);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SceneOp::runDragonOp() const {
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
switch (_opCode) {
|
|
case kSceneOpPasscode:
|
|
DragonNative::updatePasscodeGlobal();
|
|
break;
|
|
case kSceneOpMeanwhile:
|
|
// TODO: Should we draw "meanwhile" like the original? it just gets overwritten with the image anyway.
|
|
// Probably need to do something here to avoid flashing..
|
|
//engine->_compositionBuffer.fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
|
|
break;
|
|
case kSceneOpOpenGameOverMenu:
|
|
engine->setMenuToTrigger(kMenuGameOver);
|
|
break;
|
|
case kSceneOpTiredDialog:
|
|
engine->getInventory()->close();
|
|
engine->getScene()->addAndShowTiredDialog();
|
|
break;
|
|
case kSceneOpArcadeTick:
|
|
// TODO: Add a configuration option to skip arcade sequence?
|
|
// g_system->displayMessageOnOSD(_("Skipping DGDS arcade sequence"));
|
|
// engine->getGameGlobals()->setGlobal(0x21, 6);
|
|
engine->getDragonArcade()->arcadeTick();
|
|
break;
|
|
case kSceneOpDrawDragonCountdown1:
|
|
DragonNative::drawCountdown(FontManager::k4x5Font, 141, 56);
|
|
break;
|
|
case kSceneOpDrawDragonCountdown2:
|
|
DragonNative::drawCountdown(FontManager::k8x8Font, 250, 42);
|
|
break;
|
|
case kSceneOpOpenPlaySkipIntroMenu:
|
|
engine->setMenuToTrigger(kMenuSkipPlayIntro);
|
|
break;
|
|
case kSceneOpOpenBetterSaveGameMenu:
|
|
engine->setMenuToTrigger(kMenuSaveBeforeArcade);
|
|
break;
|
|
default:
|
|
error("Unexpected Dragon scene opcode %d", _opCode);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SceneOp::runChinaOp() const {
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
switch (_opCode) {
|
|
case kSceneOpChinaTankInit:
|
|
engine->getChinaTank()->init();
|
|
break;
|
|
case kSceneOpChinaTankEnd:
|
|
engine->getChinaTank()->end();
|
|
break;
|
|
case kSceneOpChinaTankTick:
|
|
engine->getChinaTank()->tick();
|
|
break;
|
|
case kSceneOpShellGameTick:
|
|
engine->getShellGame()->shellGameTick();
|
|
break;
|
|
case kSceneOpShellGameEnd:
|
|
engine->getShellGame()->shellGameEnd();
|
|
break;
|
|
case kSceneOpChinaTrainInit:
|
|
engine->getChinaTrain()->init();
|
|
break;
|
|
case kSceneOpChinaTrainEnd:
|
|
engine->getChinaTrain()->end();
|
|
break;
|
|
case kSceneOpChinaTrainTick:
|
|
engine->getChinaTrain()->tick();
|
|
break;
|
|
case kSceneOpChinaOpenGameOverMenu:
|
|
engine->setMenuToTrigger(kMenuGameOver);
|
|
break;
|
|
case kSceneOpChinaOpenSkipCreditsMenu:
|
|
engine->setMenuToTrigger(kMenuSkipPlayIntro);
|
|
break;
|
|
case kSceneOpChinaOnIntroInit:
|
|
engine->getHocIntro()->init();
|
|
break;
|
|
case kSceneOpChinaOnIntroTick:
|
|
engine->getHocIntro()->tick();
|
|
break;
|
|
case kSceneOpChinaOnIntroEnd:
|
|
engine->getHocIntro()->end();
|
|
break;
|
|
case kSceneOpChinaScrollIntro:
|
|
case kSceneOpChinaScrollLeft:
|
|
case kSceneOpChinaScrollRight:
|
|
// These map to null functions.
|
|
break;
|
|
default:
|
|
warning("TODO: Implement china-specific scene opcode: (%s)", dump("").c_str());
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SceneOp::runBeamishOp() const {
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
|
|
if (_opCode & kSceneOpHasConditionalOpsFlag) {
|
|
uint16 opcode = _opCode & ~kSceneOpHasConditionalOpsFlag;
|
|
for (const ConditionalSceneOp &cop : engine->getScene()->getConditionalOps()) {
|
|
if (cop._opCode == opcode && SceneConditions::check(cop._conditionList)) {
|
|
if (!Scene::runOps(cop._opList))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
switch (_opCode) {
|
|
case kSceneOpOpenBeamishGameOverMenu:
|
|
engine->setMenuToTrigger(kMenuGameOver);
|
|
break;
|
|
case kSceneOpOpenBeamishOpenSkipCreditsMenu:
|
|
engine->setMenuToTrigger(kMenuSkipPlayIntro);
|
|
break;
|
|
default:
|
|
warning("TODO: Implement beamish-specific scene opcode %d", _opCode);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
Common::String ConditionalSceneOp::dump(const Common::String &indent) const {
|
|
const Common::String opName = _sceneOpCodeName(static_cast<SceneOpCode>(_opCode));
|
|
Common::String str = Common::String::format("%sConditionalSceneOp<op: %s", indent.c_str(), opName.c_str());
|
|
|
|
str += DebugUtil::dumpStructList(indent, "conditionList", _conditionList);
|
|
if (!_conditionList.empty()) {
|
|
str += indent;
|
|
}
|
|
str += DebugUtil::dumpStructList(indent, "opList", _opList);
|
|
if (!_opList.empty()) {
|
|
str += "\n";
|
|
str += indent;
|
|
}
|
|
str += ">";
|
|
return str;
|
|
}
|
|
|
|
} // end namespace Dgds
|