Files
scummvm-cursorfix/engines/wintermute/ad/ad_game.cpp
2026-02-02 04:50:13 +01:00

2655 lines
73 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#include "engines/wintermute/ad/ad_actor.h"
#ifdef ENABLE_WME3D
#include "engines/wintermute/ad/ad_actor_3dx.h"
#endif
#include "engines/wintermute/ad/ad_game.h"
#include "engines/wintermute/ad/ad_entity.h"
#include "engines/wintermute/ad/ad_inventory.h"
#include "engines/wintermute/ad/ad_inventory_box.h"
#include "engines/wintermute/ad/ad_item.h"
#include "engines/wintermute/ad/ad_layer.h"
#include "engines/wintermute/ad/ad_response.h"
#include "engines/wintermute/ad/ad_response_box.h"
#include "engines/wintermute/ad/ad_response_context.h"
#include "engines/wintermute/ad/ad_scene.h"
#include "engines/wintermute/ad/ad_scene_geometry.h"
#include "engines/wintermute/ad/ad_scene_state.h"
#include "engines/wintermute/ad/ad_sentence.h"
#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/base/font/base_font.h"
#include "engines/wintermute/base/base_object.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/base_region.h"
#include "engines/wintermute/base/sound/base_sound.h"
#include "engines/wintermute/base/base_surface_storage.h"
#include "engines/wintermute/base/base_transition_manager.h"
#include "engines/wintermute/base/base_sprite.h"
#include "engines/wintermute/base/base_viewport.h"
#include "engines/wintermute/base/base_access_mgr.h"
#include "engines/wintermute/base/particles/part_emitter.h"
#include "engines/wintermute/base/save_thumb_helper.h"
#include "engines/wintermute/base/gfx/base_renderer.h"
#include "engines/wintermute/base/scriptables/script_engine.h"
#include "engines/wintermute/base/scriptables/script.h"
#include "engines/wintermute/base/scriptables/script_stack.h"
#include "engines/wintermute/base/scriptables/script_value.h"
#include "engines/wintermute/ext/scene_hooks.h"
#include "engines/wintermute/ui/ui_entity.h"
#include "engines/wintermute/ui/ui_window.h"
#include "engines/wintermute/utils/utils.h"
#include "engines/wintermute/video/video_player.h"
#include "engines/wintermute/video/video_theora_player.h"
#include "engines/wintermute/platform_osystem.h"
#include "engines/wintermute/dcgf.h"
#include "common/config-manager.h"
#include "common/str.h"
namespace Wintermute {
IMPLEMENT_PERSISTENT(AdGame, true)
//////////////////////////////////////////////////////////////////////////
AdGame::AdGame(const Common::String &gameId) : BaseGame(gameId) {
_responseBox = nullptr;
_inventoryBox = nullptr;
_scene = new AdScene(_game);
_scene->setName("");
registerObject(_scene);
_prevSceneName = nullptr;
_prevSceneFilename = nullptr;
_scheduledScene = nullptr;
_scheduledFadeIn = false;
_stateEx = GAME_NORMAL;
_selectedItem = nullptr;
_texItemLifeTime = 10000;
_texWalkLifeTime = 10000;
_texStandLifeTime = 10000;
_texTalkLifeTime = 10000;
_talkSkipButton = TALK_SKIP_LEFT;
_videoSkipButton = VIDEO_SKIP_LEFT;
_sceneViewport = nullptr;
_initialScene = true;
_debugStartupScene = nullptr;
_startupScene = nullptr;
_invObject = new AdObject(this);
_inventoryOwner = _invObject;
_tempDisableSaveState = false;
_itemsFile = nullptr;
_smartItemCursor = false;
addSpeechDir("speech");
}
//////////////////////////////////////////////////////////////////////////
AdGame::~AdGame() {
cleanup();
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::cleanup() {
for (int32 i = 0; i < _objects.getSize(); i++) {
unregisterObject(_objects[i]);
_objects[i] = nullptr;
}
_objects.removeAll();
for (int32 i = 0; i < _dlgPendingBranches.getSize(); i++) {
delete[] _dlgPendingBranches[i];
}
_dlgPendingBranches.removeAll();
for (int32 i = 0; i < _speechDirs.getSize(); i++) {
delete[] _speechDirs[i];
}
_speechDirs.removeAll();
unregisterObject(_scene);
_scene = nullptr;
// remove items
for (int32 i = 0; i < _items.getSize(); i++) {
_game->unregisterObject(_items[i]);
}
_items.removeAll();
// clear remaining inventories
SAFE_DELETE(_invObject);
for (int32 i = 0; i < _inventories.getSize(); i++) {
delete _inventories[i];
}
_inventories.removeAll();
if (_responseBox) {
_game->unregisterObject(_responseBox);
_responseBox = nullptr;
}
if (_inventoryBox) {
_game->unregisterObject(_inventoryBox);
_inventoryBox = nullptr;
}
SAFE_DELETE_ARRAY(_prevSceneName);
SAFE_DELETE_ARRAY(_prevSceneFilename);
SAFE_DELETE_ARRAY(_scheduledScene);
SAFE_DELETE_ARRAY(_debugStartupScene);
SAFE_DELETE_ARRAY(_startupScene);
SAFE_DELETE_ARRAY(_itemsFile);
SAFE_DELETE(_sceneViewport);
for (int32 i = 0; i < _sceneStates.getSize(); i++) {
delete _sceneStates[i];
}
_sceneStates.removeAll();
for (int32 i = 0; i < _responsesBranch.getSize(); i++) {
delete _responsesBranch[i];
}
_responsesBranch.removeAll();
for (int32 i = 0; i < _responsesGame.getSize(); i++) {
delete _responsesGame[i];
}
_responsesGame.removeAll();
return BaseGame::cleanup();
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::initLoop() {
if (_scheduledScene && _scheduledScene[0] && _transMgr->isReady()) {
changeScene(_scheduledScene, _scheduledFadeIn);
SAFE_DELETE_ARRAY(_scheduledScene);
_game->_activeObject = nullptr;
}
bool res;
res = BaseGame::initLoop();
if (DID_FAIL(res)) {
return res;
}
if (_scene) {
res = _scene->initLoop();
}
_sentences.removeAll();
return res;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::addObject(AdObject *object) {
_objects.add(object);
return registerObject(object);
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::removeObject(AdObject *object) {
// Below condition code is not present in Lite up to (Feb 8, 2012) (SVN repo)
// Not present in Lite up to (Nov 1, 2015) (Git repo)
// Not present up to 1.9.1 (Jan 1, 2010)
// Seems added into 1.10.1 beta (July 19, 2012)
// or later but before Mar 21, 2013 (import into Git repo)
//
// is it inventory object?
if (_inventoryOwner == object)
_inventoryOwner = nullptr;
// in case the user called Scene.CreateXXX() and Game.DeleteXXX()
if (_scene) {
bool res = _scene->removeObject(object);
if (DID_SUCCEED(res)) {
return res;
}
}
for (int32 i = 0; i < _objects.getSize(); i++) {
if (_objects[i] == object) {
_objects.removeAt(i);
break;
}
}
return unregisterObject(object);
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::changeScene(const char *filename, bool fadeIn) {
if (_scene == nullptr) {
_scene = new AdScene(_game);
registerObject(_scene);
} else {
_game->pluginEvents().applyEvent(WME_EVENT_SCENE_SHUTDOWN, _scene);
_scene->applyEvent("SceneShutdown", true);
setPrevSceneName(_scene->_name);
setPrevSceneFilename(_scene->_filename);
if (!_tempDisableSaveState) {
_scene->saveState();
}
_tempDisableSaveState = false;
}
if (_scene) {
// reset objects
for (int32 i = 0; i < _objects.getSize(); i++) {
_objects[i]->reset();
}
// reset scene properties
_scene->_sFXVolume = 100;
if (_scene->_scProp) {
_scene->_scProp->cleanup();
}
bool ret;
if (_initialScene && _debugMode && _debugStartupScene && _debugStartupScene[0]) {
_initialScene = false;
ret = _scene->loadFile(_debugStartupScene);
} else {
ret = _scene->loadFile(filename);
}
if (DID_SUCCEED(ret)) {
// invalidate references to the original scene
for (int32 i = 0; i < _objects.getSize(); i++) {
_objects[i]->invalidateCurrRegions();
_objects[i]->_stickRegion = nullptr;
}
_scene->loadState();
_game->pluginEvents().applyEvent(WME_EVENT_SCENE_INIT, _scene);
}
if (fadeIn) {
_game->_transMgr->start(TRANSITION_FADE_IN);
}
return ret;
} else {
return STATUS_FAILED;
}
}
//////////////////////////////////////////////////////////////////////////
void AdGame::addSentence(AdSentence *sentence) {
_sentences.add(sentence);
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::displaySentences(bool frozen) {
for (int32 i = 0; i < _sentences.getSize(); i++) {
if (frozen && _sentences[i]->_freezable) {
continue;
} else {
_sentences[i]->display();
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
void AdGame::finishSentences() {
for (int32 i = 0; i < _sentences.getSize(); i++) {
if (_sentences[i]->canSkip()) {
_sentences[i]->_duration = 0;
if (_sentences[i]->_sound) {
_sentences[i]->_sound->stop();
}
}
}
_game->_accessMgr->stop();
}
//////////////////////////////////////////////////////////////////////////
// high level scripting interface
//////////////////////////////////////////////////////////////////////////
bool AdGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
//////////////////////////////////////////////////////////////////////////
// ChangeScene
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "ChangeScene") == 0) {
stack->correctParams(3);
const char *filename = stack->pop()->getString();
ScValue *valFadeOut = stack->pop();
ScValue *valFadeIn = stack->pop();
bool transOut = valFadeOut->isNULL() ? true : valFadeOut->getBool();
bool transIn = valFadeIn->isNULL() ? true : valFadeIn->getBool();
scheduleChangeScene(filename, transIn);
if (transOut) {
_transMgr->start(TRANSITION_FADE_OUT, true);
}
stack->pushNULL();
// HACK: Emulate things that present in some game versions only based on scene name
// Currect usecase is adding Steam Achievements to non-steam game editions
EmulateSceneHook(filename);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// LoadActor
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LoadActor") == 0) {
stack->correctParams(1);
AdActor *act = new AdActor(_game);
if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) {
addObject(act);
stack->pushNative(act, true);
// W/A for bug in game script: 'Five Magical Amulets'
// Before engine 1.4 MainObject was not invalidated on UnloadObject.
// It was used later by engine on already released object in memory.
// Engine was fixed in version 1.4, but game scripts were never fixed.
// Assign MainObject with new loaded actor.
if (BaseEngine::instance().getGameId() == "5ma") {
_mainObject = act;
}
} else {
SAFE_DELETE(act);
stack->pushNULL();
}
return STATUS_OK;
}
#ifdef ENABLE_WME3D
//////////////////////////////////////////////////////////////////////////
// LoadActor3D
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LoadActor3D") == 0) {
stack->correctParams(1);
// assume that we have an .X model here
// wme3d has also support for .ms3d files
// but they are deprecated
AdActor3DX *act = new AdActor3DX(_game);
if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) {
addObject(act);
stack->pushNative(act, true);
} else {
SAFE_DELETE(act);
stack->pushNULL();
}
return STATUS_OK;
}
#endif
//////////////////////////////////////////////////////////////////////////
// LoadEntity
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LoadEntity") == 0) {
stack->correctParams(1);
AdEntity *ent = new AdEntity(_game);
if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) {
addObject(ent);
stack->pushNative(ent, true);
} else {
SAFE_DELETE(ent);
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// UnloadObject / UnloadActor / UnloadEntity / UnloadActor3D / DeleteEntity
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "UnloadActor3D") == 0 || strcmp(name, "DeleteEntity") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
AdObject *obj = (AdObject *)val->getNative();
// HACK: We take corrosion screenshot before entering main menu
// Unused screenshots must be deleted, after main menu is closed
if (obj && BaseEngine::instance().getGameId() == "corrosion") {
const char *mm = "interface\\system\\mainmenu.window";
const char *fn = obj->_filename;
if (fn && strcmp(fn, mm) == 0) {
SAFE_DELETE(_cachedThumbnail);
}
}
removeObject(obj);
if (val->getType() == VAL_VARIABLE_REF) {
val->setNULL();
}
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// CreateEntity
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "CreateEntity") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
AdEntity *ent = new AdEntity(_game);
addObject(ent);
if (!val->isNULL()) {
ent->setName(val->getString());
}
stack->pushNative(ent, true);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// CreateItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "CreateItem") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
AdItem *item = new AdItem(_game);
addItem(item);
if (!val->isNULL()) {
item->setName(val->getString());
}
stack->pushNative(item, true);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// DeleteItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DeleteItem") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
AdItem *item = nullptr;
if (val->isNative()) {
item = (AdItem *)val->getNative();
} else {
item = getItemByName(val->getString());
}
if (item) {
deleteItem(item);
}
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// QueryItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "QueryItem") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
AdItem *item = nullptr;
if (val->isInt()) {
int32 index = val->getInt();
if (index >= 0 && index < _items.getSize()) {
item = _items[index];
}
} else {
item = getItemByName(val->getString());
}
if (item) {
stack->pushNative(item, true);
} else {
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// AddResponse/AddResponseOnce/AddResponseOnceGame
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AddResponse") == 0 || strcmp(name, "AddResponseOnce") == 0 || strcmp(name, "AddResponseOnceGame") == 0) {
stack->correctParams(6);
int id = stack->pop()->getInt();
const char *text = stack->pop()->getString();
ScValue *val1 = stack->pop();
ScValue *val2 = stack->pop();
ScValue *val3 = stack->pop();
ScValue *val4 = stack->pop();
if (_responseBox) {
AdResponse *res = new AdResponse(_game);
if (res) {
res->_id = id;
res->setText(text);
_stringTable->expand(&res->_text);
if (!val1->isNULL()) {
res->setIcon(val1->getString());
}
if (!val2->isNULL()) {
res->setIconHover(val2->getString());
}
if (!val3->isNULL()) {
res->setIconPressed(val3->getString());
}
if (!val4->isNULL()) {
res->setFont(val4->getString());
}
if (strcmp(name, "AddResponseOnce") == 0) {
res->_responseType = RESPONSE_ONCE;
} else if (strcmp(name, "AddResponseOnceGame") == 0) {
res->_responseType = RESPONSE_ONCE_GAME;
}
_responseBox->_responses.add(res);
}
} else {
script->runtimeError("Game.AddResponse: response box is not defined");
}
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// ResetResponse
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "ResetResponse") == 0) {
stack->correctParams(1);
int id = stack->pop()->getInt(-1);
resetResponse(id);
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// ClearResponses
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "ClearResponses") == 0) {
stack->correctParams(0);
_responseBox->clearResponses();
_responseBox->clearButtons();
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetResponse
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetResponse") == 0) {
stack->correctParams(1);
bool autoSelectLast = stack->pop()->getBool();
if (_responseBox) {
_responseBox->weedResponses();
if (_responseBox->_responses.getSize() == 0) {
stack->pushNULL();
return STATUS_OK;
}
if (_responseBox->_responses.getSize() == 1 && autoSelectLast) {
stack->pushInt(_responseBox->_responses[0]->_id);
_responseBox->handleResponse(_responseBox->_responses[0]);
_responseBox->clearResponses();
return STATUS_OK;
}
_responseBox->createButtons();
_responseBox->_waitingScript = script;
script->waitForExclusive(_responseBox);
_state = GAME_SEMI_FROZEN;
_stateEx = GAME_WAITING_RESPONSE;
} else {
script->runtimeError("Game.GetResponse: response box is not defined");
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetNumResponses
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetNumResponses") == 0) {
stack->correctParams(0);
if (_responseBox) {
_responseBox->weedResponses();
stack->pushInt(_responseBox->_responses.getSize());
} else {
script->runtimeError("Game.GetNumResponses: response box is not defined");
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// StartDlgBranch
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "StartDlgBranch") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
Common::String branchName;
if (val->isNULL()) {
branchName.format("line%d", script->_currentLine);
} else {
branchName = val->getString();
}
startDlgBranch(branchName.c_str(), script->_filename == nullptr ? "" : script->_filename, script->_threadEvent == nullptr ? "" : script->_threadEvent);
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// EndDlgBranch
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "EndDlgBranch") == 0) {
stack->correctParams(1);
const char *branchName = nullptr;
ScValue *val = stack->pop();
if (!val->isNULL()) {
branchName = val->getString();
}
endDlgBranch(branchName, script->_filename == nullptr ? "" : script->_filename, script->_threadEvent == nullptr ? "" : script->_threadEvent);
stack->pushNULL();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetCurrentDlgBranch
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetCurrentDlgBranch") == 0) {
stack->correctParams(0);
if (_dlgPendingBranches.getSize() > 0) {
stack->pushString(_dlgPendingBranches[_dlgPendingBranches.getSize() - 1]);
} else {
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// TakeItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TakeItem") == 0) {
return _invObject->scCallMethod(script, stack, thisStack, name);
}
//////////////////////////////////////////////////////////////////////////
// DropItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DropItem") == 0) {
return _invObject->scCallMethod(script, stack, thisStack, name);
}
//////////////////////////////////////////////////////////////////////////
// GetItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetItem") == 0) {
return _invObject->scCallMethod(script, stack, thisStack, name);
}
//////////////////////////////////////////////////////////////////////////
// HasItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "HasItem") == 0) {
return _invObject->scCallMethod(script, stack, thisStack, name);
}
//////////////////////////////////////////////////////////////////////////
// IsItemTaken
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "IsItemTaken") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
if (!val->isNULL()) {
for (int32 i = 0; i < _inventories.getSize(); i++) {
AdInventory *inv = _inventories[i];
for (int32 j = 0; j < inv->_takenItems.getSize(); j++) {
if (val->getNative() == inv->_takenItems[j]) {
stack->pushBool(true);
return STATUS_OK;
} else if (scumm_stricmp(val->getString(), inv->_takenItems[j]->_name) == 0) {
stack->pushBool(true);
return STATUS_OK;
}
}
}
} else {
script->runtimeError("Game.IsItemTaken: item name expected");
}
stack->pushBool(false);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetInventoryWindow
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetInventoryWindow") == 0) {
stack->correctParams(0);
if (_inventoryBox && _inventoryBox->_window) {
stack->pushNative(_inventoryBox->_window, true);
} else {
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetResponsesWindow
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetResponsesWindow") == 0 || strcmp(name, "GetResponseWindow") == 0) {
stack->correctParams(0);
if (_responseBox && _responseBox->_window) {
stack->pushNative(_responseBox->_window, true);
} else {
stack->pushNULL();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// LoadResponseBox
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LoadResponseBox") == 0) {
stack->correctParams(1);
const char *filename = stack->pop()->getString();
_game->unregisterObject(_responseBox);
_responseBox = new AdResponseBox(_game);
if (_responseBox && !DID_FAIL(_responseBox->loadFile(filename))) {
registerObject(_responseBox);
stack->pushBool(true);
} else {
SAFE_DELETE(_responseBox);
stack->pushBool(false);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// LoadInventoryBox
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LoadInventoryBox") == 0) {
stack->correctParams(1);
const char *filename = stack->pop()->getString();
_game->unregisterObject(_inventoryBox);
_inventoryBox = new AdInventoryBox(_game);
if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile(filename))) {
registerObject(_inventoryBox);
stack->pushBool(true);
} else {
SAFE_DELETE(_inventoryBox);
stack->pushBool(false);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// LoadItems
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LoadItems") == 0) {
stack->correctParams(2);
const char *filename = stack->pop()->getString();
bool merge = stack->pop()->getBool(false);
bool ret = loadItemsFile(filename, merge);
stack->pushBool(DID_SUCCEED(ret));
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// AddSpeechDir
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AddSpeechDir") == 0) {
stack->correctParams(1);
const char *dir = stack->pop()->getString();
stack->pushBool(DID_SUCCEED(addSpeechDir(dir)));
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// RemoveSpeechDir
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "RemoveSpeechDir") == 0) {
stack->correctParams(1);
const char *dir = stack->pop()->getString();
stack->pushBool(DID_SUCCEED(removeSpeechDir(dir)));
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetSceneViewport
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetSceneViewport") == 0) {
stack->correctParams(4);
int x = stack->pop()->getInt();
int y = stack->pop()->getInt();
int width = stack->pop()->getInt();
int height = stack->pop()->getInt();
if (width <= 0) {
width = _renderer->getWidth();
}
if (height <= 0) {
height = _renderer->getHeight();
}
if (!_sceneViewport) {
_sceneViewport = new BaseViewport(_game);
}
if (_sceneViewport) {
_sceneViewport->setRect(x, y, x + width, y + height);
}
stack->pushBool(true);
return STATUS_OK;
}
#ifdef ENABLE_FOXTAIL
//////////////////////////////////////////////////////////////////////////
// [FoxTail] SetInventoryBoxHideSelected
// Used while changing cursor type at some included script
// Return value is never used
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetInventoryBoxHideSelected") == 0) {
stack->correctParams(1);
_inventoryBox->_hideSelected = stack->pop()->getBool(false);
stack->pushNULL();
return STATUS_OK;
}
#endif
else {
return BaseGame::scCallMethod(script, stack, thisStack, name);
}
}
//////////////////////////////////////////////////////////////////////////
ScValue *AdGame::scGetProperty(const char *name) {
_scValue->setNULL();
//////////////////////////////////////////////////////////////////////////
// Type
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "Type") == 0) {
_scValue->setString("game");
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// Scene
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "Scene") == 0) {
if (_scene) {
_scValue->setNative(_scene, true);
} else {
_scValue->setNULL();
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// SelectedItem
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SelectedItem") == 0) {
//if (_selectedItem) _scValue->setString(_selectedItem->_name);
if (_selectedItem) {
_scValue->setNative(_selectedItem, true);
} else {
_scValue->setNULL();
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// NumItems
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "NumItems") == 0) {
return _invObject->scGetProperty(name);
}
//////////////////////////////////////////////////////////////////////////
// SmartItemCursor
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SmartItemCursor") == 0) {
_scValue->setBool(_smartItemCursor);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// InventoryVisible
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "InventoryVisible") == 0) {
_scValue->setBool(_inventoryBox && _inventoryBox->_visible);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// InventoryScrollOffset
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "InventoryScrollOffset") == 0) {
if (_inventoryBox) {
_scValue->setInt(_inventoryBox->_scrollOffset);
} else {
_scValue->setInt(0);
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// ResponsesVisible (RO)
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "ResponsesVisible") == 0) {
_scValue->setBool(_stateEx == GAME_WAITING_RESPONSE);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// PrevScene / PreviousScene (RO)
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "PrevScene") == 0 || strcmp(name, "PreviousScene") == 0) {
if (!_prevSceneName) {
_scValue->setString("");
} else {
_scValue->setString(_prevSceneName);
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// PrevSceneFilename / PreviousSceneFilename (RO)
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "PrevSceneFilename") == 0 || strcmp(name, "PreviousSceneFilename") == 0) {
if (!_prevSceneFilename || !_prevSceneFilename[0]) {
_scValue->setString("");
} else {
_scValue->setString(_prevSceneFilename);
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// LastResponse (RO)
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LastResponse") == 0) {
if (!_responseBox || !_responseBox->_lastResponseText|| !_responseBox->_lastResponseText[0]) {
_scValue->setString("");
} else {
_scValue->setString(_responseBox->_lastResponseText);
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// LastResponseOrig (RO)
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "LastResponseOrig") == 0) {
if (!_responseBox || !_responseBox->_lastResponseTextOrig || !_responseBox->_lastResponseTextOrig[0]) {
_scValue->setString("");
} else {
_scValue->setString(_responseBox->_lastResponseTextOrig);
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// InventoryObject
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "InventoryObject") == 0) {
if (_inventoryOwner == _invObject) {
_scValue->setNative(this, true);
} else {
_scValue->setNative(_inventoryOwner, true);
}
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// TotalNumItems
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TotalNumItems") == 0) {
_scValue->setInt(_items.getSize());
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// TalkSkipButton
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TalkSkipButton") == 0) {
_scValue->setInt(_talkSkipButton);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// VideoSkipButton
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "VideoSkipButton") == 0) {
_scValue->setInt(_videoSkipButton);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// ChangingScene
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "ChangingScene") == 0) {
_scValue->setBool(_scheduledScene != nullptr);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// StartupScene
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "StartupScene") == 0) {
if (!_startupScene || !_startupScene[0]) {
_scValue->setNULL();
} else {
_scValue->setString(_startupScene);
}
return _scValue;
}
else {
return BaseGame::scGetProperty(name);
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::scSetProperty(const char *name, ScValue *value) {
//////////////////////////////////////////////////////////////////////////
// SelectedItem
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "SelectedItem") == 0) {
if (value->isNULL()) {
_selectedItem = nullptr;
} else {
if (value->isNative()) {
_selectedItem = nullptr;
for (int32 i = 0; i < _items.getSize(); i++) {
if (_items[i] == value->getNative()) {
_selectedItem = (AdItem *)value->getNative();
break;
}
}
} else {
// try to get by name
_selectedItem = getItemByName(value->getString());
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SmartItemCursor
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SmartItemCursor") == 0) {
_smartItemCursor = value->getBool();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// InventoryVisible
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "InventoryVisible") == 0) {
if (_inventoryBox) {
_inventoryBox->_visible = value->getBool();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// InventoryObject
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "InventoryObject") == 0) {
if (_inventoryOwner && _inventoryBox) {
_inventoryOwner->getInventory()->_scrollOffset = _inventoryBox->_scrollOffset;
}
if (value->isNULL()) {
_inventoryOwner = _invObject;
} else {
BaseObject *obj = (BaseObject *)value->getNative();
if (obj == this) {
_inventoryOwner = _invObject;
} else if (_game->validObject(obj)) {
_inventoryOwner = (AdObject *)obj;
}
}
if (_inventoryOwner && _inventoryBox) {
_inventoryBox->_scrollOffset = _inventoryOwner->getInventory()->_scrollOffset;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// InventoryScrollOffset
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "InventoryScrollOffset") == 0) {
if (_inventoryBox) {
_inventoryBox->_scrollOffset = value->getInt();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// TalkSkipButton
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TalkSkipButton") == 0) {
int val = value->getInt();
if (val < 0) {
val = 0;
}
if (val > TALK_SKIP_NONE) {
val = TALK_SKIP_NONE;
}
_talkSkipButton = (TTalkSkipButton)val;
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// VideoSkipButton
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "VideoSkipButton") == 0) {
int val = value->getInt();
if (val < 0) {
val = 0;
}
if (val > VIDEO_SKIP_NONE) {
val = VIDEO_SKIP_NONE;
}
_videoSkipButton = (TVideoSkipButton)val;
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// StartupScene
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "StartupScene") == 0) {
if (value == nullptr) {
SAFE_DELETE_ARRAY(_startupScene);
} else {
BaseUtils::setString(&_startupScene, value->getString());
}
return STATUS_OK;
}
else {
return BaseGame::scSetProperty(name, value);
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) {
ScValue *thisObj;
//////////////////////////////////////////////////////////////////////////
// Actor
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "Actor") == 0) {
stack->correctParams(0);
thisObj = thisStack->getTop();
thisObj->setNative(new AdActor(_game));
stack->pushNULL();
}
//////////////////////////////////////////////////////////////////////////
// Entity
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "Entity") == 0) {
stack->correctParams(0);
thisObj = thisStack->getTop();
thisObj->setNative(new AdEntity(_game));
stack->pushNULL();
}
//////////////////////////////////////////////////////////////////////////
// call parent
else {
return BaseGame::externalCall(script, stack, thisStack, name);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::showCursor() {
if (_cursorHidden) {
return STATUS_OK;
}
if (_selectedItem && _game->_state == GAME_RUNNING && _stateEx == GAME_NORMAL && _interactive) {
if (_selectedItem->_cursorCombined) {
BaseSprite *origLastCursor = _lastCursor;
BaseGame::showCursor();
_lastCursor = origLastCursor;
}
if (_activeObject && _selectedItem->_cursorHover && _activeObject->getExtendedFlag("usable")) {
if (!_smartItemCursor || _activeObject->canHandleEvent(_selectedItem->_name)) {
return drawCursor(_selectedItem->_cursorHover);
} else {
return drawCursor(_selectedItem->_cursorNormal);
}
} else {
return drawCursor(_selectedItem->_cursorNormal);
}
} else {
return BaseGame::showCursor();
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::loadFile(const char *filename) {
char *buffer = (char *)_fileManager->readWholeFile(filename);
if (buffer == nullptr) {
_game->LOG(0, "AdGame::loadFile failed for file '%s'", filename);
return STATUS_FAILED;
}
bool ret;
setFilename(filename);
if (DID_FAIL(ret = loadBuffer(buffer, true))) {
_game->LOG(0, "Error parsing GAME file '%s'", filename);
}
delete[] buffer;
return ret;
}
TOKEN_DEF_START
TOKEN_DEF(GAME)
TOKEN_DEF(AD_GAME)
TOKEN_DEF(RESPONSE_BOX)
TOKEN_DEF(INVENTORY_BOX)
TOKEN_DEF(ITEMS)
TOKEN_DEF(ITEM)
TOKEN_DEF(TALK_SKIP_BUTTON)
TOKEN_DEF(VIDEO_SKIP_BUTTON)
TOKEN_DEF(SCENE_VIEWPORT)
TOKEN_DEF(ENTITY_CONTAINER)
TOKEN_DEF(EDITOR_PROPERTY)
TOKEN_DEF(STARTUP_SCENE)
TOKEN_DEF(DEBUG_STARTUP_SCENE)
TOKEN_DEF_END
//////////////////////////////////////////////////////////////////////////
bool AdGame::loadBuffer(char *buffer, bool complete) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(GAME)
TOKEN_TABLE(AD_GAME)
TOKEN_TABLE(RESPONSE_BOX)
TOKEN_TABLE(INVENTORY_BOX)
TOKEN_TABLE(ITEMS)
TOKEN_TABLE(TALK_SKIP_BUTTON)
TOKEN_TABLE(VIDEO_SKIP_BUTTON)
TOKEN_TABLE(SCENE_VIEWPORT)
TOKEN_TABLE(EDITOR_PROPERTY)
TOKEN_TABLE(STARTUP_SCENE)
TOKEN_TABLE(DEBUG_STARTUP_SCENE)
TOKEN_TABLE_END
char *params;
char *params2;
int cmd = 1;
BaseParser parser(_game);
bool itemFound = false, itemsFound = false;
while (cmd > 0 && (cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
switch (cmd) {
case TOKEN_GAME:
if (DID_FAIL(BaseGame::loadBuffer(params, false))) {
cmd = PARSERR_GENERIC;
}
break;
case TOKEN_AD_GAME:
while (cmd > 0 && (cmd = parser.getCommand(&params, commands, &params2)) > 0) {
switch (cmd) {
case TOKEN_RESPONSE_BOX:
SAFE_DELETE(_responseBox);
_responseBox = new AdResponseBox(_game);
if (_responseBox && !DID_FAIL(_responseBox->loadFile(params2))) {
registerObject(_responseBox);
} else {
SAFE_DELETE(_responseBox);
cmd = PARSERR_GENERIC;
}
break;
case TOKEN_INVENTORY_BOX:
SAFE_DELETE(_inventoryBox);
_inventoryBox = new AdInventoryBox(_game);
if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile(params2))) {
registerObject(_inventoryBox);
} else {
SAFE_DELETE(_inventoryBox);
cmd = PARSERR_GENERIC;
}
break;
case TOKEN_ITEMS:
itemsFound = true;
BaseUtils::setString(&_itemsFile, params2);
if (DID_FAIL(loadItemsFile(_itemsFile))) {
SAFE_DELETE_ARRAY(_itemsFile);
cmd = PARSERR_GENERIC;
}
break;
case TOKEN_TALK_SKIP_BUTTON:
if (scumm_stricmp(params2, "right") == 0) {
_talkSkipButton = TALK_SKIP_RIGHT;
} else if (scumm_stricmp(params2, "both") == 0) {
_talkSkipButton = TALK_SKIP_BOTH;
} else {
_talkSkipButton = TALK_SKIP_LEFT;
}
break;
case TOKEN_VIDEO_SKIP_BUTTON:
if (scumm_stricmp(params2, "right") == 0) {
_videoSkipButton = VIDEO_SKIP_RIGHT;
} else if (scumm_stricmp(params2, "both") == 0) {
_videoSkipButton = VIDEO_SKIP_BOTH;
} else {
_videoSkipButton = VIDEO_SKIP_LEFT;
}
break;
case TOKEN_SCENE_VIEWPORT: {
Common::Rect32 rc;
parser.scanStr(params2, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom);
if (!_sceneViewport) {
_sceneViewport = new BaseViewport(_game);
}
if (_sceneViewport) {
_sceneViewport->setRect(rc.left, rc.top, rc.right, rc.bottom);
}
}
break;
case TOKEN_EDITOR_PROPERTY:
parseEditorProperty(params2, false);
break;
case TOKEN_STARTUP_SCENE:
BaseUtils::setString(&_startupScene, params2);
break;
case TOKEN_DEBUG_STARTUP_SCENE:
BaseUtils::setString(&_debugStartupScene, params2);
break;
default:
break;
}
}
break;
default:
break;
}
}
if (cmd == PARSERR_TOKENNOTFOUND) {
_game->LOG(0, "Syntax error in GAME definition");
return STATUS_FAILED;
}
if (cmd == PARSERR_GENERIC) {
_game->LOG(0, "Error loading GAME definition");
return STATUS_FAILED;
}
if (itemFound && !itemsFound) {
_game->LOG(0, "**Warning** Please put the items definition to a separate file.");
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::persist(BasePersistenceManager *persistMgr) {
if (!persistMgr->getIsSaving()) {
cleanup();
}
BaseGame::persist(persistMgr);
_dlgPendingBranches.persist(persistMgr);
_inventories.persist(persistMgr);
persistMgr->transferPtr(TMEMBER_PTR(_inventoryBox));
_objects.persist(persistMgr);
persistMgr->transferCharPtr(TMEMBER(_prevSceneName));
persistMgr->transferCharPtr(TMEMBER(_prevSceneFilename));
persistMgr->transferPtr(TMEMBER_PTR(_responseBox));
_responsesBranch.persist(persistMgr);
_responsesGame.persist(persistMgr);
persistMgr->transferPtr(TMEMBER_PTR(_scene));
_sceneStates.persist(persistMgr);
persistMgr->transferBool(TMEMBER(_scheduledFadeIn));
persistMgr->transferCharPtr(TMEMBER(_scheduledScene));
persistMgr->transferPtr(TMEMBER_PTR(_selectedItem));
persistMgr->transferSint32(TMEMBER_INT(_talkSkipButton));
_sentences.persist(persistMgr);
persistMgr->transferPtr(TMEMBER_PTR(_sceneViewport));
persistMgr->transferSint32(TMEMBER_INT(_stateEx));
persistMgr->transferBool(TMEMBER(_initialScene));
persistMgr->transferCharPtr(TMEMBER(_debugStartupScene));
persistMgr->transferPtr(TMEMBER_PTR(_invObject));
persistMgr->transferPtr(TMEMBER_PTR(_inventoryOwner));
persistMgr->transferBool(TMEMBER(_tempDisableSaveState));
_items.persist(persistMgr);
persistMgr->transferCharPtr(TMEMBER(_itemsFile));
_speechDirs.persist(persistMgr);
persistMgr->transferBool(TMEMBER(_smartItemCursor));
if (!persistMgr->getIsSaving()) {
_initialScene = false;
}
persistMgr->transferCharPtr(TMEMBER(_startupScene));
if (persistMgr->checkVersion(1, 9, 1)) {
persistMgr->transferSint32(TMEMBER_INT(_videoSkipButton));
} else if (!persistMgr->getIsSaving()) {
_videoSkipButton = VIDEO_SKIP_LEFT;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
void AdGame::setPrevSceneName(const char *name) {
SAFE_DELETE_ARRAY(_prevSceneName);
if (name) {
size_t nameSize = strlen(name) + 1;
_prevSceneName = new char[nameSize];
Common::strcpy_s(_prevSceneName, nameSize, name);
}
}
//////////////////////////////////////////////////////////////////////////
void AdGame::setPrevSceneFilename(const char *name) {
SAFE_DELETE_ARRAY(_prevSceneFilename);
if (name) {
size_t nameSize = strlen(name) + 1;
_prevSceneFilename = new char[nameSize];
Common::strcpy_s(_prevSceneFilename, nameSize, name);
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::scheduleChangeScene(const char *filename, bool fadeIn) {
SAFE_DELETE_ARRAY(_scheduledScene);
if (_scene && !_scene->_initialized) {
return changeScene(filename, fadeIn);
} else {
size_t filenameSize = strlen(filename) + 1;
_scheduledScene = new char [filenameSize];
Common::strcpy_s(_scheduledScene, filenameSize, filename);
_scheduledFadeIn = fadeIn;
return STATUS_OK;
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) {
BaseGame::getVersion(verMajor, verMinor, nullptr, nullptr);
if (extMajor) {
*extMajor = 0;
}
if (extMinor) {
*extMinor = 0;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::loadItemsFile(const char *filename, bool merge) {
char *buffer = (char *)_game->_fileManager->readWholeFile(filename);
if (buffer == nullptr) {
_game->LOG(0, "AdGame::LoadItemsFile failed for file '%s'", filename);
return STATUS_FAILED;
}
bool ret;
//size_t filenameSize = strlen(filename) + 1;
//_filename = new char [filenameSize];
//Common::strcpy_s(_filename, filenameSize, filename);
if (DID_FAIL(ret = loadItemsBuffer(buffer, merge))) {
_game->LOG(0, "Error parsing ITEMS file '%s'", filename);
}
delete[] buffer;
return ret;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::loadItemsBuffer(char *buffer, bool merge) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(ITEM)
TOKEN_TABLE_END
char *params;
int cmd;
BaseParser parser(_game);
if (!merge) {
while (_items.getSize() > 0) {
deleteItem(_items[0]);
}
}
while ((cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
switch (cmd) {
case TOKEN_ITEM: {
AdItem *item = new AdItem(_game);
if (item && !DID_FAIL(item->loadBuffer(params, false))) {
// delete item with the same name, if exists
if (merge) {
AdItem *prevItem = getItemByName(item->_name);
if (prevItem) {
deleteItem(prevItem);
}
}
addItem(item);
} else {
SAFE_DELETE(item);
cmd = PARSERR_GENERIC;
}
}
break;
default:
break;
}
}
if (cmd == PARSERR_TOKENNOTFOUND) {
_game->LOG(0, "Syntax error in ITEMS definition");
return STATUS_FAILED;
}
if (cmd == PARSERR_GENERIC) {
_game->LOG(0, "Error loading ITEMS definition");
return STATUS_FAILED;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
AdSceneState *AdGame::getSceneState(const char *filename, bool saving) {
size_t filenameSize = strlen(filename) + 1;
char *filenameCor = new char[filenameSize];
Common::strcpy_s(filenameCor, filenameSize, filename);
for (uint32 i = 0; i < strlen(filenameCor); i++) {
if (filenameCor[i] == '/') {
filenameCor[i] = '\\';
}
}
for (int32 i = 0; i < _sceneStates.getSize(); i++) {
if (scumm_stricmp(_sceneStates[i]->_filename, filenameCor) == 0) {
delete[] filenameCor;
return _sceneStates[i];
}
}
if (saving) {
AdSceneState *ret = new AdSceneState(_game);
ret->setFilename(filenameCor);
_sceneStates.add(ret);
delete[] filenameCor;
return ret;
} else {
delete[] filenameCor;
return nullptr;
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::windowLoadHook(UIWindow *win, char **buffer, char **params) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(ENTITY_CONTAINER)
TOKEN_TABLE_END
int cmd = PARSERR_GENERIC;
BaseParser parser(_game);
cmd = parser.getCommand(buffer, commands, params);
switch (cmd) {
case TOKEN_ENTITY_CONTAINER: {
UIEntity *ent = new UIEntity(_game);
if (!ent || DID_FAIL(ent->loadBuffer(*params, false))) {
SAFE_DELETE(ent);
cmd = PARSERR_GENERIC;
} else {
ent->_parent = win;
win->_widgets.add(ent);
}
}
break;
default:
break;
}
if (cmd == PARSERR_TOKENNOTFOUND || cmd == PARSERR_GENERIC) {
return STATUS_FAILED;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) {
if (strcmp(name, "CreateEntityContainer") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
UIEntity *ent = new UIEntity(_game);
if (!val->isNULL()) {
ent->setName(val->getString());
}
stack->pushNative(ent, true);
ent->_parent = win;
win->_widgets.add(ent);
return STATUS_OK;
} else {
return STATUS_FAILED;
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::startDlgBranch(const char *branchName, const char *scriptName, const char *eventName) {
size_t sz = strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1;
char *name = new char[sz];
Common::sprintf_s(name, sz, "%s.%s.%s", branchName, scriptName, eventName);
_dlgPendingBranches.add(name);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::endDlgBranch(const char *branchName, const char *scriptName, const char *eventName) {
char *name = nullptr;
bool deleteName = false;
if (branchName == nullptr && _dlgPendingBranches.getSize() > 0) {
name = _dlgPendingBranches[_dlgPendingBranches.getSize() - 1];
} else {
if (branchName != nullptr) {
size_t sz = strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1;
name = new char[sz];
Common::sprintf_s(name, sz, "%s.%s.%s", branchName, scriptName, eventName);
deleteName = true;
}
}
if (name == nullptr) {
return STATUS_OK;
}
int startIndex = -1;
for (int32 i = _dlgPendingBranches.getSize() - 1; i >= 0; i--) {
if (scumm_stricmp(name, _dlgPendingBranches[i]) == 0) {
startIndex = i;
break;
}
}
if (startIndex >= 0) {
for (int32 i = startIndex; i < _dlgPendingBranches.getSize(); i++) {
// clearBranchResponses(_dlgPendingBranches[i]);
delete[] _dlgPendingBranches[i];
_dlgPendingBranches[i] = nullptr;
}
_dlgPendingBranches.removeAt(startIndex, _dlgPendingBranches.getSize() - startIndex);
}
// dialogue is over, forget selected responses
if (_dlgPendingBranches.getSize() == 0) {
for (int32 i = 0; i < _responsesBranch.getSize(); i++) {
delete _responsesBranch[i];
}
_responsesBranch.removeAll();
}
if (deleteName) {
delete[] name;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::clearBranchResponses(char *name) {
for (int32 i = 0; i < _responsesBranch.getSize(); i++) {
if (scumm_stricmp(name, _responsesBranch[i]->_context) == 0) {
delete _responsesBranch[i];
_responsesBranch.removeAt(i);
i--;
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::addBranchResponse(int id) {
if (branchResponseUsed(id)) {
return STATUS_OK;
}
AdResponseContext *r = new AdResponseContext(_game);
r->_id = id;
r->setContext(_dlgPendingBranches.getSize() > 0 ? _dlgPendingBranches[_dlgPendingBranches.getSize() - 1] : nullptr);
_responsesBranch.add(r);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::branchResponseUsed(int id) const {
char *context = _dlgPendingBranches.getSize() > 0 ? _dlgPendingBranches[_dlgPendingBranches.getSize() - 1] : nullptr;
for (int32 i = 0; i < _responsesBranch.getSize(); i++) {
if (_responsesBranch[i]->_id == id) {
// make sure context != nullptr
if ((context == nullptr && _responsesBranch[i]->_context == nullptr) || (context != nullptr && scumm_stricmp(context, _responsesBranch[i]->_context) == 0)) {
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::addGameResponse(int id) {
if (gameResponseUsed(id)) {
return STATUS_OK;
}
AdResponseContext *r = new AdResponseContext(_game);
r->_id = id;
r->setContext(_dlgPendingBranches.getSize() > 0 ? _dlgPendingBranches[_dlgPendingBranches.getSize() - 1] : nullptr);
_responsesGame.add(r);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::gameResponseUsed(int id) const {
char *context = _dlgPendingBranches.getSize() > 0 ? _dlgPendingBranches[_dlgPendingBranches.getSize() - 1] : nullptr;
for (int32 i = 0; i < _responsesGame.getSize(); i++) {
AdResponseContext *respContext = _responsesGame[i];
if (respContext->_id == id) {
// make sure context != nullptr
if ((context == nullptr && respContext->_context == nullptr) || ((context != nullptr && respContext->_context != nullptr) && (context != nullptr && scumm_stricmp(context, respContext->_context) == 0))) {
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::resetResponse(int id) {
char *context = _dlgPendingBranches.getSize() > 0 ? _dlgPendingBranches[_dlgPendingBranches.getSize() - 1] : nullptr;
for (int32 i = 0; i < _responsesGame.getSize(); i++) {
if (_responsesGame[i]->_id == id) {
// make sure context != nullptr
if ((context == nullptr && _responsesGame[i]->_context == nullptr) || (context != nullptr && scumm_stricmp(context, _responsesGame[i]->_context) == 0)) {
delete _responsesGame[i];
_responsesGame.removeAt(i);
break;
}
}
}
for (int32 i = 0; i < _responsesBranch.getSize(); i++) {
if (_responsesBranch[i]->_id == id) {
// make sure context != nullptr
if ((context == nullptr && _responsesBranch[i]->_context == nullptr) || (context != nullptr && scumm_stricmp(context, _responsesBranch[i]->_context) == 0)) {
delete _responsesBranch[i];
_responsesBranch.removeAt(i);
break;
}
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::displayContent(bool doUpdate, bool displayAll) {
// init
if (doUpdate) {
initLoop();
}
// clear screen
_renderer->clear();
if (!_editorMode) {
_renderer->setScreenViewport();
}
// playing exclusive video?
if (_videoPlayer->isPlaying()) {
if (doUpdate) {
_videoPlayer->update();
}
_videoPlayer->display();
} else if (_theoraPlayer) {
if (_theoraPlayer->isPlaying()) {
if (doUpdate) {
_theoraPlayer->update();
}
_theoraPlayer->display();
}
if (_theoraPlayer->isFinished()) {
SAFE_DELETE(_theoraPlayer);
}
} else {
// process scripts
if (doUpdate) {
_scEngine->tick();
}
// process plugin events
if (doUpdate)
_game->pluginEvents().applyEvent(WME_EVENT_UPDATE, nullptr);
Common::Point32 p;
getMousePos(&p);
_scene->update();
_game->pluginEvents().applyEvent(WME_EVENT_SCENE_DRAW_BEGIN, _scene);
_scene->display();
_game->pluginEvents().applyEvent(WME_EVENT_SCENE_DRAW_END, _scene);
// display in-game windows
displayWindows(true);
if (_inventoryBox) {
_inventoryBox->display();
}
if (_stateEx == GAME_WAITING_RESPONSE) {
_responseBox->display();
}
if (_indicatorDisplay) {
#ifdef ENABLE_FOXTAIL
if (BaseEngine::instance().isFoxTail())
displayIndicatorFoxTail();
else
#endif
displayIndicator();
}
if (doUpdate || displayAll) {
_accessMgr->displayBeforeGUI();
// display normal windows
displayWindows(false);
_accessMgr->displayAfterGUI();
setActiveObject(_game->_renderer->getObjectAt(p.x, p.y));
// textual info
if (_accessGlobalPaused)
displaySentences(false);
else
displaySentences(_state == GAME_FROZEN);
showCursor();
if (_fader) {
_fader->display();
}
_transMgr->update();
}
}
if (_loadingIcon) {
_loadingIcon->display(_loadingIconX, _loadingIconY);
if (!_loadingIconPersistent) {
SAFE_DELETE(_loadingIcon);
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::registerInventory(AdInventory *inv) {
for (int32 i = 0; i < _inventories.getSize(); i++) {
if (_inventories[i] == inv) {
return STATUS_OK;
}
}
registerObject(inv);
_inventories.add(inv);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::unregisterInventory(AdInventory *inv) {
for (int32 i = 0; i < _inventories.getSize(); i++) {
if (_inventories[i] == inv) {
unregisterObject(_inventories[i]);
_inventories.removeAt(i);
return STATUS_OK;
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::isItemTaken(char *itemName) {
for (int32 i = 0; i < _inventories.getSize(); i++) {
AdInventory *inv = _inventories[i];
for (int32 j = 0; j < inv->_takenItems.getSize(); j++) {
if (scumm_stricmp(itemName, inv->_takenItems[j]->_name) == 0) {
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
AdItem *AdGame::getItemByName(const char *name) const {
for (int32 i = 0; i < _items.getSize(); i++) {
if (scumm_stricmp(_items[i]->_name, name) == 0) {
return _items[i];
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::addItem(AdItem *item) {
_items.add(item);
return _game->registerObject(item);
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::resetContent() {
// clear pending dialogs
for (int32 i = 0; i < _dlgPendingBranches.getSize(); i++) {
delete[] _dlgPendingBranches[i];
}
_dlgPendingBranches.removeAll();
// clear inventories
for (int32 i = 0; i < _inventories.getSize(); i++) {
_inventories[i]->_takenItems.removeAll();
}
// clear scene states
for (int32 i = 0; i < _sceneStates.getSize(); i++) {
delete _sceneStates[i];
}
_sceneStates.removeAll();
// clear once responses
for (int32 i = 0; i < _responsesBranch.getSize(); i++) {
delete _responsesBranch[i];
}
_responsesBranch.removeAll();
// clear once game responses
for (int32 i = 0; i < _responsesGame.getSize(); i++) {
delete _responsesGame[i];
}
_responsesGame.removeAll();
// reload inventory items
if (_itemsFile && _itemsFile[0]) {
loadItemsFile(_itemsFile);
}
_tempDisableSaveState = true;
return BaseGame::resetContent();
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::deleteItem(AdItem *item) {
if (!item) {
return STATUS_FAILED;
}
if (_selectedItem == item) {
_selectedItem = nullptr;
}
_scene->handleItemAssociations(item->_name, false);
// remove from all inventories
for (int32 i = 0; i < _inventories.getSize(); i++) {
_inventories[i]->removeItem(item);
}
// remove object
for (int32 i = 0; i < _items.getSize(); i++) {
if (_items[i] == item) {
unregisterObject(_items[i]);
_items.removeAt(i);
break;
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::addSpeechDir(const char *dir) {
if (!dir || dir[0] == '\0') {
return STATUS_FAILED;
}
size_t dirSize = strlen(dir) + 2;
char *temp = new char[dirSize];
Common::strcpy_s(temp, dirSize, dir);
if (temp[dirSize - 2 - 1] != '\\' && temp[dirSize - 2 - 1] != '/') {
Common::strcat_s(temp, dirSize, "\\");
}
for (int32 i = 0; i < _speechDirs.getSize(); i++) {
if (scumm_stricmp(_speechDirs[i], temp) == 0) {
delete[] temp;
return STATUS_OK;
}
}
_speechDirs.add(temp);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::removeSpeechDir(const char *dir) {
if (!dir || dir[0] == '\0') {
return STATUS_FAILED;
}
size_t dirSize = strlen(dir) + 2;
char *temp = new char[dirSize];
Common::strcpy_s(temp, dirSize, dir);
if (temp[dirSize - 2 - 1] != '\\' && temp[dirSize - 2 - 1] != '/') {
Common::strcat_s(temp, dirSize, "\\");
}
bool found = false;
for (int32 i = 0; i < _speechDirs.getSize(); i++) {
if (scumm_stricmp(_speechDirs[i], temp) == 0) {
delete[] _speechDirs[i];
_speechDirs.removeAt(i);
found = true;
break;
}
}
delete[] temp;
return found;
}
//////////////////////////////////////////////////////////////////////////
char *AdGame::findSpeechFile(char *stringID) {
char *ret = new char[MAX_PATH_LENGTH];
for (int32 i = 0; i < _speechDirs.getSize(); i++) {
Common::sprintf_s(ret, MAX_PATH_LENGTH, "%s%s.ogg", _speechDirs[i], stringID);
if (_game->_fileManager->hasFile(ret)) {
return ret;
}
Common::sprintf_s(ret, MAX_PATH_LENGTH, "%s%s.wav", _speechDirs[i], stringID);
if (_game->_fileManager->hasFile(ret)) {
return ret;
}
}
delete[] ret;
return nullptr;
}
#ifdef ENABLE_WME3D
//////////////////////////////////////////////////////////////////////////
bool AdGame::renderShadowGeometry() {
if (_scene && _scene->_geom)
return _scene->_geom->renderShadowGeometry();
else
return true;
}
#endif
//////////////////////////////////////////////////////////////////////////
BaseObject *AdGame::getNextAccessObject(BaseObject *currObject) {
BaseObject *ret = BaseGame::getNextAccessObject(currObject);
if (!ret) {
if (_responseBox && _stateEx == GAME_WAITING_RESPONSE)
return _responseBox->getNextAccessObject(currObject);
if (_scene)
return _scene->getNextAccessObject(currObject);
}
return ret;
}
//////////////////////////////////////////////////////////////////////////
BaseObject *AdGame::getPrevAccessObject(BaseObject *currObject) {
BaseObject *ret = BaseGame::getPrevAccessObject(currObject);
if (!ret) {
if (_responseBox && _stateEx == GAME_WAITING_RESPONSE)
return _responseBox->getPrevAccessObject(currObject);
if (_scene)
return _scene->getPrevAccessObject(currObject);
}
return ret;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::validMouse() {
Common::Point32 pos;
BasePlatform::getCursorPos(&pos);
//CBPlatform::ScreenToClient(Game->m_Renderer->m_Window, &Pos);
return _renderer->pointInViewport(&pos);
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::onMouseLeftDown() {
if (!validMouse()) {
return STATUS_OK;
}
if (_state == GAME_RUNNING && !_interactive) {
if (_talkSkipButton == TALK_SKIP_LEFT || _talkSkipButton == TALK_SKIP_BOTH) {
finishSentences();
}
return STATUS_OK;
}
if ((_videoSkipButton == VIDEO_SKIP_LEFT || _videoSkipButton == VIDEO_SKIP_BOTH) && isVideoPlaying()) {
_game->stopVideo();
return STATUS_OK;
}
if (_activeObject) {
_activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT);
}
bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick"));
if (!handled) {
if (_activeObject != nullptr) {
_activeObject->applyEvent("LeftClick");
} else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
_scene->applyEvent("LeftClick");
}
}
if (_activeObject != nullptr) {
_game->_capturedObject = _game->_activeObject;
}
_mouseLeftDown = true;
//BasePlatform::setCapture(_renderer->_window);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::onMouseLeftUp() {
if (isVideoPlaying()) {
return STATUS_OK;
}
if (_activeObject) {
_activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT);
}
//BasePlatform::releaseCapture();
_capturedObject = nullptr;
_mouseLeftDown = false;
bool handled;
if (BaseEngine::instance().getTargetExecutable() < WME_LITE) {
handled = _state==GAME_RUNNING && DID_SUCCEED(applyEvent("LeftRelease"));
} else {
handled = DID_SUCCEED(applyEvent("LeftRelease"));
}
if (!handled) {
if (_activeObject != nullptr) {
_activeObject->applyEvent("LeftRelease");
} else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
_scene->applyEvent("LeftRelease");
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::onMouseLeftDblClick() {
if (!validMouse()) {
return STATUS_OK;
}
if (isVideoPlaying()) {
return STATUS_OK;
}
if (_state == GAME_RUNNING && !_interactive) {
return STATUS_OK;
}
if (_activeObject) {
_activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT);
}
bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick"));
if (!handled) {
if (_activeObject != nullptr) {
_activeObject->applyEvent("LeftDoubleClick");
} else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
_scene->applyEvent("LeftDoubleClick");
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::onMouseRightDown() {
if (!validMouse()) {
return STATUS_OK;
}
if (_state == GAME_RUNNING && !_interactive) {
if (_talkSkipButton == TALK_SKIP_RIGHT || _talkSkipButton == TALK_SKIP_BOTH) {
finishSentences();
}
return STATUS_OK;
}
if ((_videoSkipButton == VIDEO_SKIP_RIGHT || _videoSkipButton == VIDEO_SKIP_BOTH) && isVideoPlaying()) {
_game->stopVideo();
return STATUS_OK;
}
if ((_state == GAME_RUNNING && !_interactive) || _stateEx == GAME_WAITING_RESPONSE) {
return STATUS_OK;
}
if (_activeObject) {
_activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT);
}
bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick"));
if (!handled) {
if (_activeObject != nullptr) {
_activeObject->applyEvent("RightClick");
} else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
_scene->applyEvent("RightClick");
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::onMouseRightUp() {
if (isVideoPlaying()) {
return STATUS_OK;
}
if (_activeObject) {
_activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT);
}
bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease"));
if (!handled) {
if (_activeObject != nullptr) {
_activeObject->applyEvent("RightRelease");
} else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) {
_scene->applyEvent("RightRelease");
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::displayDebugInfo() {
char str[100];
if (_game->_debugMode) {
Common::sprintf_s(str, "Mouse: %d, %d (scene: %d, %d)", _mousePos.x, _mousePos.y, _mousePos.x + (_scene ? _scene->getOffsetLeft() : 0), _mousePos.y + (_scene ? _scene->getOffsetTop() : 0));
_systemFont->drawText((byte *)str, 0, 90, _renderer->getWidth(), TAL_RIGHT);
Common::sprintf_s(str, "Scene: %s (prev: %s)", (_scene && _scene->_name && _scene->_name[0]) ? _scene->_name : "???", (_prevSceneName && _prevSceneName[0]) ? _prevSceneName : "???");
_systemFont->drawText((byte *)str, 0, 110, _renderer->getWidth(), TAL_RIGHT);
}
return BaseGame::displayDebugInfo();
}
#ifdef ENABLE_WME3D
//////////////////////////////////////////////////////////////////////////
Wintermute::TShadowType AdGame::getMaxShadowType(Wintermute::BaseObject *object) {
TShadowType ret = BaseGame::getMaxShadowType(object);
return MIN(ret, _scene->_maxShadowType);
}
#endif
//////////////////////////////////////////////////////////////////////////
bool AdGame::getLayerSize(int *layerWidth, int *layerHeight, Common::Rect32 *viewport, bool *customViewport) {
if (_scene && _scene->_mainLayer) {
int32 portX, portY, portWidth, portHeight;
_scene->getViewportOffset(&portX, &portY);
_scene->getViewportSize(&portWidth, &portHeight);
*customViewport = _sceneViewport || _scene->_viewport;
BasePlatform::setRect(viewport, portX, portY, portX + portWidth, portY + portHeight);
#ifdef ENABLE_WME3D
if (_scene->_scroll3DCompatibility) {
// backward compatibility hack
// WME pre-1.7 expects the camera to only view the top-left part of the scene
*layerWidth = _game->_renderer->getWidth();
*layerHeight = _game->_renderer->getHeight();
} else
#endif
{
*layerWidth = _scene->_mainLayer->_width;
*layerHeight = _scene->_mainLayer->_height;
if (_game->_editorResolutionWidth > 0)
*layerWidth = _game->_editorResolutionWidth;
if (_game->_editorResolutionHeight > 0)
*layerHeight = _game->_editorResolutionHeight;
}
return true;
} else
return BaseGame::getLayerSize(layerWidth, layerHeight, viewport, customViewport);
}
#ifdef ENABLE_WME3D
//////////////////////////////////////////////////////////////////////////
uint32 AdGame::getAmbientLightColor() {
if (_scene) {
return _scene->_ambientLightColor;
} else {
return BaseGame::getAmbientLightColor();
}
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::getFogParams(bool *fogEnabled, uint32 *fogColor, float *start, float *end) {
if (_scene) {
*fogEnabled = _scene->_fogEnabled;
*fogColor = _scene->_fogColor;
*start = _scene->_fogStart;
*end = _scene->_fogEnd;
return true;
} else {
return BaseGame::getFogParams(fogEnabled, fogColor, start, end);
}
}
#endif
//////////////////////////////////////////////////////////////////////////
bool AdGame::onScriptShutdown(ScScript *script) {
if (_responseBox && _responseBox->_waitingScript == script) {
_responseBox->_waitingScript = nullptr;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::handleCustomActionStart(BaseGameCustomAction action) {
bool isCorrosion = BaseEngine::instance().getGameId() == "corrosion";
if (isCorrosion) {
// Corrosion Enhanced Edition contain city map screen, which is
// mouse controlled and conflicts with those custom actions
const char *m = "items\\street_map\\windows\\street_map_window.script";
if (_scEngine->isRunningScript(m)) {
return false;
}
}
int xLeft = 30;
int xCenter = _renderer->getWidth() / 2;
int xRight = _renderer->getWidth() - 30;
int yTop = 35;
int yCenter = _renderer->getHeight() / 2;
int yBottom = _renderer->getHeight() - 35;
if (isCorrosion && !(ConfMan.get("extra").contains("Enhanced"))) {
// original version of Corrosion has a toolbar under the game screen
yBottom -= 60;
}
BaseArray<AdObject *> objects;
Common::Point32 p;
int distance = xCenter * xCenter + yCenter * yCenter;
switch (action) {
case kClickAtCenter:
p.x = xCenter;
p.y = yCenter;
break;
case kClickAtLeft:
p.x = xLeft;
p.y = yCenter;
break;
case kClickAtRight:
p.x = xRight;
p.y = yCenter;
break;
case kClickAtTop:
p.x = xCenter;
p.y = yTop;
break;
case kClickAtBottom:
p.x = xCenter;
p.y = yBottom;
break;
case kClickAtEntityNearestToCenter:
p.x = xCenter;
p.y = yCenter;
// Looking through all objects for entities near to the center
if (_scene && _scene->getSceneObjects(objects, true)) {
for (int32 i = 0; i < objects.getSize(); i++) {
BaseRegion *region;
if (objects[i]->_type != OBJECT_ENTITY ||
!objects[i]->_active ||
!objects[i]->_registrable ||
(!(region = ((AdEntity *)objects[i])->_region))
) {
continue;
}
// Something exactly at center? Great!
if (region->pointInRegion(xCenter, yCenter)) {
distance = 0;
p.x = xCenter;
p.y = yCenter;
break;
}
// Something at the edge? Available with other actions.
if (region->pointInRegion(xLeft, yCenter) ||
region->pointInRegion(xRight, yCenter) ||
region->pointInRegion(xCenter, yBottom) ||
region->pointInRegion(xCenter, yTop)
) {
continue;
}
// Keep entities that has less distance to center
int x = ((AdEntity *)objects[i])->_posX;
int y = ((AdEntity *)objects[i])->_posY - ((AdEntity *)objects[i])->getHeight() / 2;
int d = (x - xCenter) * (x - xCenter) + (y - yCenter) * (y - yCenter);
if (distance > d) {
distance = d;
p.x = x;
p.y = y;
}
}
}
break;
default:
return false;
}
BasePlatform::setCursorPos(p.x, p.y);
setActiveObject(_game->_renderer->getObjectAt(p.x, p.y));
onMouseLeftDown();
onMouseLeftUp();
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdGame::handleCustomActionEnd(BaseGameCustomAction action) {
return false;
}
Common::String AdGame::debuggerToString() const {
return Common::String::format("%p: Game \"%s\"", (const void *)this, _name);
}
} // End of namespace Wintermute