Files
2026-02-02 04:50:13 +01:00

682 lines
21 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 "twine/scene/gamestate.h"
#include "common/rect.h"
#include "common/str.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "twine/audio/music.h"
#include "twine/audio/sound.h"
#include "twine/input.h"
#include "twine/menu/interface.h"
#include "twine/menu/menu.h"
#include "twine/menu/menuoptions.h"
#include "twine/renderer/redraw.h"
#include "twine/renderer/renderer.h"
#include "twine/renderer/screens.h"
#include "twine/resources/resources.h"
#include "twine/scene/actor.h"
#include "twine/scene/animations.h"
#include "twine/scene/collision.h"
#include "twine/scene/extra.h"
#include "twine/scene/grid.h"
#include "twine/scene/scene.h"
#include "twine/shared.h"
#include "twine/text.h"
#include "twine/twine.h"
#define SIZE_FOUND_OBJ 130
namespace TwinE {
GameState::GameState(TwinEEngine *engine) : _engine(engine) {
clearGameFlags();
Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
Common::fill(&_holomapFlags[0], &_holomapFlags[MAX_HOLO_POS_2], 0);
Common::fill(&_gameListChoice[0], &_gameListChoice[10], TextId::kNone);
}
void GameState::init3DGame() {
_engine->_renderer->setIsoProjection(_engine->width() / 2 - 8 - 1, _engine->height() / 2, SIZE_BRICK_XZ);
_engine->_renderer->setPosCamera(0, 0, 0);
_engine->_renderer->setAngleCamera(LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0);
_engine->_renderer->setLightVector(_engine->_scene->_alphaLight, _engine->_scene->_betaLight, LBAAngles::ANGLE_0);
}
void GameState::initGameStateVars() {
debug(2, "Init game state variables");
_engine->_extra->clearExtra();
for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
_engine->_redraw->overlayList[i].num = -1;
}
for (int32 i = 0; i < ARRAYSIZE(_engine->_scene->_listFlagCube); i++) {
_engine->_scene->_listFlagCube[i] = 0;
}
clearGameFlags();
Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
_engine->_scene->initSceneVars();
Common::fill(&_holomapFlags[0], &_holomapFlags[MAX_HOLO_POS_2], 0);
}
void GameState::initHeroVars() {
_engine->_actor->initObject(OWN_ACTOR_SCENE_INDEX); // reset Hero
_magicBall = -1;
_inventoryNumLeafsBox = 2;
_inventoryNumLeafs = 2;
_goldPieces = 0;
_nbLittleKeys = 0;
_magicPoint = 0;
_weapon = false;
_engine->_scene->_sceneHero->_genBody = BodyType::btNormal;
_engine->_scene->_sceneHero->setLife(_engine->getMaxLife());
_engine->_scene->_sceneHero->_talkColor = COLOR_BRIGHT_BLUE;
}
void GameState::initEngineVars() {
debug(2, "Init engine variables");
_engine->_interface->unsetClip();
_engine->_scene->_alphaLight = LBAAngles::ANGLE_315;
_engine->_scene->_betaLight = LBAAngles::ANGLE_334;
init3DGame();
initGameStateVars();
initHeroVars();
_engine->_scene->_sceneStart.x = 16 * SIZE_BRICK_XZ;
_engine->_scene->_sceneStart.y = 24 * SIZE_BRICK_Y;
_engine->_scene->_sceneStart.z = 16 * SIZE_BRICK_XZ;
_engine->_scene->_numCube = SCENE_CEILING_GRID_FADE_1;
_engine->_scene->_newCube = LBA1SceneId::Citadel_Island_Prison;
_engine->_sceneLoopState = SceneLoopState::Continue;
_engine->_scene->_mecaPenguinIdx = -1;
_engine->_menuOptions->flagCredits = false;
_inventoryNumLeafs = 0;
_inventoryNumLeafsBox = 2;
_magicPoint = 0;
_goldPieces = 0;
_nbLittleKeys = 0;
_inventoryNumGas = 0;
_engine->_actor->_cropBottomScreen = 0;
_magicLevelIdx = 0;
_weapon = false;
setChapter(0);
_engine->_scene->_sceneTextBank = TextBankId::Options_and_menus;
_engine->_scene->_numObjFollow = OWN_ACTOR_SCENE_INDEX;
_engine->_actor->_heroBehaviour = HeroBehaviourType::kNormal;
_engine->_actor->_previousHeroAngle = 0;
_engine->_actor->_previousHeroBehaviour = HeroBehaviourType::kNormal;
}
// https://web.archive.org/web/*/http://lbafileinfo.kazekr.net/index.php?title=LBA1:Savegame
bool GameState::loadGame(Common::SeekableReadStream *file) {
if (file == nullptr) {
return false;
}
if (!_engine->isLBA1()) {
warning("Loading not implemented for lba2");
return false;
}
debug(2, "Load game");
const byte saveFileVersion = file->readByte();
// 4 is dotemu enhanced version of lba1
if (saveFileVersion != 3 && saveFileVersion != 4) {
warning("Could not load savegame - wrong magic byte");
return false;
}
initEngineVars();
int playerNameIdx = 0;
do {
const byte c = file->readByte();
_engine->_menuOptions->_saveGameName[playerNameIdx++] = c;
if (c == '\0') {
break;
}
if (playerNameIdx >= ARRAYSIZE(_engine->_menuOptions->_saveGameName)) {
warning("Failed to load savegame. Invalid playername.");
return false;
}
} while (true);
byte numGameFlags = file->readByte();
if (numGameFlags != NUM_GAME_FLAGS_LBA1) {
warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS_LBA1, numGameFlags);
return false;
}
for (uint8 i = 0; i < numGameFlags; ++i) {
setGameFlag(i, file->readByte());
}
_engine->_scene->_newCube = file->readByte(); // scene index
setChapter(file->readByte());
_engine->_actor->_heroBehaviour = (HeroBehaviourType)file->readByte();
_engine->_actor->_previousHeroBehaviour = _engine->_actor->_heroBehaviour;
_engine->_scene->_sceneHero->setLife(file->readByte());
setKashes(file->readSint16LE());
_magicLevelIdx = file->readByte();
setMagicPoints(file->readByte());
setLeafBoxes(file->readByte());
_engine->_scene->_sceneStart.x = file->readSint16LE();
_engine->_scene->_sceneStart.y = file->readSint16LE();
_engine->_scene->_sceneStart.z = file->readSint16LE();
_engine->_scene->_sceneHero->_beta = ToAngle(file->readSint16LE());
_engine->_actor->_previousHeroAngle = _engine->_scene->_sceneHero->_beta;
_engine->_scene->_sceneHero->_genBody = (BodyType)file->readByte();
const byte numHolomapFlags = file->readByte(); // number of holomap locations
if (numHolomapFlags != _engine->numHoloPos()) {
warning("Failed to load holomapflags. Got %u, expected %i", numHolomapFlags, _engine->numHoloPos());
return false;
}
file->read(_holomapFlags, _engine->numHoloPos());
setGas(file->readByte());
const byte numInventoryFlags = file->readByte(); // number of used inventory items, always 28
if (numInventoryFlags != NUM_INVENTORY_ITEMS) {
warning("Failed to load inventoryFlags. Got %u, expected %i", numInventoryFlags, NUM_INVENTORY_ITEMS);
return false;
}
file->read(_inventoryFlags, NUM_INVENTORY_ITEMS);
setLeafs(file->readByte());
_weapon = file->readByte();
if (saveFileVersion == 4) {
// the time the game was played
file->readUint32LE();
file->readUint32LE();
}
_engine->_scene->_numCube = SCENE_CEILING_GRID_FADE_1;
_engine->_scene->_flagChgCube = ScenePositionType::kReborn;
return true;
}
bool GameState::saveGame(Common::WriteStream *file) {
debug(2, "Save game");
if (!_engine->isLBA1()) {
warning("Saving not implemented for lba2");
return false;
}
if (_engine->_menuOptions->_saveGameName[0] == '\0') {
Common::strlcpy(_engine->_menuOptions->_saveGameName, "TwinEngineSave", sizeof(_engine->_menuOptions->_saveGameName));
}
int32 sceneIdx = _engine->_scene->_numCube;
if (sceneIdx == Polar_Island_end_scene || sceneIdx == Citadel_Island_end_sequence_1 || sceneIdx == Citadel_Island_end_sequence_2 || sceneIdx == Credits_List_Sequence) {
/* inventoryMagicPoints = 0x50 */
/* herobehaviour = 0 */
/* newheropos.x = 0xffff */
sceneIdx = Polar_Island_Final_Battle;
}
file->writeByte(0x03);
file->writeString(_engine->_menuOptions->_saveGameName);
file->writeByte('\0');
file->writeByte(NUM_GAME_FLAGS_LBA1);
for (uint8 i = 0; i < NUM_GAME_FLAGS_LBA1; ++i) {
file->writeByte((uint8)hasGameFlag(i));
}
file->writeByte(sceneIdx);
file->writeByte(getChapter());
file->writeByte((byte)_engine->_actor->_heroBehaviour);
file->writeByte(_engine->_scene->_sceneHero->_lifePoint);
file->writeSint16LE(_goldPieces);
file->writeByte(_magicLevelIdx);
file->writeByte(_magicPoint);
file->writeByte(_inventoryNumLeafsBox);
// we don't save the whole scene state - so we have to make sure that the hero is
// respawned at the start of the scene - and not at its current position
file->writeSint16LE(_engine->_scene->_sceneStart.x);
file->writeSint16LE(_engine->_scene->_sceneStart.y);
file->writeSint16LE(_engine->_scene->_sceneStart.z);
file->writeSint16LE(FromAngle(_engine->_scene->_sceneHero->_beta));
file->writeByte((uint8)_engine->_scene->_sceneHero->_genBody);
// number of holomap locations
file->writeByte(_engine->numHoloPos());
file->write(_holomapFlags, _engine->numHoloPos());
file->writeByte(_inventoryNumGas);
// number of inventory items
file->writeByte(NUM_INVENTORY_ITEMS);
file->write(_inventoryFlags, NUM_INVENTORY_ITEMS);
file->writeByte(_inventoryNumLeafs);
file->writeByte(_weapon ? 1 : 0);
file->writeByte(0);
return true;
}
void GameState::setChapter(int16 chapter) {
if (_engine->isLBA1()) {
_gameChapter = chapter;
return;
}
setGameFlag(253, chapter);
}
int16 GameState::getChapter() const {
if (_engine->isLBA1()) {
return _gameChapter;
}
return _listFlagGame[253];
}
void GameState::setGameFlag(uint8 index, int16 value) {
if (_listFlagGame[index] == value) {
return;
}
debug(2, "Set gameStateFlags[%u]=%u", index, value);
_listFlagGame[index] = value;
if (!value) {
return;
}
if ((index == GAMEFLAG_VIDEO_BAFFE || index == GAMEFLAG_VIDEO_BAFFE2 || index == GAMEFLAG_VIDEO_BAFFE3 || index == GAMEFLAG_VIDEO_BAFFE5) &&
_listFlagGame[GAMEFLAG_VIDEO_BAFFE] != 0 && _listFlagGame[GAMEFLAG_VIDEO_BAFFE2] != 0 && _listFlagGame[GAMEFLAG_VIDEO_BAFFE3] != 0 && _listFlagGame[GAMEFLAG_VIDEO_BAFFE5] != 0) {
// all 4 slap videos
_engine->unlockAchievement("LBA_ACH_012");
} else if (index == GAMEFLAG_VIDEO_BATEAU2) {
// second video of ferry trip
_engine->unlockAchievement("LBA_ACH_010");
} else if (index == (uint8)InventoryItems::kiUseSabre) {
_engine->unlockAchievement("LBA_ACH_002");
} else if (index == (uint8)InventoryItems::kBottleOfSyrup) {
_engine->unlockAchievement("LBA_ACH_007");
}
}
void GameState::doFoundObj(InventoryItems item) {
_engine->_grid->centerOnActor(_engine->_scene->_sceneHero);
// Hide hero in scene
_engine->_scene->_sceneHero->_flags.bIsInvisible = 1;
_engine->_redraw->drawScene(true);
_engine->_scene->_sceneHero->_flags.bIsInvisible = 0;
_engine->saveFrontBuffer();
IVec3 itemCamera;
itemCamera.x = _engine->_grid->_startCube.x * SIZE_BRICK_XZ;
itemCamera.y = _engine->_grid->_startCube.y * SIZE_BRICK_Y;
itemCamera.z = _engine->_grid->_startCube.z * SIZE_BRICK_XZ;
BodyData &bodyData = _engine->_scene->_sceneHero->_entityDataPtr->getBody(_engine->_scene->_sceneHero->_body);
const IVec3 bodyPos = _engine->_scene->_sceneHero->_posObj - itemCamera;
Common::Rect modelRect;
_engine->_renderer->renderIsoModel(bodyPos, LBAAngles::ANGLE_0, LBAAngles::ANGLE_45, LBAAngles::ANGLE_0, bodyData, modelRect);
_engine->_interface->setClip(modelRect);
const int32 itemX = (_engine->_scene->_sceneHero->_posObj.x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
int32 itemY = _engine->_scene->_sceneHero->_posObj.y / SIZE_BRICK_Y;
if (_engine->_scene->_sceneHero->brickShape() != ShapeType::kNone) {
itemY++;
}
const int32 itemZ = (_engine->_scene->_sceneHero->_posObj.z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
_engine->_grid->drawOverBrick(itemX, itemY, itemZ);
IVec3 projPos = _engine->_renderer->projectPoint(bodyPos);
projPos.y -= 150;
const int32 boxTopLeftX = projPos.x - (SIZE_FOUND_OBJ / 2);
const int32 boxTopLeftY = projPos.y - (SIZE_FOUND_OBJ / 2);
const int32 boxBottomRightX = projPos.x + (SIZE_FOUND_OBJ / 2);
const int32 boxBottomRightY = projPos.y + (SIZE_FOUND_OBJ / 2);
const Common::Rect boxRect(boxTopLeftX, boxTopLeftY, boxBottomRightX, boxBottomRightY);
_engine->_sound->mixSample(Samples::BigItemFound, 0x1000, 1, 128, 128);
// process vox play
_engine->_music->stopMusic();
_engine->_text->initDial(TextBankId::Inventory_Intro_and_Holomap);
_engine->_interface->unsetClip();
_engine->_text->initItemFoundText(item);
_engine->_text->initDialWindow();
ProgressiveTextState textState = ProgressiveTextState::ContinueRunning;
_engine->_text->initVoxToPlayTextId((TextId)item);
const int32 bodyAnimIdx = _engine->_animations->searchAnim(AnimationTypes::kFoundItem, OWN_ACTOR_SCENE_INDEX);
const AnimData &ptranim = _engine->_resources->_animData[bodyAnimIdx];
_engine->_animations->stockInterAnim(bodyData, &bodyData._animTimerData);
uint frameanim = 0;
_engine->_redraw->_nbOptPhysBox = 0;
AnimTimerDataStruct animTimerData;
ScopedKeyMap uiKeyMap(_engine, uiKeyMapId);
int16 itemAngle = LBAAngles::ANGLE_0;
for (;;) {
FrameMarker frame(_engine, 66);
_engine->_interface->unsetClip();
_engine->_redraw->_nbPhysBox = 0;
_engine->_redraw->clsBoxes();
_engine->_interface->shadeBox(boxRect, 4);
_engine->_interface->setClip(boxRect);
itemAngle += LBAAngles::ANGLE_2;
_engine->_renderer->draw3dObject(projPos.x, projPos.y, _engine->_resources->_inventoryTable[item], itemAngle, 10000);
_engine->_menu->drawRectBorders(boxRect);
_engine->_redraw->addPhysBox(boxRect);
_engine->_interface->unsetClip();
init3DGame();
if (_engine->_animations->setInterAnimObjet(frameanim, ptranim, bodyData, &animTimerData)) {
frameanim++; // keyframe
if (frameanim >= ptranim.getNbFramesAnim()) {
frameanim = ptranim.getLoopFrame();
}
}
_engine->_renderer->renderIsoModel(bodyPos, LBAAngles::ANGLE_0, LBAAngles::ANGLE_45, LBAAngles::ANGLE_0, bodyData, modelRect);
_engine->_interface->setClip(modelRect);
_engine->_grid->drawOverBrick(itemX, itemY, itemZ);
_engine->_redraw->addPhysBox(modelRect);
if (textState == ProgressiveTextState::ContinueRunning) {
_engine->_interface->unsetClip();
textState = _engine->_text->nextDialChar();
} else {
_engine->_text->fadeInRemainingChars();
}
_engine->_redraw->flipBoxes();
_engine->readKeys();
if (_engine->_input->toggleAbortAction()) {
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
break;
}
if (_engine->_input->toggleActionIfActive(TwinEActionType::UINextPage)) {
if (textState == ProgressiveTextState::End) {
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
break;
}
if (textState == ProgressiveTextState::NextPage) {
textState = ProgressiveTextState::ContinueRunning;
}
}
_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry);
// advance the timer to play animations
_engine->timerRef++;
debugC(3, kDebugLevels::kDebugTimers, "FoundObj time: %i", _engine->timerRef);
}
while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
FrameMarker frame(_engine);
_engine->readKeys();
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
break;
}
}
init3DGame();
_engine->_text->initSceneTextBank();
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
}
void GameState::gameAskChoice(TextId choiceIdx) {
_engine->saveFrontBuffer();
_gameChoicesSettings.reset();
_gameChoicesSettings.setTextBankId((TextBankId)((int)_engine->_scene->_sceneTextBank + (int)TextBankId::Citadel_Island));
// filled via script
for (int32 i = 0; i < _gameNbChoices; i++) {
_gameChoicesSettings.addButton(_gameListChoice[i], 0);
}
_engine->_text->drawAskQuestion(choiceIdx);
_engine->_menu->doGameMenu(&_gameChoicesSettings);
const int16 activeButton = _gameChoicesSettings.getActiveButton();
_gameChoice = _gameListChoice[activeButton];
// get right VOX entry index
if (_engine->_text->initVoxToPlayTextId(_gameChoice)) {
while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
FrameMarker frame(_engine);
if (_engine->shouldQuit()) {
break;
}
}
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
_engine->_text->_hasHiddenVox = false;
_engine->_text->_voxHiddenIndex = 0;
}
}
void GameState::processGameoverAnimation() {
const int32 tmpLbaTime = _engine->timerRef;
_engine->testRestoreModeSVGA(false);
// workaround to fix hero redraw after drowning
_engine->_scene->_sceneHero->_flags.bIsInvisible = 1;
_engine->_redraw->drawScene(true);
_engine->_scene->_sceneHero->_flags.bIsInvisible = 0;
// TODO: inSceneryView
_engine->setPalette(_engine->_screens->_ptrPal);
_engine->saveFrontBuffer();
BodyData gameOverPtr;
if (!gameOverPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_GAMEOVERMDL, _engine->isLBA1())) {
return;
}
_engine->_sound->stopSamples();
_engine->_music->stopMusicMidi(); // stop fade music
_engine->_renderer->setProjection(_engine->width() / 2, _engine->height() / 2, 128, 200, 200);
int32 startLbaTime = _engine->timerRef;
const Common::Rect &rect = _engine->centerOnScreen(_engine->width() / 2, _engine->height() / 2);
_engine->_interface->setClip(rect);
int32 zoom = 50000;
Common::Rect dummy;
while (!_engine->_input->toggleAbortAction() && (_engine->timerRef - startLbaTime) <= _engine->toSeconds(10)) {
FrameMarker frame(_engine, 66);
_engine->readKeys();
if (_engine->shouldQuit()) {
return;
}
zoom = boundRuleThree(40000, 3200, _engine->toSeconds(10), _engine->timerRef - startLbaTime);
const int32 angle = ruleThree32(1, LBAAngles::ANGLE_360, _engine->toSeconds(2), (_engine->timerRef - startLbaTime) % _engine->toSeconds(2));
_engine->blitWorkToFront(rect);
_engine->_renderer->setFollowCamera(0, 0, 0, 0, -angle, 0, zoom);
_engine->_renderer->affObjetIso(0, 0, 0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, gameOverPtr, dummy);
_engine->timerRef++;
debugC(3, kDebugLevels::kDebugTimers, "GameOver time: %i", _engine->timerRef);
}
const uint16 pitchBend = 0x1000 + _engine->getRandomNumber(2000) - (2000 / 2);
_engine->_sound->mixSample(Samples::Explode, pitchBend, 1, 128, 128);
_engine->blitWorkToFront(rect);
_engine->_renderer->setFollowCamera(0, 0, 0, 0, 0, 0, zoom);
_engine->_renderer->affObjetIso(0, 0, 0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, gameOverPtr, dummy);
_engine->delaySkip(2000);
_engine->_interface->unsetClip();
_engine->restoreFrontBuffer();
init3DGame();
_engine->timerRef = tmpLbaTime;
}
void GameState::giveUp() {
_engine->_sound->stopSamples();
// TODO: is an autosave desired on giving up? I don't think so. What did the original game do here?
//_engine->autoSave();
initGameStateVars();
_engine->_scene->stopRunningGame();
}
int16 GameState::setGas(int16 value) {
_inventoryNumGas = CLIP<int16>(value, 0, 100);
return _inventoryNumGas;
}
void GameState::addGas(int16 value) {
setGas(_inventoryNumGas + value);
}
// late game items from lba1 classic new game +
void GameState::handleLateGameItems() {
if (!_endGameItems) {
return;
}
debug("Give end game items");
_endGameItems = false;
_magicLevelIdx = 4;
setMaxMagicPoints();
giveItem(InventoryItems::kiUseSabre);
giveItem(InventoryItems::kiProtoPack);
giveItem(InventoryItems::kiHolomap);
giveItem(InventoryItems::kiTunic);
giveItem(InventoryItems::kiMagicBall);
giveItem(InventoryItems::kSendellsMedallion);
giveItem(InventoryItems::kiPenguin);
giveItem(InventoryItems::kGasItem);
giveItem(InventoryItems::kiCloverLeaf);
addGas(10);
}
int16 GameState::setKashes(int16 value) {
_goldPieces = CLIP<int16>(value, 0, 999);
if (_engine->_gameState->_goldPieces >= 500) {
_engine->unlockAchievement("LBA_ACH_011");
}
return _goldPieces;
}
int16 GameState::setZlitos(int16 value) {
_zlitosPieces = CLIP<int16>(value, 0, 999);
return _zlitosPieces;
}
int16 GameState::setKeys(int16 value) {
_nbLittleKeys = MAX<int16>(0, value);
return _nbLittleKeys;
}
void GameState::addKeys(int16 val) {
setKeys(_nbLittleKeys + val);
}
void GameState::addKashes(int16 val) {
setKashes(_goldPieces + val);
}
int16 GameState::setMagicPoints(int16 val) {
_magicPoint = val;
if (_magicPoint > _magicLevelIdx * 20) {
_magicPoint = _magicLevelIdx * 20;
} else if (_magicPoint < 0) {
_magicPoint = 0;
}
return _magicPoint;
}
int16 GameState::setMaxMagicPoints() {
_magicPoint = _magicLevelIdx * 20;
return _magicPoint;
}
void GameState::addMagicPoints(int16 val) {
setMagicPoints(_magicPoint + val);
}
int16 GameState::setLeafs(int16 val) {
_inventoryNumLeafs = val;
if (_inventoryNumLeafs > _inventoryNumLeafsBox) {
_inventoryNumLeafs = _inventoryNumLeafsBox;
}
return _inventoryNumLeafs;
}
void GameState::addLeafs(int16 val) {
setLeafs(_inventoryNumLeafs + val);
}
int16 GameState::setLeafBoxes(int16 val) {
_inventoryNumLeafsBox = val;
if (_inventoryNumLeafsBox > 10) {
_inventoryNumLeafsBox = 10;
}
if (_inventoryNumLeafsBox == 5) {
_engine->unlockAchievement("LBA_ACH_003");
}
return _inventoryNumLeafsBox;
}
void GameState::addLeafBoxes(int16 val) {
setLeafBoxes(_inventoryNumLeafsBox + val);
}
void GameState::clearGameFlags() {
debug(2, "Clear all gameStateFlags");
Common::fill(&_listFlagGame[0], &_listFlagGame[NUM_GAME_FLAGS], 0);
}
int16 GameState::hasGameFlag(uint8 index) const {
debug(6, "Query gameStateFlags[%u]=%u", index, _listFlagGame[index]);
return _listFlagGame[index];
}
} // namespace TwinE