mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-15 13:11:32 +00:00
653 lines
21 KiB
C++
653 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/file.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"
|
|
|
|
namespace TwinE {
|
|
|
|
GameState::GameState(TwinEEngine *engine) : _engine(engine) {
|
|
clearGameFlags();
|
|
Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
|
|
Common::fill(&_holomapFlags[0], &_holomapFlags[NUM_LOCATIONS], 0);
|
|
Common::fill(&_gameChoices[0], &_gameChoices[10], TextId::kNone);
|
|
}
|
|
|
|
void GameState::initEngineProjections() {
|
|
_engine->_renderer->setOrthoProjection(_engine->width() / 2 - 9, _engine->height() / 2, 512);
|
|
_engine->_renderer->setBaseTranslation(0, 0, 0);
|
|
_engine->_renderer->setBaseRotation(ANGLE_0, ANGLE_0, ANGLE_0);
|
|
_engine->_renderer->setLightVector(_engine->_scene->_alphaLight, _engine->_scene->_betaLight, ANGLE_0);
|
|
}
|
|
|
|
void GameState::initGameStateVars() {
|
|
debug(2, "Init game state variables");
|
|
_engine->_extra->resetExtras();
|
|
|
|
for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
|
|
_engine->_redraw->overlayList[i].info0 = -1;
|
|
}
|
|
|
|
for (int32 i = 0; i < ARRAYSIZE(_engine->_scene->_sceneFlags); i++) {
|
|
_engine->_scene->_sceneFlags[i] = 0;
|
|
}
|
|
|
|
clearGameFlags();
|
|
Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
|
|
|
|
_engine->_scene->initSceneVars();
|
|
|
|
Common::fill(&_holomapFlags[0], &_holomapFlags[NUM_LOCATIONS], 0);
|
|
}
|
|
|
|
void GameState::initHeroVars() {
|
|
_engine->_actor->resetActor(OWN_ACTOR_SCENE_INDEX); // reset Hero
|
|
|
|
_magicBallIdx = -1;
|
|
|
|
_inventoryNumLeafsBox = 2;
|
|
_inventoryNumLeafs = 2;
|
|
_inventoryNumKashes = 0;
|
|
_inventoryNumKeys = 0;
|
|
_inventoryMagicPoints = 0;
|
|
|
|
_usingSabre = false;
|
|
|
|
_engine->_scene->_sceneHero->_body = BodyType::btNormal;
|
|
_engine->_scene->_sceneHero->setLife(kActorMaxLife);
|
|
_engine->_scene->_sceneHero->_talkColor = COLOR_BRIGHT_BLUE;
|
|
}
|
|
|
|
void GameState::initEngineVars() {
|
|
debug(2, "Init engine variables");
|
|
_engine->_interface->resetClip();
|
|
|
|
_engine->_scene->_alphaLight = ANGLE_315;
|
|
_engine->_scene->_betaLight = ANGLE_334;
|
|
initEngineProjections();
|
|
initGameStateVars();
|
|
initHeroVars();
|
|
|
|
_engine->_scene->_newHeroPos.x = 16 * BRICK_SIZE;
|
|
_engine->_scene->_newHeroPos.y = 24 * BRICK_HEIGHT;
|
|
_engine->_scene->_newHeroPos.z = 16 * BRICK_SIZE;
|
|
|
|
_engine->_scene->_currentSceneIdx = SCENE_CEILING_GRID_FADE_1;
|
|
_engine->_scene->_needChangeScene = LBA1SceneId::Citadel_Island_Prison;
|
|
_engine->_sceneLoopState = SceneLoopState::Continue;
|
|
_engine->_scene->_mecaPenguinIdx = -1;
|
|
_engine->_menuOptions->canShowCredits = false;
|
|
|
|
_inventoryNumLeafs = 0;
|
|
_inventoryNumLeafsBox = 2;
|
|
_inventoryMagicPoints = 0;
|
|
_inventoryNumKashes = 0;
|
|
_inventoryNumKeys = 0;
|
|
_inventoryNumGas = 0;
|
|
|
|
_engine->_actor->_cropBottomScreen = 0;
|
|
|
|
_magicLevelIdx = 0;
|
|
_usingSabre = false;
|
|
|
|
_gameChapter = 0;
|
|
|
|
_engine->_scene->_sceneTextBank = TextBankId::Options_and_menus;
|
|
_engine->_scene->_currentlyFollowedActor = 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;
|
|
}
|
|
|
|
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) {
|
|
warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS, numGameFlags);
|
|
return false;
|
|
}
|
|
for (uint8 i = 0; i < numGameFlags; ++i) {
|
|
setGameFlag(i, file->readByte());
|
|
}
|
|
_engine->_scene->_needChangeScene = file->readByte(); // scene index
|
|
_gameChapter = 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->_newHeroPos.x = file->readSint16LE();
|
|
_engine->_scene->_newHeroPos.y = file->readSint16LE();
|
|
_engine->_scene->_newHeroPos.z = file->readSint16LE();
|
|
_engine->_scene->_sceneHero->_angle = ToAngle(file->readSint16LE());
|
|
_engine->_actor->_previousHeroAngle = _engine->_scene->_sceneHero->_angle;
|
|
_engine->_scene->_sceneHero->_body = (BodyType)file->readByte();
|
|
|
|
const byte numHolomapFlags = file->readByte(); // number of holomap locations
|
|
if (numHolomapFlags != NUM_LOCATIONS) {
|
|
warning("Failed to load holomapflags. Got %u, expected %i", numHolomapFlags, NUM_LOCATIONS);
|
|
return false;
|
|
}
|
|
file->read(_holomapFlags, NUM_LOCATIONS);
|
|
|
|
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());
|
|
_usingSabre = file->readByte();
|
|
|
|
if (saveFileVersion == 4) {
|
|
// the time the game was played
|
|
file->readUint32LE();
|
|
file->readUint32LE();
|
|
}
|
|
|
|
_engine->_scene->_currentSceneIdx = SCENE_CEILING_GRID_FADE_1;
|
|
_engine->_scene->_heroPositionType = ScenePositionType::kReborn;
|
|
return true;
|
|
}
|
|
|
|
bool GameState::saveGame(Common::WriteStream *file) {
|
|
debug(2, "Save game");
|
|
if (_engine->_menuOptions->_saveGameName[0] == '\0') {
|
|
Common::strlcpy(_engine->_menuOptions->_saveGameName, "TwinEngineSave", sizeof(_engine->_menuOptions->_saveGameName));
|
|
}
|
|
|
|
int32 sceneIdx = _engine->_scene->_currentSceneIdx;
|
|
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);
|
|
for (uint8 i = 0; i < NUM_GAME_FLAGS; ++i) {
|
|
file->writeByte(hasGameFlag(i));
|
|
}
|
|
file->writeByte(sceneIdx);
|
|
file->writeByte(_gameChapter);
|
|
file->writeByte((byte)_engine->_actor->_heroBehaviour);
|
|
file->writeByte(_engine->_scene->_sceneHero->_life);
|
|
file->writeSint16LE(_inventoryNumKashes);
|
|
file->writeByte(_magicLevelIdx);
|
|
file->writeByte(_inventoryMagicPoints);
|
|
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->_newHeroPos.x);
|
|
file->writeSint16LE(_engine->_scene->_newHeroPos.y);
|
|
file->writeSint16LE(_engine->_scene->_newHeroPos.z);
|
|
file->writeSint16LE(FromAngle(_engine->_scene->_sceneHero->_angle));
|
|
file->writeByte((uint8)_engine->_scene->_sceneHero->_body);
|
|
|
|
// number of holomap locations
|
|
file->writeByte(NUM_LOCATIONS);
|
|
file->write(_holomapFlags, NUM_LOCATIONS);
|
|
|
|
file->writeByte(_inventoryNumGas);
|
|
|
|
// number of inventory items
|
|
file->writeByte(NUM_INVENTORY_ITEMS);
|
|
file->write(_inventoryFlags, NUM_INVENTORY_ITEMS);
|
|
|
|
file->writeByte(_inventoryNumLeafs);
|
|
file->writeByte(_usingSabre ? 1 : 0);
|
|
file->writeByte(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
void GameState::setGameFlag(uint8 index, uint8 value) {
|
|
if (_gameStateFlags[index] == value) {
|
|
return;
|
|
}
|
|
debug(2, "Set gameStateFlags[%u]=%u", index, value);
|
|
_gameStateFlags[index] = value;
|
|
if (!value) {
|
|
return;
|
|
}
|
|
|
|
if ((index == GAMEFLAG_VIDEO_BAFFE || index == GAMEFLAG_VIDEO_BAFFE2 || index == GAMEFLAG_VIDEO_BAFFE3 || index == GAMEFLAG_VIDEO_BAFFE5) &&
|
|
_gameStateFlags[GAMEFLAG_VIDEO_BAFFE] != 0 && _gameStateFlags[GAMEFLAG_VIDEO_BAFFE2] != 0 && _gameStateFlags[GAMEFLAG_VIDEO_BAFFE3] != 0 && _gameStateFlags[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::processFoundItem(InventoryItems item) {
|
|
ScopedEngineFreeze freeze(_engine);
|
|
_engine->_grid->centerOnActor(_engine->_scene->_sceneHero);
|
|
|
|
_engine->exitSceneryView();
|
|
// Hide hero in scene
|
|
_engine->_scene->_sceneHero->_staticFlags.bIsHidden = 1;
|
|
_engine->_redraw->redrawEngineActions(true);
|
|
_engine->_scene->_sceneHero->_staticFlags.bIsHidden = 0;
|
|
|
|
_engine->saveFrontBuffer();
|
|
|
|
IVec3 itemCamera;
|
|
itemCamera.x = _engine->_grid->_newCamera.x * BRICK_SIZE;
|
|
itemCamera.y = _engine->_grid->_newCamera.y * BRICK_HEIGHT;
|
|
itemCamera.z = _engine->_grid->_newCamera.z * BRICK_SIZE;
|
|
|
|
BodyData &bodyData = _engine->_resources->_bodyData[_engine->_scene->_sceneHero->_entity];
|
|
const IVec3 bodyPos = _engine->_scene->_sceneHero->_pos - itemCamera;
|
|
Common::Rect modelRect;
|
|
_engine->_renderer->renderIsoModel(bodyPos, ANGLE_0, ANGLE_45, ANGLE_0, bodyData, modelRect);
|
|
_engine->_interface->setClip(modelRect);
|
|
|
|
const int32 itemX = (_engine->_scene->_sceneHero->_pos.x + BRICK_HEIGHT) / BRICK_SIZE;
|
|
int32 itemY = _engine->_scene->_sceneHero->_pos.y / BRICK_HEIGHT;
|
|
if (_engine->_scene->_sceneHero->brickShape() != ShapeType::kNone) {
|
|
itemY++;
|
|
}
|
|
const int32 itemZ = (_engine->_scene->_sceneHero->_pos.z + BRICK_HEIGHT) / BRICK_SIZE;
|
|
|
|
_engine->_grid->drawOverModelActor(itemX, itemY, itemZ);
|
|
|
|
IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(bodyPos);
|
|
projPos.y -= 150;
|
|
|
|
const int32 boxTopLeftX = projPos.x - 65;
|
|
const int32 boxTopLeftY = projPos.y - 65;
|
|
const int32 boxBottomRightX = projPos.x + 65;
|
|
const int32 boxBottomRightY = projPos.y + 65;
|
|
const Common::Rect boxRect(boxTopLeftX, boxTopLeftY, boxBottomRightX, boxBottomRightY);
|
|
_engine->_sound->playSample(Samples::BigItemFound);
|
|
|
|
// process vox play
|
|
_engine->_music->stopMusic();
|
|
_engine->_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
|
|
|
|
_engine->_interface->resetClip();
|
|
_engine->_text->initItemFoundText(item);
|
|
_engine->_text->initDialogueBox();
|
|
|
|
ProgressiveTextState textState = ProgressiveTextState::ContinueRunning;
|
|
|
|
_engine->_text->initVoxToPlayTextId((TextId)item);
|
|
|
|
const int32 bodyAnimIdx = _engine->_animations->getBodyAnimIndex(AnimationTypes::kFoundItem);
|
|
const AnimData ¤tAnimData = _engine->_resources->_animData[bodyAnimIdx];
|
|
|
|
AnimTimerDataStruct tmpAnimTimer = _engine->_scene->_sceneHero->_animTimerData;
|
|
|
|
_engine->_animations->stockAnimation(bodyData, &_engine->_scene->_sceneHero->_animTimerData);
|
|
|
|
uint currentAnimState = 0;
|
|
|
|
_engine->_redraw->_numOfRedrawBox = 0;
|
|
|
|
ScopedKeyMap uiKeyMap(_engine, uiKeyMapId);
|
|
int16 itemAngle = ANGLE_0;
|
|
for (;;) {
|
|
FrameMarker frame(_engine, 66);
|
|
_engine->_interface->resetClip();
|
|
_engine->_redraw->_currNumOfRedrawBox = 0;
|
|
_engine->_redraw->blitBackgroundAreas();
|
|
_engine->_interface->drawTransparentBox(boxRect, 4);
|
|
|
|
_engine->_interface->setClip(boxRect);
|
|
|
|
itemAngle += ANGLE_2;
|
|
|
|
_engine->_renderer->renderInventoryItem(_engine->_renderer->_projPos.x, _engine->_renderer->_projPos.y, _engine->_resources->_inventoryTable[item], itemAngle, 10000);
|
|
|
|
_engine->_menu->drawRectBorders(boxRect);
|
|
_engine->_redraw->addRedrawArea(boxRect);
|
|
_engine->_interface->resetClip();
|
|
initEngineProjections();
|
|
|
|
if (_engine->_animations->setModelAnimation(currentAnimState, currentAnimData, bodyData, &_engine->_scene->_sceneHero->_animTimerData)) {
|
|
currentAnimState++; // keyframe
|
|
if (currentAnimState >= currentAnimData.getNumKeyframes()) {
|
|
currentAnimState = currentAnimData.getLoopFrame();
|
|
}
|
|
}
|
|
|
|
_engine->_renderer->renderIsoModel(bodyPos, ANGLE_0, ANGLE_45, ANGLE_0, bodyData, modelRect);
|
|
_engine->_interface->setClip(modelRect);
|
|
_engine->_grid->drawOverModelActor(itemX, itemY, itemZ);
|
|
_engine->_redraw->addRedrawArea(modelRect);
|
|
|
|
if (textState == ProgressiveTextState::ContinueRunning) {
|
|
_engine->_interface->resetClip();
|
|
textState = _engine->_text->updateProgressiveText();
|
|
} else {
|
|
_engine->_text->fadeInRemainingChars();
|
|
}
|
|
|
|
_engine->_redraw->flipRedrawAreas();
|
|
|
|
_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);
|
|
|
|
_engine->_lbaTime++;
|
|
}
|
|
|
|
while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
|
|
FrameMarker frame(_engine);
|
|
_engine->readKeys();
|
|
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
initEngineProjections();
|
|
_engine->_text->initSceneTextBank();
|
|
_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
|
|
|
|
_engine->_scene->_sceneHero->_animTimerData = tmpAnimTimer;
|
|
_engine->_interface->resetClip();
|
|
}
|
|
|
|
void GameState::processGameChoices(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 < _numChoices; i++) {
|
|
_gameChoicesSettings.addButton(_gameChoices[i], 0);
|
|
}
|
|
|
|
_engine->_text->drawAskQuestion(choiceIdx);
|
|
|
|
_engine->_menu->processMenu(&_gameChoicesSettings, false);
|
|
const int16 activeButton = _gameChoicesSettings.getActiveButton();
|
|
_choiceAnswer = _gameChoices[activeButton];
|
|
|
|
// get right VOX entry index
|
|
if (_engine->_text->initVoxToPlayTextId(_choiceAnswer)) {
|
|
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->_lbaTime;
|
|
|
|
_engine->exitSceneryView();
|
|
// workaround to fix hero redraw after drowning
|
|
_engine->_scene->_sceneHero->_staticFlags.bIsHidden = 1;
|
|
_engine->_redraw->redrawEngineActions(true);
|
|
_engine->_scene->_sceneHero->_staticFlags.bIsHidden = 0;
|
|
|
|
// TODO: inSceneryView
|
|
_engine->setPalette(_engine->_screens->_paletteRGBA);
|
|
_engine->saveFrontBuffer();
|
|
BodyData gameOverPtr;
|
|
if (!gameOverPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_GAMEOVERMDL, _engine->isLBA1())) {
|
|
return;
|
|
}
|
|
|
|
_engine->_sound->stopSamples();
|
|
_engine->_music->stopMidiMusic(); // stop fade music
|
|
_engine->_renderer->setCameraPosition(_engine->width() / 2, _engine->height() / 2, 128, 200, 200);
|
|
int32 startLbaTime = _engine->_lbaTime;
|
|
const Common::Rect &rect = _engine->centerOnScreen(_engine->width() / 2, _engine->height() / 2);
|
|
_engine->_interface->setClip(rect);
|
|
|
|
Common::Rect dummy;
|
|
while (!_engine->_input->toggleAbortAction() && (_engine->_lbaTime - startLbaTime) <= TO_SECONDS(10)) {
|
|
FrameMarker frame(_engine, 66);
|
|
_engine->readKeys();
|
|
if (_engine->shouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
const int32 avg = _engine->_collision->getAverageValue(40000, 3200, 500, _engine->_lbaTime - startLbaTime);
|
|
const int32 cdot = _engine->_screens->lerp(1, ANGLE_360, 100, (_engine->_lbaTime - startLbaTime) % 100);
|
|
|
|
_engine->blitWorkToFront(rect);
|
|
_engine->_renderer->setCameraAngle(0, 0, 0, 0, -cdot, 0, avg);
|
|
_engine->_renderer->renderIsoModel(0, 0, 0, ANGLE_0, ANGLE_0, ANGLE_0, gameOverPtr, dummy);
|
|
|
|
_engine->_lbaTime++;
|
|
}
|
|
|
|
_engine->_sound->playSample(Samples::Explode);
|
|
_engine->blitWorkToFront(rect);
|
|
_engine->_renderer->setCameraAngle(0, 0, 0, 0, 0, 0, 3200);
|
|
_engine->_renderer->renderIsoModel(0, 0, 0, ANGLE_0, ANGLE_0, ANGLE_0, gameOverPtr, dummy);
|
|
|
|
_engine->delaySkip(2000);
|
|
|
|
_engine->_interface->resetClip();
|
|
_engine->restoreFrontBuffer();
|
|
initEngineProjections();
|
|
|
|
_engine->_lbaTime = 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) {
|
|
_inventoryNumKashes = CLIP<int16>(value, 0, 999);
|
|
if (_engine->_gameState->_inventoryNumKashes >= 500) {
|
|
_engine->unlockAchievement("LBA_ACH_011");
|
|
}
|
|
return _inventoryNumKashes;
|
|
}
|
|
|
|
int16 GameState::setKeys(int16 value) {
|
|
_inventoryNumKeys = MAX<int16>(0, value);
|
|
return _inventoryNumKeys;
|
|
}
|
|
|
|
void GameState::addKeys(int16 val) {
|
|
setKeys(_inventoryNumKeys + val);
|
|
}
|
|
|
|
void GameState::addKashes(int16 val) {
|
|
setKashes(_inventoryNumKashes + val);
|
|
}
|
|
|
|
int16 GameState::setMagicPoints(int16 val) {
|
|
_inventoryMagicPoints = val;
|
|
if (_inventoryMagicPoints > _magicLevelIdx * 20) {
|
|
_inventoryMagicPoints = _magicLevelIdx * 20;
|
|
} else if (_inventoryMagicPoints < 0) {
|
|
_inventoryMagicPoints = 0;
|
|
}
|
|
return _inventoryMagicPoints;
|
|
}
|
|
|
|
int16 GameState::setMaxMagicPoints() {
|
|
_inventoryMagicPoints = _magicLevelIdx * 20;
|
|
return _inventoryMagicPoints;
|
|
}
|
|
|
|
void GameState::addMagicPoints(int16 val) {
|
|
setMagicPoints(_inventoryMagicPoints + 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(&_gameStateFlags[0], &_gameStateFlags[NUM_GAME_FLAGS], 0);
|
|
}
|
|
|
|
uint8 GameState::hasGameFlag(uint8 index) const {
|
|
debug(6, "Query gameStateFlags[%u]=%u", index, _gameStateFlags[index]);
|
|
return _gameStateFlags[index];
|
|
}
|
|
|
|
} // namespace TwinE
|