682 lines
21 KiB
C++
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
|