1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "twine/scene/gamestate.h"
24 #include "common/file.h"
25 #include "common/rect.h"
26 #include "common/str.h"
27 #include "common/system.h"
28 #include "common/textconsole.h"
29 #include "common/util.h"
30 #include "twine/audio/music.h"
31 #include "twine/audio/sound.h"
32 #include "twine/input.h"
33 #include "twine/menu/interface.h"
34 #include "twine/menu/menu.h"
35 #include "twine/menu/menuoptions.h"
36 #include "twine/renderer/redraw.h"
37 #include "twine/renderer/renderer.h"
38 #include "twine/renderer/screens.h"
39 #include "twine/resources/resources.h"
40 #include "twine/scene/actor.h"
41 #include "twine/scene/animations.h"
42 #include "twine/scene/collision.h"
43 #include "twine/scene/extra.h"
44 #include "twine/scene/grid.h"
45 #include "twine/scene/scene.h"
46 #include "twine/shared.h"
47 #include "twine/text.h"
48 #include "twine/twine.h"
49
50 namespace TwinE {
51
GameState(TwinEEngine * engine)52 GameState::GameState(TwinEEngine *engine) : _engine(engine) {
53 clearGameFlags();
54 Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
55 Common::fill(&_holomapFlags[0], &_holomapFlags[NUM_LOCATIONS], 0);
56 Common::fill(&_gameChoices[0], &_gameChoices[10], TextId::kNone);
57 }
58
initEngineProjections()59 void GameState::initEngineProjections() {
60 _engine->_renderer->setOrthoProjection(_engine->width() / 2 - 9, _engine->height() / 2, 512);
61 _engine->_renderer->setBaseTranslation(0, 0, 0);
62 _engine->_renderer->setBaseRotation(ANGLE_0, ANGLE_0, ANGLE_0);
63 _engine->_renderer->setLightVector(_engine->_scene->_alphaLight, _engine->_scene->_betaLight, ANGLE_0);
64 }
65
initGameStateVars()66 void GameState::initGameStateVars() {
67 debug(2, "Init game state variables");
68 _engine->_extra->resetExtras();
69
70 for (int32 i = 0; i < OVERLAY_MAX_ENTRIES; i++) {
71 _engine->_redraw->overlayList[i].info0 = -1;
72 }
73
74 for (int32 i = 0; i < ARRAYSIZE(_engine->_scene->_sceneFlags); i++) {
75 _engine->_scene->_sceneFlags[i] = 0;
76 }
77
78 clearGameFlags();
79 Common::fill(&_inventoryFlags[0], &_inventoryFlags[NUM_INVENTORY_ITEMS], 0);
80
81 _engine->_scene->initSceneVars();
82
83 Common::fill(&_holomapFlags[0], &_holomapFlags[NUM_LOCATIONS], 0);
84 }
85
initHeroVars()86 void GameState::initHeroVars() {
87 _engine->_actor->resetActor(OWN_ACTOR_SCENE_INDEX); // reset Hero
88
89 _magicBallIdx = -1;
90
91 _inventoryNumLeafsBox = 2;
92 _inventoryNumLeafs = 2;
93 _inventoryNumKashes = 0;
94 _inventoryNumKeys = 0;
95 _inventoryMagicPoints = 0;
96
97 _usingSabre = false;
98
99 _engine->_scene->_sceneHero->_body = BodyType::btNormal;
100 _engine->_scene->_sceneHero->setLife(kActorMaxLife);
101 _engine->_scene->_sceneHero->_talkColor = COLOR_BRIGHT_BLUE;
102 }
103
initEngineVars()104 void GameState::initEngineVars() {
105 debug(2, "Init engine variables");
106 _engine->_interface->resetClip();
107
108 _engine->_scene->_alphaLight = ANGLE_315;
109 _engine->_scene->_betaLight = ANGLE_334;
110 initEngineProjections();
111 initGameStateVars();
112 initHeroVars();
113
114 _engine->_scene->_newHeroPos.x = 16 * BRICK_SIZE;
115 _engine->_scene->_newHeroPos.y = 24 * BRICK_HEIGHT;
116 _engine->_scene->_newHeroPos.z = 16 * BRICK_SIZE;
117
118 _engine->_scene->_currentSceneIdx = SCENE_CEILING_GRID_FADE_1;
119 _engine->_scene->_needChangeScene = LBA1SceneId::Citadel_Island_Prison;
120 _engine->_quitGame = -1;
121 _engine->_scene->_mecaPenguinIdx = -1;
122 _engine->_menuOptions->canShowCredits = false;
123
124 _inventoryNumLeafs = 0;
125 _inventoryNumLeafsBox = 2;
126 _inventoryMagicPoints = 0;
127 _inventoryNumKashes = 0;
128 _inventoryNumKeys = 0;
129 _inventoryNumGas = 0;
130
131 _engine->_actor->_cropBottomScreen = 0;
132
133 _magicLevelIdx = 0;
134 _usingSabre = false;
135
136 _gameChapter = 0;
137
138 _engine->_scene->_sceneTextBank = TextBankId::Options_and_menus;
139 _engine->_scene->_currentlyFollowedActor = OWN_ACTOR_SCENE_INDEX;
140 _engine->_actor->_heroBehaviour = HeroBehaviourType::kNormal;
141 _engine->_actor->_previousHeroAngle = 0;
142 _engine->_actor->_previousHeroBehaviour = HeroBehaviourType::kNormal;
143 }
144
145 // http://lbafileinfo.kazekr.net/index.php?title=LBA1:Savegame
loadGame(Common::SeekableReadStream * file)146 bool GameState::loadGame(Common::SeekableReadStream *file) {
147 if (file == nullptr) {
148 return false;
149 }
150
151 debug(2, "Load game");
152 const byte saveFileVersion = file->readByte();
153 // 4 is dotemu enhanced version of lba1
154 if (saveFileVersion != 3 && saveFileVersion != 4) {
155 warning("Could not load savegame - wrong magic byte");
156 return false;
157 }
158
159 initEngineVars();
160
161 int playerNameIdx = 0;
162 do {
163 const byte c = file->readByte();
164 _engine->_menuOptions->_saveGameName[playerNameIdx++] = c;
165 if (c == '\0') {
166 break;
167 }
168 if (playerNameIdx >= ARRAYSIZE(_engine->_menuOptions->_saveGameName)) {
169 warning("Failed to load savegame. Invalid playername.");
170 return false;
171 }
172 } while (true);
173
174 byte numGameFlags = file->readByte();
175 if (numGameFlags != NUM_GAME_FLAGS) {
176 warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS, numGameFlags);
177 return false;
178 }
179 for (uint8 i = 0; i < numGameFlags; ++i) {
180 setGameFlag(i, file->readByte());
181 }
182 _engine->_scene->_needChangeScene = file->readByte(); // scene index
183 _gameChapter = file->readByte();
184
185 _engine->_actor->_heroBehaviour = (HeroBehaviourType)file->readByte();
186 _engine->_actor->_previousHeroBehaviour = _engine->_actor->_heroBehaviour;
187 _engine->_scene->_sceneHero->setLife(file->readByte());
188 setKashes(file->readSint16LE());
189 _magicLevelIdx = file->readByte();
190 setMagicPoints(file->readByte());
191 setLeafBoxes(file->readByte());
192 _engine->_scene->_newHeroPos.x = file->readSint16LE();
193 _engine->_scene->_newHeroPos.y = file->readSint16LE();
194 _engine->_scene->_newHeroPos.z = file->readSint16LE();
195 _engine->_scene->_sceneHero->_angle = ToAngle(file->readSint16LE());
196 _engine->_actor->_previousHeroAngle = _engine->_scene->_sceneHero->_angle;
197 _engine->_scene->_sceneHero->_body = (BodyType)file->readByte();
198
199 const byte numHolomapFlags = file->readByte(); // number of holomap locations
200 if (numHolomapFlags != NUM_LOCATIONS) {
201 warning("Failed to load holomapflags. Got %u, expected %i", numHolomapFlags, NUM_LOCATIONS);
202 return false;
203 }
204 file->read(_holomapFlags, NUM_LOCATIONS);
205
206 setGas(file->readByte());
207
208 const byte numInventoryFlags = file->readByte(); // number of used inventory items, always 28
209 if (numInventoryFlags != NUM_INVENTORY_ITEMS) {
210 warning("Failed to load inventoryFlags. Got %u, expected %i", numInventoryFlags, NUM_INVENTORY_ITEMS);
211 return false;
212 }
213 file->read(_inventoryFlags, NUM_INVENTORY_ITEMS);
214
215 setLeafs(file->readByte());
216 _usingSabre = file->readByte();
217
218 if (saveFileVersion == 4) {
219 // the time the game was played
220 file->readUint32LE();
221 file->readUint32LE();
222 }
223
224 _engine->_scene->_currentSceneIdx = SCENE_CEILING_GRID_FADE_1;
225 _engine->_scene->_heroPositionType = ScenePositionType::kReborn;
226 return true;
227 }
228
saveGame(Common::WriteStream * file)229 bool GameState::saveGame(Common::WriteStream *file) {
230 debug(2, "Save game");
231 if (_engine->_menuOptions->_saveGameName[0] == '\0') {
232 Common::strlcpy(_engine->_menuOptions->_saveGameName, "TwinEngineSave", sizeof(_engine->_menuOptions->_saveGameName));
233 }
234
235 int32 sceneIdx = _engine->_scene->_currentSceneIdx;
236 if (sceneIdx == Polar_Island_end_scene || sceneIdx == Citadel_Island_end_sequence_1 || sceneIdx == Citadel_Island_end_sequence_2 || sceneIdx == Credits_List_Sequence) {
237 /* inventoryMagicPoints = 0x50 */
238 /* herobehaviour = 0 */
239 /* newheropos.x = 0xffff */
240 sceneIdx = Polar_Island_Final_Battle;
241 }
242
243 file->writeByte(0x03);
244 file->writeString(_engine->_menuOptions->_saveGameName);
245 file->writeByte('\0');
246 file->writeByte(NUM_GAME_FLAGS);
247 for (uint8 i = 0; i < NUM_GAME_FLAGS; ++i) {
248 file->writeByte(hasGameFlag(i));
249 }
250 file->writeByte(sceneIdx);
251 file->writeByte(_gameChapter);
252 file->writeByte((byte)_engine->_actor->_heroBehaviour);
253 file->writeByte(_engine->_scene->_sceneHero->_life);
254 file->writeSint16LE(_inventoryNumKashes);
255 file->writeByte(_magicLevelIdx);
256 file->writeByte(_inventoryMagicPoints);
257 file->writeByte(_inventoryNumLeafsBox);
258 // we don't save the whole scene state - so we have to make sure that the hero is
259 // respawned at the start of the scene - and not at its current position
260 file->writeSint16LE(_engine->_scene->_newHeroPos.x);
261 file->writeSint16LE(_engine->_scene->_newHeroPos.y);
262 file->writeSint16LE(_engine->_scene->_newHeroPos.z);
263 file->writeSint16LE(FromAngle(_engine->_scene->_sceneHero->_angle));
264 file->writeByte((uint8)_engine->_scene->_sceneHero->_body);
265
266 // number of holomap locations
267 file->writeByte(NUM_LOCATIONS);
268 file->write(_holomapFlags, NUM_LOCATIONS);
269
270 file->writeByte(_inventoryNumGas);
271
272 // number of inventory items
273 file->writeByte(NUM_INVENTORY_ITEMS);
274 file->write(_inventoryFlags, NUM_INVENTORY_ITEMS);
275
276 file->writeByte(_inventoryNumLeafs);
277 file->writeByte(_usingSabre ? 1 : 0);
278 file->writeByte(0);
279
280 return true;
281 }
282
setGameFlag(uint8 index,uint8 value)283 void GameState::setGameFlag(uint8 index, uint8 value) {
284 if (_gameStateFlags[index] == value) {
285 return;
286 }
287 debug(2, "Set gameStateFlags[%u]=%u", index, value);
288 _gameStateFlags[index] = value;
289 if (!value) {
290 return;
291 }
292
293 if ((index == GAMEFLAG_VIDEO_BAFFE || index == GAMEFLAG_VIDEO_BAFFE2 || index == GAMEFLAG_VIDEO_BAFFE3 || index == GAMEFLAG_VIDEO_BAFFE5) &&
294 _gameStateFlags[GAMEFLAG_VIDEO_BAFFE] != 0 && _gameStateFlags[GAMEFLAG_VIDEO_BAFFE2] != 0 && _gameStateFlags[GAMEFLAG_VIDEO_BAFFE3] != 0 && _gameStateFlags[GAMEFLAG_VIDEO_BAFFE5] != 0) {
295 // all 4 slap videos
296 _engine->unlockAchievement("LBA_ACH_012");
297 } else if (index == GAMEFLAG_VIDEO_BATEAU2) {
298 // second video of ferry trip
299 _engine->unlockAchievement("LBA_ACH_010");
300 } else if (index == (uint8)InventoryItems::kiUseSabre) {
301 _engine->unlockAchievement("LBA_ACH_002");
302 } else if (index == (uint8)InventoryItems::kBottleOfSyrup) {
303 _engine->unlockAchievement("LBA_ACH_007");
304 }
305 }
306
processFoundItem(InventoryItems item)307 void GameState::processFoundItem(InventoryItems item) {
308 ScopedEngineFreeze freeze(_engine);
309 _engine->_grid->centerOnActor(_engine->_scene->_sceneHero);
310
311 _engine->exitSceneryView();
312 // Hide hero in scene
313 _engine->_scene->_sceneHero->_staticFlags.bIsHidden = 1;
314 _engine->_redraw->redrawEngineActions(true);
315 _engine->_scene->_sceneHero->_staticFlags.bIsHidden = 0;
316
317 _engine->saveFrontBuffer();
318
319 const int32 itemCameraX = _engine->_grid->_newCamera.x * BRICK_SIZE;
320 const int32 itemCameraY = _engine->_grid->_newCamera.y * BRICK_HEIGHT;
321 const int32 itemCameraZ = _engine->_grid->_newCamera.z * BRICK_SIZE;
322
323 BodyData &bodyData = _engine->_resources->_bodyData[_engine->_scene->_sceneHero->_entity];
324 const int32 bodyX = _engine->_scene->_sceneHero->_pos.x - itemCameraX;
325 const int32 bodyY = _engine->_scene->_sceneHero->_pos.y - itemCameraY;
326 const int32 bodyZ = _engine->_scene->_sceneHero->_pos.z - itemCameraZ;
327 Common::Rect modelRect;
328 _engine->_renderer->renderIsoModel(bodyX, bodyY, bodyZ, ANGLE_0, ANGLE_45, ANGLE_0, bodyData, modelRect);
329 _engine->_interface->setClip(modelRect);
330
331 const int32 itemX = (_engine->_scene->_sceneHero->_pos.x + BRICK_HEIGHT) / BRICK_SIZE;
332 int32 itemY = _engine->_scene->_sceneHero->_pos.y / BRICK_HEIGHT;
333 if (_engine->_scene->_sceneHero->brickShape() != ShapeType::kNone) {
334 itemY++;
335 }
336 const int32 itemZ = (_engine->_scene->_sceneHero->_pos.z + BRICK_HEIGHT) / BRICK_SIZE;
337
338 _engine->_grid->drawOverModelActor(itemX, itemY, itemZ);
339
340 IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(bodyX, bodyY, bodyZ);
341 projPos.y -= 150;
342
343 const int32 boxTopLeftX = projPos.x - 65;
344 const int32 boxTopLeftY = projPos.y - 65;
345 const int32 boxBottomRightX = projPos.x + 65;
346 const int32 boxBottomRightY = projPos.y + 65;
347 const Common::Rect boxRect(boxTopLeftX, boxTopLeftY, boxBottomRightX, boxBottomRightY);
348 _engine->_sound->playSample(Samples::BigItemFound);
349
350 // process vox play
351 _engine->_music->stopMusic();
352 _engine->_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
353
354 _engine->_interface->resetClip();
355 _engine->_text->initItemFoundText(item);
356 _engine->_text->initDialogueBox();
357
358 ProgressiveTextState textState = ProgressiveTextState::ContinueRunning;
359
360 _engine->_text->initVoxToPlayTextId((TextId)item);
361
362 const int32 bodyAnimIdx = _engine->_animations->getBodyAnimIndex(AnimationTypes::kFoundItem);
363 const AnimData ¤tAnimData = _engine->_resources->_animData[bodyAnimIdx];
364
365 AnimTimerDataStruct tmpAnimTimer = _engine->_scene->_sceneHero->_animTimerData;
366
367 _engine->_animations->stockAnimation(bodyData, &_engine->_scene->_sceneHero->_animTimerData);
368
369 uint currentAnimState = 0;
370
371 _engine->_redraw->_numOfRedrawBox = 0;
372
373 ScopedKeyMap uiKeyMap(_engine, uiKeyMapId);
374 int16 itemAngle = ANGLE_0;
375 for (;;) {
376 FrameMarker frame(_engine, 66);
377 _engine->_interface->resetClip();
378 _engine->_redraw->_currNumOfRedrawBox = 0;
379 _engine->_redraw->blitBackgroundAreas();
380 _engine->_interface->drawTransparentBox(boxRect, 4);
381
382 _engine->_interface->setClip(boxRect);
383
384 itemAngle += ANGLE_2;
385
386 _engine->_renderer->renderInventoryItem(_engine->_renderer->_projPos.x, _engine->_renderer->_projPos.y, _engine->_resources->_inventoryTable[item], itemAngle, 10000);
387
388 _engine->_menu->drawRectBorders(boxRect);
389 _engine->_redraw->addRedrawArea(boxRect);
390 _engine->_interface->resetClip();
391 initEngineProjections();
392
393 if (_engine->_animations->setModelAnimation(currentAnimState, currentAnimData, bodyData, &_engine->_scene->_sceneHero->_animTimerData)) {
394 currentAnimState++; // keyframe
395 if (currentAnimState >= currentAnimData.getNumKeyframes()) {
396 currentAnimState = currentAnimData.getLoopFrame();
397 }
398 }
399
400 _engine->_renderer->renderIsoModel(bodyX, bodyY, bodyZ, ANGLE_0, ANGLE_45, ANGLE_0, bodyData, modelRect);
401 _engine->_interface->setClip(modelRect);
402 _engine->_grid->drawOverModelActor(itemX, itemY, itemZ);
403 _engine->_redraw->addRedrawArea(modelRect);
404
405 if (textState == ProgressiveTextState::ContinueRunning) {
406 _engine->_interface->resetClip();
407 textState = _engine->_text->updateProgressiveText();
408 } else {
409 _engine->_text->fadeInRemainingChars();
410 }
411
412 _engine->_redraw->flipRedrawAreas();
413
414 _engine->readKeys();
415 if (_engine->_input->toggleAbortAction()) {
416 _engine->_text->stopVox(_engine->_text->_currDialTextEntry);
417 break;
418 }
419
420 if (_engine->_input->toggleActionIfActive(TwinEActionType::UINextPage)) {
421 if (textState == ProgressiveTextState::End) {
422 _engine->_text->stopVox(_engine->_text->_currDialTextEntry);
423 break;
424 }
425 if (textState == ProgressiveTextState::NextPage) {
426 textState = ProgressiveTextState::ContinueRunning;
427 }
428 }
429
430 _engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry);
431
432 _engine->_lbaTime++;
433 }
434
435 while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
436 FrameMarker frame(_engine);
437 _engine->readKeys();
438 if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
439 break;
440 }
441 }
442
443 initEngineProjections();
444 _engine->_text->initSceneTextBank();
445 _engine->_text->stopVox(_engine->_text->_currDialTextEntry);
446
447 _engine->_scene->_sceneHero->_animTimerData = tmpAnimTimer;
448 _engine->_interface->resetClip();
449 }
450
processGameChoices(TextId choiceIdx)451 void GameState::processGameChoices(TextId choiceIdx) {
452 _engine->saveFrontBuffer();
453
454 _gameChoicesSettings.reset();
455 _gameChoicesSettings.setTextBankId((TextBankId)((int)_engine->_scene->_sceneTextBank + (int)TextBankId::Citadel_Island));
456
457 // filled via script
458 for (int32 i = 0; i < _numChoices; i++) {
459 _gameChoicesSettings.addButton(_gameChoices[i], 0);
460 }
461
462 _engine->_text->drawAskQuestion(choiceIdx);
463
464 _engine->_menu->processMenu(&_gameChoicesSettings, false);
465 const int16 activeButton = _gameChoicesSettings.getActiveButton();
466 _choiceAnswer = _gameChoices[activeButton];
467
468 // get right VOX entry index
469 if (_engine->_text->initVoxToPlayTextId(_choiceAnswer)) {
470 while (_engine->_text->playVoxSimple(_engine->_text->_currDialTextEntry)) {
471 FrameMarker frame(_engine);
472 if (_engine->shouldQuit()) {
473 break;
474 }
475 }
476 _engine->_text->stopVox(_engine->_text->_currDialTextEntry);
477
478 _engine->_text->_hasHiddenVox = false;
479 _engine->_text->_voxHiddenIndex = 0;
480 }
481 }
482
processGameoverAnimation()483 void GameState::processGameoverAnimation() {
484 const int32 tmpLbaTime = _engine->_lbaTime;
485
486 _engine->exitSceneryView();
487 // workaround to fix hero redraw after drowning
488 _engine->_scene->_sceneHero->_staticFlags.bIsHidden = 1;
489 _engine->_redraw->redrawEngineActions(true);
490 _engine->_scene->_sceneHero->_staticFlags.bIsHidden = 0;
491
492 // TODO: inSceneryView
493 _engine->setPalette(_engine->_screens->_paletteRGBA);
494 _engine->saveFrontBuffer();
495 BodyData gameOverPtr;
496 if (!gameOverPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_GAMEOVERMDL, _engine->isLBA1())) {
497 return;
498 }
499
500 _engine->_sound->stopSamples();
501 _engine->_music->stopMidiMusic(); // stop fade music
502 _engine->_renderer->setCameraPosition(_engine->width() / 2, _engine->height() / 2, 128, 200, 200);
503 int32 startLbaTime = _engine->_lbaTime;
504 const Common::Rect &rect = _engine->centerOnScreen(_engine->width() / 2, _engine->height() / 2);
505 _engine->_interface->setClip(rect);
506
507 Common::Rect dummy;
508 while (!_engine->_input->toggleAbortAction() && (_engine->_lbaTime - startLbaTime) <= 500) {
509 FrameMarker frame(_engine, 66);
510 _engine->readKeys();
511 if (_engine->shouldQuit()) {
512 return;
513 }
514
515 const int32 avg = _engine->_collision->getAverageValue(40000, 3200, 500, _engine->_lbaTime - startLbaTime);
516 const int32 cdot = _engine->_screens->lerp(1, ANGLE_360, 100, (_engine->_lbaTime - startLbaTime) % 100);
517
518 _engine->blitWorkToFront(rect);
519 _engine->_renderer->setCameraAngle(0, 0, 0, 0, -cdot, 0, avg);
520 _engine->_renderer->renderIsoModel(0, 0, 0, ANGLE_0, ANGLE_0, ANGLE_0, gameOverPtr, dummy);
521
522 _engine->_lbaTime++;
523 }
524
525 _engine->_sound->playSample(Samples::Explode);
526 _engine->blitWorkToFront(rect);
527 _engine->_renderer->setCameraAngle(0, 0, 0, 0, 0, 0, 3200);
528 _engine->_renderer->renderIsoModel(0, 0, 0, ANGLE_0, ANGLE_0, ANGLE_0, gameOverPtr, dummy);
529
530 _engine->delaySkip(2000);
531
532 _engine->_interface->resetClip();
533 _engine->restoreFrontBuffer();
534 initEngineProjections();
535
536 _engine->_lbaTime = tmpLbaTime;
537 }
538
giveUp()539 void GameState::giveUp() {
540 _engine->_sound->stopSamples();
541 // TODO: is an autosave desired on giving up? I don't think so. What did the original game do here?
542 //_engine->autoSave();
543 initGameStateVars();
544 _engine->_scene->stopRunningGame();
545 }
546
setGas(int16 value)547 int16 GameState::setGas(int16 value) {
548 _inventoryNumGas = CLIP<int16>(value, 0, 100);
549 return _inventoryNumGas;
550 }
551
addGas(int16 value)552 void GameState::addGas(int16 value) {
553 setGas(_inventoryNumGas + value);
554 }
555
setKashes(int16 value)556 int16 GameState::setKashes(int16 value) {
557 _inventoryNumKashes = CLIP<int16>(value, 0, 999);
558 if (_engine->_gameState->_inventoryNumKashes >= 500) {
559 _engine->unlockAchievement("LBA_ACH_011");
560 }
561 return _inventoryNumKashes;
562 }
563
setKeys(int16 value)564 int16 GameState::setKeys(int16 value) {
565 _inventoryNumKeys = MAX<int16>(0, value);
566 return _inventoryNumKeys;
567 }
568
addKeys(int16 val)569 void GameState::addKeys(int16 val) {
570 setKeys(_inventoryNumKeys + val);
571 }
572
addKashes(int16 val)573 void GameState::addKashes(int16 val) {
574 setKashes(_inventoryNumKashes + val);
575 }
576
setMagicPoints(int16 val)577 int16 GameState::setMagicPoints(int16 val) {
578 _inventoryMagicPoints = val;
579 if (_inventoryMagicPoints > _magicLevelIdx * 20) {
580 _inventoryMagicPoints = _magicLevelIdx * 20;
581 } else if (_inventoryMagicPoints < 0) {
582 _inventoryMagicPoints = 0;
583 }
584 return _inventoryMagicPoints;
585 }
586
setMaxMagicPoints()587 int16 GameState::setMaxMagicPoints() {
588 _inventoryMagicPoints = _magicLevelIdx * 20;
589 return _inventoryMagicPoints;
590 }
591
addMagicPoints(int16 val)592 void GameState::addMagicPoints(int16 val) {
593 setMagicPoints(_inventoryMagicPoints + val);
594 }
595
setLeafs(int16 val)596 int16 GameState::setLeafs(int16 val) {
597 _inventoryNumLeafs = val;
598 if (_inventoryNumLeafs > _inventoryNumLeafsBox) {
599 _inventoryNumLeafs = _inventoryNumLeafsBox;
600 }
601 return _inventoryNumLeafs;
602 }
603
addLeafs(int16 val)604 void GameState::addLeafs(int16 val) {
605 setLeafs(_inventoryNumLeafs + val);
606 }
607
setLeafBoxes(int16 val)608 int16 GameState::setLeafBoxes(int16 val) {
609 _inventoryNumLeafsBox = val;
610 if (_inventoryNumLeafsBox > 10) {
611 _inventoryNumLeafsBox = 10;
612 }
613 if (_inventoryNumLeafsBox == 5) {
614 _engine->unlockAchievement("LBA_ACH_003");
615 }
616 return _inventoryNumLeafsBox;
617 }
618
addLeafBoxes(int16 val)619 void GameState::addLeafBoxes(int16 val) {
620 setLeafBoxes(_inventoryNumLeafsBox + val);
621 }
622
clearGameFlags()623 void GameState::clearGameFlags() {
624 debug(2, "Clear all gameStateFlags");
625 Common::fill(&_gameStateFlags[0], &_gameStateFlags[NUM_GAME_FLAGS], 0);
626 }
627
hasGameFlag(uint8 index) const628 uint8 GameState::hasGameFlag(uint8 index) const {
629 debug(6, "Query gameStateFlags[%u]=%u", index, _gameStateFlags[index]);
630 return _gameStateFlags[index];
631 }
632
633 } // namespace TwinE
634