[Scummvm-git-logs] scummvm master -> 92b68a1132eb1272a70c62a266cc1e945eed3ff9

mgerhardy martin.gerhardy at gmail.com
Tue Nov 3 17:31:44 UTC 2020


This automated email contains information about 26 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
f68fbea3b5 TWINE: added return vales to music play methods
6569e84c8c TWINE: init audio cd
55ef559065 TWINE: use better sound type
b89a0ee053 TWINE: fallback to midi if audio cd isn't available
003160bab7 TWINE: missing missing null byte for playername string in savegame
7ba94d71af TWINE: started to implement save/load
8e926838be TWINE: minor cleanup
3257107fad TWINE: refactored menu data
42628c51be TWINE: return the button text - not just the id
2e5d3977c9 TWINE: reduced memory allocations
a41bf7b8ee TWINE: toggle virtual keyboard in player name menu
b2c114d73f TWINE: don't leave on empty name
f5d3e304ec TWINE: added enum for text banks
df8dc9f270 TWINE: replaced magic numbers
7fcf6917db TWINE: hide enum
0a3a634daf TWINE: moved scene text bank id into scene class
f560390f7e TWINE: implement load-savegame menu
d5e0874557 TWINE: reduced visibility
d3c2f7cd91 TWINE: added ScopedEngineFreeze
a7f3a63454 TWINE: reduced scoped + const
d66db9ad12 TWINE: started to implement holomap location loading
688e4a7349 TWINE: replaced magic number
0488fe67c4 TWINE: reduced cyclic complexity
3f6828a7a4 TWINE: allow to activate debug options via console
cf8e7339d0 TWINE: implemented drawText for debug window
92b68a1132 TWINE: cleanup in debug window


Commit: f68fbea3b509bbf4b227f2e512690ebd8478d61c
    https://github.com/scummvm/scummvm/commit/f68fbea3b509bbf4b227f2e512690ebd8478d61c
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:24:11+01:00

Commit Message:
TWINE: added return vales to music play methods

Changed paths:
    engines/twine/music.cpp
    engines/twine/music.h
    engines/twine/twine.h


diff --git a/engines/twine/music.cpp b/engines/twine/music.cpp
index ab1b8f2541..3c00784655 100644
--- a/engines/twine/music.cpp
+++ b/engines/twine/music.cpp
@@ -121,12 +121,12 @@ void Music::musicFadeOut() {
 	musicVolume(volume);
 }
 
-void Music::playTrackMusicCd(int32 track) {
+bool Music::playTrackMusicCd(int32 track) {
 	if (!_engine->cfgfile.UseCD) {
-		return;
+		return false;
 	}
 	AudioCDManager *cdrom = g_system->getAudioCDManager();
-	cdrom->play(track, 1, 0, 0);
+	return cdrom->play(track, 1, 0, 0);
 }
 
 void Music::stopTrackMusicCd() {
@@ -138,18 +138,18 @@ void Music::stopTrackMusicCd() {
 	cdrom->stop();
 }
 
-void Music::playTrackMusic(int32 track) {
+bool Music::playTrackMusic(int32 track) {
 	if (!_engine->cfgfile.Sound) {
-		return;
+		return false;
 	}
 
 	if (track == currentMusic) {
-		return;
+		return true;
 	}
 	currentMusic = track;
 
 	stopMusic();
-	playTrackMusicCd(track);
+	return playTrackMusicCd(track);
 }
 
 void Music::stopTrackMusic() {
@@ -161,13 +161,13 @@ void Music::stopTrackMusic() {
 	stopTrackMusicCd();
 }
 
-void Music::playMidiMusic(int32 midiIdx, int32 loop) {
+bool Music::playMidiMusic(int32 midiIdx, int32 loop) {
 	if (!_engine->cfgfile.Sound || _engine->cfgfile.MidiType == MIDIFILE_NONE) {
-		return;
+		return false;
 	}
 
 	if (midiIdx == currentMusic) {
-		return;
+		return true;
 	}
 
 	stopMusic();
@@ -187,6 +187,7 @@ void Music::playMidiMusic(int32 midiIdx, int32 loop) {
 
 	int32 midiSize = HQR::getAllocEntry(&midiPtr, filename, midiIdx);
 	_midiPlayer.play(midiPtr, midiSize);
+	return true;
 }
 
 void Music::stopMidiMusic() {
diff --git a/engines/twine/music.h b/engines/twine/music.h
index 35efac4ea1..e7fb52049d 100644
--- a/engines/twine/music.h
+++ b/engines/twine/music.h
@@ -48,11 +48,11 @@ private:
 
 	/** Auxiliar midi pointer to  */
 	uint8 *midiPtr = nullptr;
+	/** Track number of the current playing music */
+	int32 currentMusic = 0;
 
 public:
 	Music(TwinEEngine *engine);
-	/** Track number of the current playing music */
-	int32 currentMusic = 0;
 
 	/**
 	 * Music volume
@@ -63,21 +63,21 @@ public:
 	 * Play CD music
 	 * @param track track number to play
 	 */
-	void playTrackMusicCd(int32 track);
+	bool playTrackMusicCd(int32 track);
 	/** Stop CD music */
 	void stopTrackMusicCd();
 	/**
 	 * Generic play music, according with settings it plays CD or high quality sounds instead
 	 * @param track track number to play
 	 */
-	void playTrackMusic(int32 track);
+	bool playTrackMusic(int32 track);
 	/** Generic stop music according with settings */
 	void stopTrackMusic();
 	/**
 	 * Play MIDI music
 	 * @param midiIdx music index under mini_mi_win.hqr
 	 */
-	void playMidiMusic(int32 midiIdx, int32 loop = 0);
+	bool playMidiMusic(int32 midiIdx, int32 loop = 0);
 	/** Stop MIDI music */
 	void stopMidiMusic();
 
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index b030001bc1..0cf6d18278 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -209,9 +209,6 @@ public:
 	 * Contains all the data used in the engine to configurated the game in particulary ways. */
 	ConfigFile cfgfile;
 
-	/** CD Game directory */
-	const char *cdDir = "";
-
 	/** Initialize LBA engine */
 	void initEngine();
 	void initMCGA();


Commit: 6569e84c8c2837cce35db50ae4f84cad70bdfcaf
    https://github.com/scummvm/scummvm/commit/6569e84c8c2837cce35db50ae4f84cad70bdfcaf
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:24:11+01:00

Commit Message:
TWINE: init audio cd

Changed paths:
    engines/twine/music.cpp


diff --git a/engines/twine/music.cpp b/engines/twine/music.cpp
index 3c00784655..cf0c6154f4 100644
--- a/engines/twine/music.cpp
+++ b/engines/twine/music.cpp
@@ -204,17 +204,9 @@ bool Music::initCdrom() {
 	if (!_engine->cfgfile.Sound) {
 		return false;
 	}
-#if 0 // TODO: mgerhardy
 	AudioCDManager* cdrom = g_system->getAudioCDManager();
-	if (cdrom->numtracks == NUM_CD_TRACKS) {
-		_engine->cdDir = "LBA";
-		_engine->cfgfile.UseCD = 1;
-		return true;
-	}
-#endif
-	// not found the right CD
-	_engine->cfgfile.UseCD = 0;
-	return false;
+	_engine->cfgfile.UseCD = cdrom->open();
+	return _engine->cfgfile.UseCD;
 }
 
 void Music::stopMusic() {


Commit: 55ef55906536fd78a6e15a138778aa903a42656d
    https://github.com/scummvm/scummvm/commit/55ef55906536fd78a6e15a138778aa903a42656d
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:24:55+01:00

Commit Message:
TWINE: use better sound type

Changed paths:
    engines/twine/sound.cpp


diff --git a/engines/twine/sound.cpp b/engines/twine/sound.cpp
index 9271c5f273..82a5de14d4 100644
--- a/engines/twine/sound.cpp
+++ b/engines/twine/sound.cpp
@@ -94,7 +94,7 @@ void Sound::playSample(int32 index, int32 frequency, int32 repeat, int32 x, int3
 
 	uint8 *sampPtr = _engine->_resources->samplesTable[index];
 	int32 sampSize = _engine->_resources->samplesSizeTable[index];
-	playSample(channelIdx, index, sampPtr, sampSize, repeat, Resources::HQR_SAMPLES_FILE, Audio::Mixer::kPlainSoundType, DisposeAfterUse::NO);
+	playSample(channelIdx, index, sampPtr, sampSize, repeat, Resources::HQR_SAMPLES_FILE, Audio::Mixer::kSFXSoundType, DisposeAfterUse::NO);
 }
 
 void Sound::playVoxSample(int32 index) {


Commit: b89a0ee05311939a1da10befbaa73576192657a1
    https://github.com/scummvm/scummvm/commit/b89a0ee05311939a1da10befbaa73576192657a1
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:24:55+01:00

Commit Message:
TWINE: fallback to midi if audio cd isn't available

Changed paths:
    engines/twine/hqr.cpp
    engines/twine/music.cpp
    engines/twine/music.h
    engines/twine/scene.cpp


diff --git a/engines/twine/hqr.cpp b/engines/twine/hqr.cpp
index 2837c0883e..1bac915968 100644
--- a/engines/twine/hqr.cpp
+++ b/engines/twine/hqr.cpp
@@ -196,6 +196,10 @@ int32 numEntries(const char *filename) {
 
 int32 getAllocEntry(uint8 **ptr, const char *filename, int32 index) {
 	const int32 size = entrySize(filename, index);
+	if (size == 0) {
+		warning("HQR: failed to get entry for index %i from file: %s", index, filename);
+		return 0;
+	}
 	*ptr = (uint8 *)malloc(size * sizeof(uint8));
 	if (!*ptr) {
 		warning("HQR: unable to allocate entry memory");
diff --git a/engines/twine/music.cpp b/engines/twine/music.cpp
index cf0c6154f4..5e58b44cb2 100644
--- a/engines/twine/music.cpp
+++ b/engines/twine/music.cpp
@@ -149,7 +149,14 @@ bool Music::playTrackMusic(int32 track) {
 	currentMusic = track;
 
 	stopMusic();
-	return playTrackMusicCd(track);
+	if (playTrackMusicCd(track)) {
+		return true;
+	}
+	if (playMidiMusic(track)) {
+		return true;
+	}
+	warning("Failed to play track %i", track);
+	return false;
 }
 
 void Music::stopTrackMusic() {
@@ -186,7 +193,11 @@ bool Music::playMidiMusic(int32 midiIdx, int32 loop) {
 	}
 
 	int32 midiSize = HQR::getAllocEntry(&midiPtr, filename, midiIdx);
+	if (midiSize == 0) {
+		return false;
+	}
 	_midiPlayer.play(midiPtr, midiSize);
+	debug("Play midi music %i from %s", midiIdx, filename);
 	return true;
 }
 
@@ -212,6 +223,7 @@ bool Music::initCdrom() {
 void Music::stopMusic() {
 	stopTrackMusic();
 	stopMidiMusic();
+	currentMusic = -1;
 }
 
 } // namespace TwinE
diff --git a/engines/twine/music.h b/engines/twine/music.h
index e7fb52049d..e2733686f0 100644
--- a/engines/twine/music.h
+++ b/engines/twine/music.h
@@ -49,8 +49,14 @@ private:
 	/** Auxiliar midi pointer to  */
 	uint8 *midiPtr = nullptr;
 	/** Track number of the current playing music */
-	int32 currentMusic = 0;
-
+	int32 currentMusic = -1;
+	/**
+	 * Play CD music
+	 * @param track track number to play
+	 */
+	bool playTrackMusicCd(int32 track);
+	/** Stop CD music */
+	void stopTrackMusicCd();
 public:
 	Music(TwinEEngine *engine);
 
@@ -59,13 +65,7 @@ public:
 	 * @param current volume number
 	 */
 	void musicVolume(int32 volume);
-	/**
-	 * Play CD music
-	 * @param track track number to play
-	 */
-	bool playTrackMusicCd(int32 track);
-	/** Stop CD music */
-	void stopTrackMusicCd();
+
 	/**
 	 * Generic play music, according with settings it plays CD or high quality sounds instead
 	 * @param track track number to play
diff --git a/engines/twine/scene.cpp b/engines/twine/scene.cpp
index 1bf160425d..6a0603bc1a 100644
--- a/engines/twine/scene.cpp
+++ b/engines/twine/scene.cpp
@@ -336,7 +336,7 @@ void Scene::changeScene() {
 	_engine->_renderer->setLightVector(alphaLight, betaLight, 0);
 
 	if (sceneMusic != -1) {
-		_engine->_music->playMidiMusic(sceneMusic); // TODO this should play midi or cd tracks
+		_engine->_music->playTrackMusic(sceneMusic);
 	}
 }
 


Commit: 003160bab702a3abcec3bcd743f19f4af58c77ac
    https://github.com/scummvm/scummvm/commit/003160bab702a3abcec3bcd743f19f4af58c77ac
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:24:55+01:00

Commit Message:
TWINE: missing missing null byte for playername string in savegame

Changed paths:
    engines/twine/gamestate.cpp


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index 2f58233da7..5f4f5b847f 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -178,20 +178,19 @@ bool GameState::loadGame(Common::InSaveFile *file) {
 	int playerNameIdx = 0;
 	do {
 		const byte c = file->readByte();
+		playerName[playerNameIdx++] = c;
 		if (c == '\0') {
 			break;
 		}
-		playerName[playerNameIdx++] = c;
 		if (playerNameIdx >= ARRAYSIZE(playerName)) {
-			warning("Failed to load savegame");
+			warning("Failed to load savegame. Invalid playername.");
 			return false;
 		}
 	} while (true);
-	playerName[playerNameIdx] = '\0';
 
 	byte numGameFlags = file->readByte();
 	if (numGameFlags != NUM_GAME_FLAGS) {
-		warning("Failed to load gameflags");
+		warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS, numGameFlags);
 		return false;
 	}
 	file->read(gameFlags, numGameFlags);
@@ -242,6 +241,7 @@ bool GameState::saveGame(Common::OutSaveFile *file) {
 
 	file->writeByte(0x03);
 	file->writeString(playerName);
+	file->writeByte('\0');
 	file->writeByte(NUM_GAME_FLAGS);
 	file->write(gameFlags, sizeof(gameFlags));
 	file->writeByte(_engine->_scene->currentSceneIdx);


Commit: 7ba94d71afc0d8f4a1e37fea2e00782c4159fd95
    https://github.com/scummvm/scummvm/commit/7ba94d71afc0d8f4a1e37fea2e00782c4159fd95
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: started to implement save/load

Changed paths:
    engines/twine/gamestate.cpp
    engines/twine/gamestate.h
    engines/twine/menu.cpp
    engines/twine/menu.h
    engines/twine/menuoptions.cpp
    engines/twine/menuoptions.h
    engines/twine/metaengine.cpp
    engines/twine/twine.cpp
    engines/twine/twine.h


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index 5f4f5b847f..ea820a73f4 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -168,7 +168,7 @@ void GameState::initEngineVars() {
 	_engine->_actor->previousHeroBehaviour = kNormal;
 }
 
-bool GameState::loadGame(Common::InSaveFile *file) {
+bool GameState::loadGame(Common::SeekableReadStream *file) {
 	if (file == nullptr) {
 		return false;
 	}
@@ -235,9 +235,10 @@ bool GameState::loadGame(Common::InSaveFile *file) {
 	return true;
 }
 
-bool GameState::saveGame(Common::OutSaveFile *file) {
-	// TODO: the player name must be handled properly
-	Common::strlcpy(playerName, "TwinEngineSave", sizeof(playerName));
+bool GameState::saveGame(Common::WriteStream *file) {
+	if (playerName[0] == '\0') {
+		Common::strlcpy(playerName, "TwinEngineSave", sizeof(playerName));
+	}
 
 	file->writeByte(0x03);
 	file->writeString(playerName);
diff --git a/engines/twine/gamestate.h b/engines/twine/gamestate.h
index 69c988fdcb..fe84dae1c4 100644
--- a/engines/twine/gamestate.h
+++ b/engines/twine/gamestate.h
@@ -139,8 +139,8 @@ public:
 
 	void processFoundItem(int32 item);
 
-	bool loadGame(Common::InSaveFile *file);
-	bool saveGame(Common::OutSaveFile *file);
+	bool loadGame(Common::SeekableReadStream *file);
+	bool saveGame(Common::WriteStream *file);
 
 	void processGameChoices(int32 choiceIdx);
 
diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index 1e45794b48..aa14d1bc0f 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -699,7 +699,7 @@ bool Menu::init() {
 	return HQR::getEntry(plasmaEffectPtr, Resources::HQR_RESS_FILE, RESSHQR_PLASMAEFFECT) > 0;
 }
 
-void Menu::run() {
+EngineState Menu::run() {
 	_engine->_text->initTextBank(0);
 
 	_engine->_music->playTrackMusic(9); // LBA's Theme
@@ -707,31 +707,32 @@ void Menu::run() {
 
 	switch (processMenu(MainMenuState)) {
 	case TextId::kNewGame: {
-		_engine->_menuOptions->newGameMenu();
+		if (_engine->_menuOptions->newGameMenu()) {
+			return EngineState::GameLoop;
+		}
 		break;
 	}
 	case TextId::kContinueGame: {
-		_engine->_menuOptions->continueGameMenu();
+		if (_engine->_menuOptions->continueGameMenu()) {
+			return EngineState::LoadedGame;
+		}
 		break;
 	}
 	case TextId::kOptions: {
 		optionsMenu();
 		break;
 	}
-	case TextId::kQuit: {
-		Common::Event event;
-		event.type = Common::EVENT_QUIT;
-		_engine->_system->getEventManager()->pushEvent(event);
-		break;
-	}
 	case kBackground: {
 		_engine->_screens->loadMenuImage();
 		break;
 	}
+	case TextId::kQuit:
 	case kQuitEngine:
-		return;
+		debug("quit the game");
+		return EngineState::QuitGame;
 	}
 	_engine->_system->delayMillis(1000 / _engine->cfgfile.Fps);
+	return EngineState::Menu;
 }
 
 int32 Menu::giveupMenu() {
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index f39e353a09..e467376e25 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -24,6 +24,7 @@
 #define TWINE_MENU_H
 
 #include "twine/actor.h"
+#include "twine/twine.h"
 
 namespace TwinE {
 
@@ -190,7 +191,7 @@ public:
 	bool init();
 
 	/** Used to run the main menu */
-	void run();
+	EngineState run();
 
 	/** Used to run the in-game give-up menu */
 	int32 giveupMenu();
diff --git a/engines/twine/menuoptions.cpp b/engines/twine/menuoptions.cpp
index 3f409e42f9..1d3ce36013 100644
--- a/engines/twine/menuoptions.cpp
+++ b/engines/twine/menuoptions.cpp
@@ -21,7 +21,9 @@
  */
 
 #include "twine/menuoptions.h"
+#include "common/error.h"
 #include "common/keyboard.h"
+#include "common/str-array.h"
 #include "common/system.h"
 #include "common/util.h"
 #include "twine/flamovies.h"
@@ -282,19 +284,18 @@ bool MenuOptions::enterPlayerName(int32 textIdx) {
 	return false;
 }
 
-void MenuOptions::newGameMenu() {
-	if (enterPlayerName(TextId::kEnterYourName)) {
-		_engine->_gameState->initEngineVars();
-		newGame();
-
-		if (_engine->gameEngineLoop()) {
-			showCredits();
-		}
+bool MenuOptions::newGameMenu() {
+	if (!enterPlayerName(TextId::kEnterYourName)) {
+		return false;
 	}
+	_engine->_gameState->initEngineVars();
+	newGame();
+	return true;
 }
 
 int MenuOptions::chooseSave(int textIdx) {
-	if (!_engine->hasSavedSlots()) {
+	const Common::StringArray& savegames = _engine->getSaveSlots();
+	if (savegames.empty()) {
 		return -1;
 	}
 	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
@@ -311,7 +312,7 @@ int MenuOptions::chooseSave(int textIdx) {
 		_engine->flip();
 
 		if (_engine->shouldQuit()) {
-			break;
+			return kQuitEngine;
 		}
 		_engine->_system->delayMillis(1);
 	} while (_engine->_input->toggleAbortAction());
@@ -319,32 +320,13 @@ int MenuOptions::chooseSave(int textIdx) {
 	return 0;
 }
 
-void MenuOptions::continueGameMenu() {
+bool MenuOptions::continueGameMenu() {
 	const int slot = chooseSave(TextId::kContinueGame);
 	if (slot >= 0) {
 		_engine->_gameState->initEngineVars();
-		_engine->loadSaveSlot(slot);
-		if (_engine->_scene->newHeroX == -1) {
-			_engine->_scene->heroPositionType = ScenePositionType::kNoPosition;
-		}
-		if (_engine->_gameState->gameChapter == 0 && _engine->_scene->currentSceneIdx == LBA1SceneId::Citadel_Island_Prison) {
-			newGame();
-		} else {
-			_engine->_text->newGameVar5 = 0;
-			_engine->_text->textClipSmall();
-			_engine->_text->newGameVar4 = 1;
-		}
-
-		if (_engine->gameEngineLoop()) {
-			showCredits();
-		}
-
-		_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
-		do {
-			_engine->readKeys();
-			_engine->_system->delayMillis(1);
-		} while (!_engine->shouldQuit() && !_engine->_input->toggleAbortAction());
+		return _engine->loadGameState(slot).getCode() == Common::kNoError;
 	}
+	return false;
 }
 
 } // namespace TwinE
diff --git a/engines/twine/menuoptions.h b/engines/twine/menuoptions.h
index 78cb7e933e..f7e044eb80 100644
--- a/engines/twine/menuoptions.h
+++ b/engines/twine/menuoptions.h
@@ -40,22 +40,22 @@ private:
 	void drawSelectableCharacters();
 	void drawPlayerName(int32 centerx, int32 top, int32 type);
 	void drawSelectableCharacter(int32 x, int32 y, bool selected);
-	void showCredits();
-	void newGame();
 	int chooseSave(int textIdx);
 
 public:
 	MenuOptions(TwinEEngine *engine) : _engine(engine) {}
 
+	void showCredits();
 	bool canShowCredits = false;
 
 	char playerName[32] {'\0'};
 
 	/** Main menu new game options */
-	void newGameMenu();
+	bool newGameMenu();
+	void newGame();
 
 	/** Main menu continue game options */
-	void continueGameMenu();
+	bool continueGameMenu();
 };
 
 } // namespace TwinE
diff --git a/engines/twine/metaengine.cpp b/engines/twine/metaengine.cpp
index 73e2d16609..3393169a51 100644
--- a/engines/twine/metaengine.cpp
+++ b/engines/twine/metaengine.cpp
@@ -41,10 +41,6 @@ public:
 		return "TwinE";
 	}
 
-	bool hasFeature(MetaEngineFeature f) const override {
-		return false;
-	}
-
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
 		if (desc) {
 			TwineGameType gameType = TwineGameType::GType_LBA;
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 2ba2925d95..42d3a3d1aa 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -153,7 +153,34 @@ Common::Error TwinEEngine::run() {
 
 	_menu->init();
 	while (!shouldQuit()) {
-		_menu->run();
+		readKeys();
+		switch (_state) {
+		case EngineState::QuitGame: {
+			Common::Event event;
+			event.type = Common::EVENT_QUIT;
+			_system->getEventManager()->pushEvent(event);
+			break;
+		}
+		case EngineState::LoadedGame:
+			debug("Loaded game");
+			if (_scene->newHeroX == -1) {
+				_scene->heroPositionType = ScenePositionType::kNoPosition;
+			}
+			_text->newGameVar5 = 0;
+			_text->textClipSmall();
+			_text->newGameVar4 = 1;
+			_state = EngineState::GameLoop;
+			break;
+		case EngineState::GameLoop:
+			if (gameEngineLoop()) {
+				_menuOptions->showCredits();
+			}
+			_state = EngineState::Menu;
+			break;
+		case EngineState::Menu:
+			_state = _menu->run();
+			break;
+		}
 	}
 
 	ConfMan.setInt("CombatAuto", _actor->autoAgressive ? 1 : 0);
@@ -180,34 +207,39 @@ bool TwinEEngine::hasFeature(EngineFeature f) const {
 	return false;
 }
 
-bool TwinEEngine::hasSavedSlots() {
+Common::StringArray TwinEEngine::getSaveSlots() {
 	Common::SaveFileManager *saveFileMan = getSaveFileManager();
 	const Common::String pattern(getMetaEngine().getSavegameFilePattern(_targetName.c_str()));
-	return !saveFileMan->listSavefiles(pattern).empty();
+	return saveFileMan->listSavefiles(pattern);
 }
 
 void TwinEEngine::wipeSaveSlot(int slot) {
 	Common::SaveFileManager *saveFileMan = getSaveFileManager();
-	const Common::String &saveFile = getMetaEngine().getSavegameFile(slot, _targetName.c_str());
+	const Common::String &saveFile = getSaveStateName(slot);
 	saveFileMan->removeSavefile(saveFile);
 }
 
-bool TwinEEngine::loadSaveSlot(int slot) {
-	Common::SaveFileManager *saveFileMan = getSaveFileManager();
-	const Common::String &saveFile = getMetaEngine().getSavegameFile(slot, _targetName.c_str());
-	Common::InSaveFile *file = saveFileMan->openForLoading(saveFile);
-	return _gameState->loadGame(file);
+bool TwinEEngine::canSaveGameStateCurrently() { return _scene->currentScene != nullptr; }
+
+Common::Error TwinEEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	debug("load game stream");
+	if (!_gameState->loadGame(stream)) {
+		return Common::Error(Common::kReadingFailed);
+	}
+	_state = EngineState::LoadedGame;
+	return Common::Error(Common::kNoError);
 }
 
-bool TwinEEngine::saveSlot(int slot) {
-	Common::SaveFileManager *saveFileMan = getSaveFileManager();
-	const Common::String &saveFile = getMetaEngine().getSavegameFile(slot, _targetName.c_str());
-	Common::OutSaveFile *file = saveFileMan->openForSaving(saveFile);
-	return _gameState->saveGame(file);
+Common::Error TwinEEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	if (!_gameState->saveGame(stream)) {
+		return Common::Error(Common::kWritingFailed);
+	}
+	return Common::Error(Common::kNoError);
 }
 
 void TwinEEngine::autoSave() {
-	// TODO:
+	// TODO: scene title, not player name
+	saveGameState(getAutosaveSlot(), _gameState->playerName, true);
 }
 
 void TwinEEngine::allocVideoMemory() {
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index 0cf6d18278..735f5c661b 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -154,6 +154,13 @@ struct Keyboard;
 class Debug;
 class DebugScene;
 
+enum class EngineState {
+	Menu,
+	GameLoop,
+	LoadedGame,
+	QuitGame
+};
+
 class TwinEEngine : public Engine {
 private:
 	int32 isTimeFreezed = 0;
@@ -161,6 +168,7 @@ private:
 	ActorMoveStruct loopMovePtr; // mainLoopVar1
 	PauseToken _pauseToken;
 	TwineGameType _gameType;
+	EngineState _state = EngineState::Menu;
 
 public:
 	TwinEEngine(OSystem *system, Common::Language language, uint32 flagsTwineGameType, TwineGameType gameType);
@@ -169,10 +177,14 @@ public:
 	Common::Error run() override;
 	bool hasFeature(EngineFeature f) const override;
 
+	bool canLoadGameStateCurrently() override { return true; }
+	bool canSaveGameStateCurrently() override;
+
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
+	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
+
 	void wipeSaveSlot(int slot);
-	bool hasSavedSlots();
-	bool loadSaveSlot(int slot);
-	bool saveSlot(int slot);
+	Common::StringArray getSaveSlots();
 	void autoSave();
 
 	bool isLBA1() const { return _gameType == TwineGameType::GType_LBA; };


Commit: 8e926838be43817e0c3fe5192e3dfd5f20d4b11c
    https://github.com/scummvm/scummvm/commit/8e926838be43817e0c3fe5192e3dfd5f20d4b11c
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: minor cleanup

Changed paths:
    engines/twine/menu.cpp
    engines/twine/menu.h


diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index aa14d1bc0f..bc6288d052 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -374,7 +374,7 @@ void Menu::drawButtonGfx(int32 width, int32 topheight, int32 buttonId, int32 tex
 	_engine->copyBlockPhys(left, top, right, bottom);
 }
 
-void Menu::drawButton(const int16 *menuSettings, bool hover) {
+void Menu::drawButtons(const int16 *menuSettings, bool hover) {
 	int16 buttonNumber = menuSettings[MenuSettings_CurrentLoadedButton];
 	const int32 maxButton = menuSettings[MenuSettings_NumberOfButtons];
 	int32 topHeight = menuSettings[MenuSettings_ButtonsBoxHeight];
@@ -395,10 +395,8 @@ void Menu::drawButton(const int16 *menuSettings, bool hover) {
 	localData += MenuSettings_FirstButtonState;
 	do {
 		// get menu item settings
-		uint8 menuItemId = (uint8)*localData;
-		localData += 1;
-		uint16 textId = *localData;
-		localData += 1;
+		int32 menuItemId = *localData++;
+		int32 textId = *localData++;
 		if (hover) {
 			if (currentButton == buttonNumber) {
 				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, hover);
@@ -564,12 +562,12 @@ int32 Menu::processMenu(int16 *menuSettings) {
 			menuSettings[MenuSettings_CurrentLoadedButton] = currentButton;
 
 			// draw all buttons
-			drawButton(menuSettings, false);
+			drawButtons(menuSettings, false);
 			buttonsNeedRedraw = false;
 		}
 
 		// draw plasma effect for the current selected button
-		drawButton(menuSettings, true);
+		drawButtons(menuSettings, true);
 		if (_engine->shouldQuit()) {
 			return kQuitEngine;
 		}
@@ -773,23 +771,20 @@ int32 Menu::giveupMenu() {
 }
 
 void Menu::drawInfoMenu(int16 left, int16 top) {
-	int32 boxLeft, boxTop, boxRight, boxBottom;
-	int32 newBoxLeft, newBoxLeft2, i;
-
 	_engine->_interface->resetClip();
 	drawBox(left, top, left + 450, top + 80);
 	_engine->_interface->drawSplittedBox(left + 1, top + 1, left + 449, top + 79, 0);
 
-	newBoxLeft2 = left + 9;
+	int32 newBoxLeft2 = left + 9;
 
 	_engine->_grid->drawSprite(0, newBoxLeft2, top + 13, _engine->_resources->spriteTable[SPRITEHQR_LIFEPOINTS]);
 
-	boxRight = left + 325;
-	newBoxLeft = left + 25;
-	boxLeft = _engine->_screens->crossDot(newBoxLeft, boxRight, 50, _engine->_scene->sceneHero->life);
+	int32 boxRight = left + 325;
+	int32 newBoxLeft = left + 25;
+	int32 boxLeft = _engine->_screens->crossDot(newBoxLeft, boxRight, 50, _engine->_scene->sceneHero->life);
 
-	boxTop = top + 10;
-	boxBottom = top + 25;
+	int32 boxTop = top + 10;
+	int32 boxBottom = top + 25;
 	_engine->_interface->drawSplittedBox(newBoxLeft, boxTop, boxLeft, boxBottom, 91);
 	drawBox(left + 25, top + 10, left + 324, top + 10 + 14);
 
@@ -821,12 +816,12 @@ void Menu::drawInfoMenu(int16 left, int16 top) {
 	}
 
 	// Clover leaf boxes
-	for (i = 0; i < _engine->_gameState->inventoryNumLeafsBox; i++) {
+	for (int32 i = 0; i < _engine->_gameState->inventoryNumLeafsBox; i++) {
 		_engine->_grid->drawSprite(0, _engine->_screens->crossDot(left + 25, left + 325, 10, i), top + 58, _engine->_resources->spriteTable[SPRITEHQR_CLOVERLEAFBOX]);
 	}
 
 	// Clover leafs
-	for (i = 0; i < _engine->_gameState->inventoryNumLeafs; i++) {
+	for (int32 i = 0; i < _engine->_gameState->inventoryNumLeafs; i++) {
 		_engine->_grid->drawSprite(0, _engine->_screens->crossDot(left + 25, left + 325, 10, i) + 2, top + 60, _engine->_resources->spriteTable[SPRITEHQR_CLOVERLEAF]);
 	}
 
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index e467376e25..65ab4d4850 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -120,7 +120,7 @@ private:
 	 * @param data menu settings array
 	 * @param mode flag to know if should draw as a hover button or not
 	 */
-	void drawButton(const int16 *menuSettings, bool hover);
+	void drawButtons(const int16 *menuSettings, bool hover);
 	/** Used to run the advanced options menu */
 	int32 advoptionsMenu();
 	/** Used to run the volume menu */


Commit: 3257107fadf6a39249be5c980aa2ac64ff93ada0
    https://github.com/scummvm/scummvm/commit/3257107fadf6a39249be5c980aa2ac64ff93ada0
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: refactored menu data

Changed paths:
    engines/twine/gamestate.cpp
    engines/twine/gamestate.h
    engines/twine/menu.cpp
    engines/twine/menu.h
    engines/twine/twine.cpp


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index ea820a73f4..8e01413844 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -55,7 +55,6 @@ GameState::GameState(TwinEEngine *engine) : _engine(engine) {
 	Common::fill(&holomapFlags[0], &holomapFlags[150], 0);
 	playerName[0] = 0;
 	Common::fill(&gameChoices[0], &gameChoices[10], 0);
-	Common::fill(&gameChoicesSettings[0], &gameChoicesSettings[18], 0);
 }
 
 void GameState::initEngineProjections() {
@@ -411,21 +410,19 @@ void GameState::processFoundItem(int32 item) {
 void GameState::processGameChoices(int32 choiceIdx) {
 	_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
 
-	gameChoicesSettings[MenuSettings_CurrentLoadedButton] = 0;      // Current loaded button (button number)
-	gameChoicesSettings[MenuSettings_NumberOfButtons] = numChoices; // Num of buttons
-	gameChoicesSettings[MenuSettings_ButtonsBoxHeight] = 0;         // Buttons box height
-	gameChoicesSettings[MenuSettings_HeaderEnd] = _engine->_text->currentTextBank + 3;
+	gameChoicesSettings.reset();
+	gameChoicesSettings.setHeadlineTextId(_engine->_text->currentTextBank + 3);
 
 	// filled via script
 	for (int32 i = 0; i < numChoices; i++) {
-		gameChoicesSettings[i * 2 + MenuSettings_FirstButtonState] = 0;
-		gameChoicesSettings[i * 2 + MenuSettings_FirstButton] = gameChoices[i];
+		gameChoicesSettings.addButton(gameChoices[i], 0);
 	}
 
 	_engine->_text->drawAskQuestion(choiceIdx);
 
-	_engine->_menu->processMenu(gameChoicesSettings);
-	choiceAnswer = gameChoices[gameChoicesSettings[MenuSettings_CurrentLoadedButton]];
+	_engine->_menu->processMenu(&gameChoicesSettings);
+	const int16 activeButton = gameChoicesSettings.getActiveButton();
+	choiceAnswer = gameChoices[activeButton];
 
 	// get right VOX entry index
 	if (_engine->_text->initVoxToPlay(choiceAnswer)) {
diff --git a/engines/twine/gamestate.h b/engines/twine/gamestate.h
index fe84dae1c4..2a6e5f2b03 100644
--- a/engines/twine/gamestate.h
+++ b/engines/twine/gamestate.h
@@ -26,6 +26,7 @@
 #include "common/savefile.h"
 #include "common/scummsys.h"
 #include "twine/actor.h"
+#include "twine/menu.h"
 
 namespace TwinE {
 
@@ -128,7 +129,7 @@ public:
 
 	int32 gameChoices[10];         // inGameMenuData
 	int32 numChoices = 0;          // numOfOptionsInChoice
-	int16 gameChoicesSettings[18]; // choiceTab -  same structure as menu settings
+	MenuSettings gameChoicesSettings; // choiceTab -  same structure as menu settings
 	int32 choiceAnswer = 0;        // inGameMenuAnswer
 
 	/** Initialize all engine variables */
diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index bc6288d052..35e5befcb2 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -78,152 +78,101 @@ enum _MenuButtonTypes {
 #define kBackground 9999
 
 namespace _priv {
-/** Main Menu Settings
-
-	Used to create the game main menu. */
-static const int16 MainMenuSettings[] = {
-    0,   // Current loaded button (button number)
-    4,   // Num of buttons
-    200, // Buttons box height ( is used to calc the height where the first button will appear )
-    0,   // unused
-    0,
-    TextId::kNewGame, // new game
-    0,
-    TextId::kContinueGame, // continue game
-    0,
-    TextId::kOptions, // options
-    0,
-    TextId::kQuit, // quit
-};
 
-/** Give Up Menu Settings
-
-	Used to create the in-game menu. */
-static const int16 GiveUpMenuSettings[] = {
-    0,   // Current loaded button (button number)
-    2,   // Num of buttons
-    240, // Buttons box height ( is used to calc the height where the first button will appear )
-    0,   // unused
-    0,
-    TextId::kContinue, // continue game
-    0,
-    TextId::kGiveUp, // quit game
-};
+static MenuSettings createMainMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.setButtonsBoxHeight(200);
+	settings.addButton(TextId::kNewGame);
+	settings.addButton(TextId::kContinueGame);
+	settings.addButton(TextId::kOptions);
+	settings.addButton(TextId::kQuit);
+	return settings;
+}
 
-/** Give Up Menu Settings
-
-	Used to create the in-game menu. This menu have one extra item to save the game */
-static const int16 GiveUpMenuWithSaveSettings[] = {
-    0,   // Current loaded button (button number)
-    3,   // Num of buttons
-    240, // Buttons box height ( is used to calc the height where the first button will appear )
-    0,   // unused
-    0,
-    TextId::kContinue, // continue game
-    0,
-    TextId::kCreateSaveGame, // save game
-    0,
-    TextId::kGiveUp, // quit game
-};
+static MenuSettings createGiveUpMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.setButtonsBoxHeight(240);
+	settings.addButton(TextId::kContinue);
+	settings.addButton(TextId::kGiveUp);
+	return settings;
+}
 
-/** Options Menu Settings
-
-	Used to create the options menu. */
-static const int16 OptionsMenuSettings[] = {
-    0, // Current loaded button (button number)
-    4, // Num of buttons
-    0, // Buttons box height ( is used to calc the height where the first button will appear )
-    0, // unused
-    0,
-    TextId::kReturnMenu, // return to previous menu
-    0,
-    TextId::kVolumeSettings, // volume settings
-    0,
-    TextId::kSaveManage, // save game management
-    0,
-    TextId::kAdvanced, // advanced options
-};
+static MenuSettings createGiveUpSaveMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.setButtonsBoxHeight(240);
+	settings.addButton(TextId::kContinue);
+	settings.addButton(TextId::kCreateSaveGame);
+	settings.addButton(TextId::kGiveUp);
+	return settings;
+}
 
-/** Advanced Options Menu Settings
-
-	Used to create the advanced options menu. */
-static const int16 AdvOptionsMenuSettings[] = {
-    0, // Current loaded button (button number)
-    5, // Num of buttons
-    0, // Buttons box height ( is used to calc the height where the first button will appear )
-    0, // unused
-    0,
-    TextId::kReturnMenu, // return to main menu
-    MenuButtonTypes::kAgressiveMode,
-    TextId::kBehaviourAgressiveManual, // aggressive mode (manual|auto)
-    MenuButtonTypes::kPolygonDetails,
-    TextId::kDetailsPolygonsHigh, // Polygon detail (full|medium|low)
-    MenuButtonTypes::kShadowSettings,
-    TextId::kDetailsShadowHigh, // Shadows (all|character|no)
-    MenuButtonTypes::kSceneryZoom,
-    TextId::kScenaryZoomOn, // scenary zoon (on|off)
-};
+static MenuSettings createOptionsMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.addButton(TextId::kReturnMenu);
+	settings.addButton(TextId::kVolumeSettings);
+	settings.addButton(TextId::kSaveManage);
+	settings.addButton(TextId::kAdvanced);
+	return settings;
+}
 
-/** Save Game Management Menu Settings
-
-	Used to create the save game management menu. */
-static const int16 SaveManageMenuSettings[] = {
-    0, // Current loaded button (button number)
-    3, // Num of buttons
-    0, // Buttons box height ( is used to calc the height where the first button will appear )
-    0, // unused
-    0,
-    TextId::kReturnMenu, // return to main menu
-    0,
-    TextId::kCreateSaveGame, // copy saved game
-    0,
-    TextId::kDeleteSaveGame, // delete saved game
-};
+static MenuSettings createAdvancedOptionsMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.addButton(TextId::kReturnMenu);
+	settings.addButton(TextId::kBehaviourAgressiveManual, MenuButtonTypes::kAgressiveMode);
+	settings.addButton(TextId::kDetailsPolygonsHigh, MenuButtonTypes::kPolygonDetails);
+	settings.addButton(TextId::kDetailsShadowHigh, MenuButtonTypes::kShadowSettings);
+	settings.addButton(TextId::kScenaryZoomOn, MenuButtonTypes::kSceneryZoom);
+	return settings;
+}
+
+static MenuSettings createSaveManageMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.addButton(TextId::kReturnMenu);
+	settings.addButton(TextId::kCreateSaveGame);
+	settings.addButton(TextId::kDeleteSaveGame);
+	return settings;
+}
+
+static MenuSettings createVolumeMenu() {
+	MenuSettings settings;
+	settings.reset();
+	settings.addButton(TextId::kReturnMenu);
+	settings.addButton(TextId::kMusicVolume, MenuButtonTypes::kMusicVolume);
+	settings.addButton(TextId::kSoundVolume, MenuButtonTypes::kSoundVolume);
+	settings.addButton(TextId::kCDVolume, MenuButtonTypes::kCDVolume);
+	settings.addButton(TextId::kLineInVolume, MenuButtonTypes::kLineVolume);
+	settings.addButton(TextId::kMasterVolume, MenuButtonTypes::kMasterVolume);
+	settings.addButton(TextId::kSaveSettings);
+	return settings;
+}
 
-/** Volume Menu Settings
-
-	Used to create the volume menu. */
-static const int16 VolumeMenuSettings[] = {
-    0, // Current loaded button (button number)
-    7, // Num of buttons
-    0, // Buttons box height ( is used to calc the height where the first button will appear )
-    0, // unused
-    0,
-    TextId::kReturnMenu, // return to main menu
-    MenuButtonTypes::kMusicVolume,
-    TextId::kMusicVolume, // music volume
-    MenuButtonTypes::kSoundVolume,
-    TextId::kSoundVolume, // sfx volume
-    MenuButtonTypes::kCDVolume,
-    TextId::kCDVolume, // cd volume
-    MenuButtonTypes::kLineVolume,
-    TextId::kLineInVolume, // line-in volume
-    MenuButtonTypes::kMasterVolume,
-    TextId::kMasterVolume, // master volume
-    0,
-    TextId::kSaveSettings, // save parameters
-};
 } // namespace _priv
 
-static int16 *copySettings(const int16 *settings, size_t size) {
-	int16 *buf = (int16 *)malloc(size);
+static MenuSettings *copySettings(const MenuSettings settings) {
+	MenuSettings *buf = (MenuSettings *)malloc(sizeof(MenuSettings));
 	if (buf == nullptr) {
 		error("Failed to allocate menu state memory");
 	}
-	memcpy(buf, settings, size);
+	*buf = settings;
 	return buf;
 }
 
 Menu::Menu(TwinEEngine *engine) {
 	_engine = engine;
 
-	OptionsMenuState = copySettings(_priv::OptionsMenuSettings, sizeof(_priv::OptionsMenuSettings));
-	GiveUpMenuWithSaveState = copySettings(_priv::GiveUpMenuWithSaveSettings, sizeof(_priv::GiveUpMenuWithSaveSettings));
-	VolumeMenuState = copySettings(_priv::VolumeMenuSettings, sizeof(_priv::VolumeMenuSettings));
-	SaveManageMenuState = copySettings(_priv::SaveManageMenuSettings, sizeof(_priv::SaveManageMenuSettings));
-	GiveUpMenuState = copySettings(_priv::GiveUpMenuSettings, sizeof(_priv::GiveUpMenuSettings));
-	MainMenuState = copySettings(_priv::MainMenuSettings, sizeof(_priv::MainMenuSettings));
-	AdvOptionsMenuState = copySettings(_priv::AdvOptionsMenuSettings, sizeof(_priv::AdvOptionsMenuSettings));
+	OptionsMenuState = copySettings(_priv::createOptionsMenu());
+	GiveUpMenuWithSaveState = copySettings(_priv::createGiveUpSaveMenu());
+	VolumeMenuState = copySettings(_priv::createVolumeMenu());
+	SaveManageMenuState = copySettings(_priv::createSaveManageMenu());
+	GiveUpMenuState = copySettings(_priv::createGiveUpMenu());
+	MainMenuState = copySettings(_priv::createMainMenu());
+	AdvOptionsMenuState = copySettings(_priv::createAdvancedOptionsMenu());
 
 	Common::fill(&behaviourAnimState[0], &behaviourAnimState[4], 0);
 	Common::fill(&itemAngle[0], &itemAngle[255], 0);
@@ -374,10 +323,10 @@ void Menu::drawButtonGfx(int32 width, int32 topheight, int32 buttonId, int32 tex
 	_engine->copyBlockPhys(left, top, right, bottom);
 }
 
-void Menu::drawButtons(const int16 *menuSettings, bool hover) {
-	int16 buttonNumber = menuSettings[MenuSettings_CurrentLoadedButton];
-	const int32 maxButton = menuSettings[MenuSettings_NumberOfButtons];
-	int32 topHeight = menuSettings[MenuSettings_ButtonsBoxHeight];
+void Menu::drawButtons(const MenuSettings *menuSettings, bool hover) {
+	int16 buttonNumber = menuSettings->getActiveButton();
+	const int32 maxButton = menuSettings->getButtonCount();
+	int32 topHeight = menuSettings->getButtonBoxHeight();
 
 	if (topHeight == 0) {
 		topHeight = 35;
@@ -389,35 +338,29 @@ void Menu::drawButtons(const int16 *menuSettings, bool hover) {
 		return;
 	}
 
-	uint8 currentButton = 0;
-
-	const int16 *localData = menuSettings;
-	localData += MenuSettings_FirstButtonState;
-	do {
-		// get menu item settings
-		int32 menuItemId = *localData++;
-		int32 textId = *localData++;
+	for (int16 i = 0; i < maxButton; ++i) {
+		const int32 menuItemId = menuSettings->getButtonState(i);
+		const int32 textId = menuSettings->getButtonTextId(i);
 		if (hover) {
-			if (currentButton == buttonNumber) {
+			if (i == buttonNumber) {
 				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, hover);
 			}
 		} else {
-			if (currentButton == buttonNumber) {
+			if (i == buttonNumber) {
 				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, true);
 			} else {
 				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, false);
 			}
 		}
 
-		currentButton++;
 		topHeight += 56; // increase button top height
-	} while (currentButton < maxButton);
+	}
 }
 
-int32 Menu::processMenu(int16 *menuSettings) {
-	int16 currentButton = menuSettings[MenuSettings_CurrentLoadedButton];
+int32 Menu::processMenu(MenuSettings *menuSettings) {
+	int16 currentButton = menuSettings->getActiveButton();
 	bool buttonsNeedRedraw = true;
-	const int32 numEntry = menuSettings[MenuSettings_NumberOfButtons];
+	const int32 numEntry = menuSettings->getButtonCount();
 	int32 maxButton = numEntry - 1;
 
 	_engine->_input->enableKeyMap(uiKeyMapId);
@@ -440,9 +383,7 @@ int32 Menu::processMenu(int16 *menuSettings) {
 			buttonsNeedRedraw = true;
 		}
 
-		int16 *menuStatePtr = &menuSettings[MenuSettings_FirstButtonState] + currentButton * 2;
-		const int16 id = *menuStatePtr; // get button parameters from settings array
-		int16 *textId = menuStatePtr + 1; // to store the changed values
+		const int16 id = menuSettings->getActiveButtonState();
 		if (menuSettings == AdvOptionsMenuState) {
 			switch (id) {
 			case MenuButtonTypes::kAgressiveMode:
@@ -450,9 +391,9 @@ int32 Menu::processMenu(int16 *menuSettings) {
 					_engine->_actor->autoAgressive = !_engine->_actor->autoAgressive;
 				}
 				if (_engine->_actor->autoAgressive) {
-					*textId = TextId::kBehaviourAgressiveManual;
+					menuSettings->setActiveButtonTextId(TextId::kBehaviourAgressiveManual);
 				} else {
-					*textId = TextId::kBehaviourAgressiveAuto;
+					menuSettings->setActiveButtonTextId(TextId::kBehaviourAgressiveAuto);
 				}
 				break;
 			case MenuButtonTypes::kPolygonDetails:
@@ -464,11 +405,11 @@ int32 Menu::processMenu(int16 *menuSettings) {
 					_engine->cfgfile.PolygonDetails %= 3;
 				}
 				if (_engine->cfgfile.PolygonDetails == 0) {
-					*textId = TextId::kDetailsPolygonsLow;
+					menuSettings->setActiveButtonTextId(TextId::kDetailsPolygonsLow);
 				} else if (_engine->cfgfile.PolygonDetails == 1) {
-					*textId = TextId::kDetailsPolygonsMiddle;
+					menuSettings->setActiveButtonTextId(TextId::kDetailsPolygonsMiddle);
 				} else {
-					*textId = TextId::kDetailsPolygonsHigh;
+					menuSettings->setActiveButtonTextId(TextId::kDetailsPolygonsHigh);
 				}
 				break;
 			case MenuButtonTypes::kShadowSettings:
@@ -480,11 +421,11 @@ int32 Menu::processMenu(int16 *menuSettings) {
 					_engine->cfgfile.ShadowMode %= 3;
 				}
 				if (_engine->cfgfile.ShadowMode == 0) {
-					*textId = TextId::kShadowsDisabled;
+					menuSettings->setActiveButtonTextId(TextId::kShadowsDisabled);
 				} else if (_engine->cfgfile.ShadowMode == 1) {
-					*textId = TextId::kShadowsFigures;
+					menuSettings->setActiveButtonTextId(TextId::kShadowsFigures);
 				} else {
-					*textId = TextId::kDetailsShadowHigh;
+					menuSettings->setActiveButtonTextId(TextId::kDetailsShadowHigh);
 				}
 				break;
 			case MenuButtonTypes::kSceneryZoom:
@@ -492,9 +433,9 @@ int32 Menu::processMenu(int16 *menuSettings) {
 					_engine->cfgfile.SceZoom = !_engine->cfgfile.SceZoom;
 				}
 				if (_engine->cfgfile.SceZoom) {
-					*textId = TextId::kNoScenaryZoom;
+					menuSettings->setActiveButtonTextId(TextId::kNoScenaryZoom);
 				} else {
-					*textId = TextId::kScenaryZoomOn;
+					menuSettings->setActiveButtonTextId(TextId::kScenaryZoomOn);
 				}
 				break;
 			default:
@@ -559,7 +500,7 @@ int32 Menu::processMenu(int16 *menuSettings) {
 		}
 
 		if (buttonsNeedRedraw) {
-			menuSettings[MenuSettings_CurrentLoadedButton] = currentButton;
+			menuSettings->setActiveButton(currentButton);
 
 			// draw all buttons
 			drawButtons(menuSettings, false);
@@ -575,8 +516,7 @@ int32 Menu::processMenu(int16 *menuSettings) {
 		_engine->_system->delayMillis(10);
 	} while (!_engine->_input->toggleActionIfActive(TwinEActionType::UIEnter));
 
-	const int buttonTextId = *(menuSettings + MenuSettings_FirstButton + currentButton * 2); // get current browsed button
-	return buttonTextId;
+	return menuSettings->getActiveButtonTextId();
 }
 
 int32 Menu::advoptionsMenu() {
@@ -737,7 +677,7 @@ int32 Menu::giveupMenu() {
 	_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
 	_engine->_sound->pauseSamples();
 
-	int16 *localMenu;
+	MenuSettings *localMenu;
 	if (_engine->cfgfile.UseAutoSaving == 1) {
 		localMenu = GiveUpMenuState;
 	} else {
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index 65ab4d4850..2221055c56 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -92,6 +92,75 @@ enum _TextId {
 #define PLASMA_HEIGHT 50
 #define kQuitEngine 9998
 
+class MenuSettings {
+private:
+	int16 _settings[100] {0};
+	int8 _activeButtonIdx = 0;
+public:
+	void reset() {
+		_settings[MenuSettings_NumberOfButtons] = 0;
+		setButtonsBoxHeight(0);
+		setActiveButton(0);
+	}
+
+	// used to calc the height where the first button will appear
+	void setButtonsBoxHeight(int16 height) {
+		_settings[MenuSettings_ButtonsBoxHeight] = height;
+	}
+
+	void setActiveButton(int16 buttonIdx) {
+		_activeButtonIdx = buttonIdx;
+		_settings[MenuSettings_CurrentLoadedButton] = buttonIdx;
+	}
+
+	void setActiveButtonTextId(int16 textIndex) {
+		setButtonTextId(getActiveButton(), textIndex);
+	}
+
+	void setButtonTextId(int16 buttonIdx, int16 textIndex) {
+		_settings[MenuSettings_FirstButton + buttonIdx * 2] = textIndex;
+	}
+
+	int16 getActiveButtonTextId() const {
+		return getButtonTextId(getActiveButton());
+	}
+
+	int16 getActiveButtonState() const {
+		return getButtonState(getActiveButton());
+	}
+
+	int16 getButtonTextId(int buttonIndex) const {
+		return _settings[MenuSettings_FirstButton + buttonIndex * 2];
+	}
+
+	int16 getButtonState(int buttonIndex) const {
+		return _settings[MenuSettings_FirstButtonState + buttonIndex * 2];
+	}
+
+	int16 getActiveButton() const {
+		return _activeButtonIdx;
+	}
+
+	int16 getButtonBoxHeight() const {
+		return _settings[MenuSettings_ButtonsBoxHeight];
+	}
+
+	int16 getButtonCount() const {
+		return _settings[MenuSettings_NumberOfButtons];
+	}
+
+	void setHeadlineTextId(int16 textIndex) {
+		_settings[MenuSettings_HeaderEnd] = textIndex;
+	}
+
+	void addButton(int16 textId, int16 state = 0) {
+		const int16 i = _settings[MenuSettings_NumberOfButtons];
+		_settings[i * 2 + MenuSettings_FirstButtonState] = state;
+		_settings[i * 2 + MenuSettings_FirstButton] = textId;
+		++_settings[MenuSettings_NumberOfButtons];
+	}
+};
+
 class Menu {
 private:
 	TwinEEngine *_engine;
@@ -120,7 +189,7 @@ private:
 	 * @param data menu settings array
 	 * @param mode flag to know if should draw as a hover button or not
 	 */
-	void drawButtons(const int16 *menuSettings, bool hover);
+	void drawButtons(const MenuSettings *menuSettings, bool hover);
 	/** Used to run the advanced options menu */
 	int32 advoptionsMenu();
 	/** Used to run the volume menu */
@@ -141,17 +210,17 @@ private:
 	 */
 	void drawMagicItemsBox(int32 left, int32 top, int32 right, int32 bottom, int32 color);
 
-	int16 *GiveUpMenuWithSaveState;
-	int16 *VolumeMenuState;
-	int16 *SaveManageMenuState;
-	int16 *GiveUpMenuState;
-	int16 *MainMenuState;
-	int16 *AdvOptionsMenuState;
+	MenuSettings *GiveUpMenuWithSaveState;
+	MenuSettings *VolumeMenuState;
+	MenuSettings *SaveManageMenuState;
+	MenuSettings *GiveUpMenuState;
+	MenuSettings *MainMenuState;
+	MenuSettings *AdvOptionsMenuState;
 
 public:
 	Menu(TwinEEngine *engine);
 	~Menu();
-	int16 *OptionsMenuState;
+	MenuSettings *OptionsMenuState;
 
 	int32 currMenuTextIndex = -1;
 	int32 currMenuTextBank = -1;
@@ -186,7 +255,7 @@ public:
 	 * @param menuSettings menu settings array with the information to build the menu options
 	 * @return pressed menu button identification
 	 */
-	int32 processMenu(int16 *menuSettings);
+	int32 processMenu(MenuSettings *menuSettings);
 
 	bool init();
 
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 42d3a3d1aa..ac7ca9904c 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -457,11 +457,11 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 		if (_input->toggleActionIfActive(TwinEActionType::OptionsMenu)) {
 			freezeTime();
 			_sound->pauseSamples();
-			_menu->OptionsMenuState[MenuSettings_FirstButton] = TextId::kReturnGame;
+			_menu->OptionsMenuState->setButtonTextId(0, TextId::kReturnGame);
 			_text->initTextBank(0);
 			_menu->optionsMenu();
 			_text->initTextBank(_text->currentTextBank + 3);
-			_menu->OptionsMenuState[MenuSettings_FirstButton] = TextId::kReturnMenu;
+			_menu->OptionsMenuState->setButtonTextId(0, TextId::kReturnMenu);
 			// TODO: play music
 			_sound->resumeSamples();
 			unfreezeTime();


Commit: 42628c51be119870c2cac8f9ae17d75abada3a04
    https://github.com/scummvm/scummvm/commit/42628c51be119870c2cac8f9ae17d75abada3a04
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: return the button text - not just the id

Changed paths:
    engines/twine/menu.cpp
    engines/twine/menu.h


diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index 35e5befcb2..2c8e4709c5 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -163,6 +163,15 @@ static MenuSettings *copySettings(const MenuSettings settings) {
 	return buf;
 }
 
+const char *MenuSettings::getButtonText(Text *text, int buttonIndex) const {
+	const int32 textId = getButtonTextId(buttonIndex);
+	static char dialText[256];
+	if (!text->getMenuText(textId, dialText, sizeof(dialText))) {
+		dialText[0] = '\0';
+	}
+	return dialText;
+}
+
 Menu::Menu(TwinEEngine *engine) {
 	_engine = engine;
 
@@ -256,7 +265,7 @@ void Menu::drawBox(int32 left, int32 top, int32 right, int32 bottom) {
 	_engine->_interface->drawLine(left + 1, bottom, right, bottom, 73); // bottom line
 }
 
-void Menu::drawButtonGfx(int32 width, int32 topheight, int32 buttonId, int32 textId, bool hover) {
+void Menu::drawButtonGfx(int32 width, int32 topheight, int32 buttonId, const char *dialText, bool hover) {
 	const int32 left = width - kMainMenuButtonSpan / 2;
 	const int32 right = width + kMainMenuButtonSpan / 2;
 
@@ -315,8 +324,6 @@ void Menu::drawButtonGfx(int32 width, int32 topheight, int32 buttonId, int32 tex
 
 	_engine->_text->setFontColor(15);
 	_engine->_text->setFontParameters(2, 8);
-	char dialText[256];
-	_engine->_text->getMenuText(textId, dialText, sizeof(dialText));
 	const int32 textSize = _engine->_text->getTextSize(dialText);
 	_engine->_text->drawText(width - (textSize / 2), topheight - 18, dialText);
 
@@ -340,16 +347,16 @@ void Menu::drawButtons(const MenuSettings *menuSettings, bool hover) {
 
 	for (int16 i = 0; i < maxButton; ++i) {
 		const int32 menuItemId = menuSettings->getButtonState(i);
-		const int32 textId = menuSettings->getButtonTextId(i);
+		const char *text = menuSettings->getButtonText(_engine->_text, i);
 		if (hover) {
 			if (i == buttonNumber) {
-				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, hover);
+				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, text, hover);
 			}
 		} else {
 			if (i == buttonNumber) {
-				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, true);
+				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, text, true);
 			} else {
-				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, textId, false);
+				drawButtonGfx(kMainMenuButtonWidth, topHeight, menuItemId, text, false);
 			}
 		}
 
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index 2221055c56..85c58a0d3f 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -96,6 +96,11 @@ class MenuSettings {
 private:
 	int16 _settings[100] {0};
 	int8 _activeButtonIdx = 0;
+
+	int16 getButtonTextId(int buttonIndex) const {
+		return _settings[MenuSettings_FirstButton + buttonIndex * 2];
+	}
+
 public:
 	void reset() {
 		_settings[MenuSettings_NumberOfButtons] = 0;
@@ -129,14 +134,12 @@ public:
 		return getButtonState(getActiveButton());
 	}
 
-	int16 getButtonTextId(int buttonIndex) const {
-		return _settings[MenuSettings_FirstButton + buttonIndex * 2];
-	}
-
 	int16 getButtonState(int buttonIndex) const {
 		return _settings[MenuSettings_FirstButtonState + buttonIndex * 2];
 	}
 
+	const char *getButtonText(Text *text, int buttonIndex) const;
+
 	int16 getActiveButton() const {
 		return _activeButtonIdx;
 	}
@@ -179,10 +182,10 @@ private:
 	 * @param width menu button width
 	 * @param topheight is the height between the top of the screen and the first button
 	 * @param buttonId current button identification from menu settings
-	 * @param textId
+	 * @param dialText
 	 * @param hover flag to know if should draw as a hover button or not
 	 */
-	void drawButtonGfx(int32 width, int32 topheight, int32 buttonId, int32 textId, bool hover);
+	void drawButtonGfx(int32 width, int32 topheight, int32 buttonId, const char *dialText, bool hover);
 	void plasmaEffectRenderFrame();
 	/**
 	 * Process the menu button draw


Commit: 2e5d3977c9054c62a2e01029ab8229586b0b7493
    https://github.com/scummvm/scummvm/commit/2e5d3977c9054c62a2e01029ab8229586b0b7493
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: reduced memory allocations

Changed paths:
    engines/twine/menu.cpp
    engines/twine/menu.h
    engines/twine/twine.cpp


diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index 2c8e4709c5..e2d30eb4c0 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -81,7 +81,6 @@ namespace _priv {
 
 static MenuSettings createMainMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.setButtonsBoxHeight(200);
 	settings.addButton(TextId::kNewGame);
 	settings.addButton(TextId::kContinueGame);
@@ -92,7 +91,6 @@ static MenuSettings createMainMenu() {
 
 static MenuSettings createGiveUpMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.setButtonsBoxHeight(240);
 	settings.addButton(TextId::kContinue);
 	settings.addButton(TextId::kGiveUp);
@@ -101,7 +99,6 @@ static MenuSettings createGiveUpMenu() {
 
 static MenuSettings createGiveUpSaveMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.setButtonsBoxHeight(240);
 	settings.addButton(TextId::kContinue);
 	settings.addButton(TextId::kCreateSaveGame);
@@ -111,7 +108,6 @@ static MenuSettings createGiveUpSaveMenu() {
 
 static MenuSettings createOptionsMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.addButton(TextId::kReturnMenu);
 	settings.addButton(TextId::kVolumeSettings);
 	settings.addButton(TextId::kSaveManage);
@@ -121,7 +117,6 @@ static MenuSettings createOptionsMenu() {
 
 static MenuSettings createAdvancedOptionsMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.addButton(TextId::kReturnMenu);
 	settings.addButton(TextId::kBehaviourAgressiveManual, MenuButtonTypes::kAgressiveMode);
 	settings.addButton(TextId::kDetailsPolygonsHigh, MenuButtonTypes::kPolygonDetails);
@@ -132,7 +127,6 @@ static MenuSettings createAdvancedOptionsMenu() {
 
 static MenuSettings createSaveManageMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.addButton(TextId::kReturnMenu);
 	settings.addButton(TextId::kCreateSaveGame);
 	settings.addButton(TextId::kDeleteSaveGame);
@@ -141,7 +135,6 @@ static MenuSettings createSaveManageMenu() {
 
 static MenuSettings createVolumeMenu() {
 	MenuSettings settings;
-	settings.reset();
 	settings.addButton(TextId::kReturnMenu);
 	settings.addButton(TextId::kMusicVolume, MenuButtonTypes::kMusicVolume);
 	settings.addButton(TextId::kSoundVolume, MenuButtonTypes::kSoundVolume);
@@ -154,15 +147,6 @@ static MenuSettings createVolumeMenu() {
 
 } // namespace _priv
 
-static MenuSettings *copySettings(const MenuSettings settings) {
-	MenuSettings *buf = (MenuSettings *)malloc(sizeof(MenuSettings));
-	if (buf == nullptr) {
-		error("Failed to allocate menu state memory");
-	}
-	*buf = settings;
-	return buf;
-}
-
 const char *MenuSettings::getButtonText(Text *text, int buttonIndex) const {
 	const int32 textId = getButtonTextId(buttonIndex);
 	static char dialText[256];
@@ -175,13 +159,13 @@ const char *MenuSettings::getButtonText(Text *text, int buttonIndex) const {
 Menu::Menu(TwinEEngine *engine) {
 	_engine = engine;
 
-	OptionsMenuState = copySettings(_priv::createOptionsMenu());
-	GiveUpMenuWithSaveState = copySettings(_priv::createGiveUpSaveMenu());
-	VolumeMenuState = copySettings(_priv::createVolumeMenu());
-	SaveManageMenuState = copySettings(_priv::createSaveManageMenu());
-	GiveUpMenuState = copySettings(_priv::createGiveUpMenu());
-	MainMenuState = copySettings(_priv::createMainMenu());
-	AdvOptionsMenuState = copySettings(_priv::createAdvancedOptionsMenu());
+	optionsMenuState = _priv::createOptionsMenu();
+	giveUpMenuWithSaveState = _priv::createGiveUpSaveMenu();
+	volumeMenuState = _priv::createVolumeMenu();
+	saveManageMenuState = _priv::createSaveManageMenu();
+	giveUpMenuState = _priv::createGiveUpMenu();
+	mainMenuState = _priv::createMainMenu();
+	advOptionsMenuState = _priv::createAdvancedOptionsMenu();
 
 	Common::fill(&behaviourAnimState[0], &behaviourAnimState[4], 0);
 	Common::fill(&itemAngle[0], &itemAngle[255], 0);
@@ -190,13 +174,6 @@ Menu::Menu(TwinEEngine *engine) {
 
 Menu::~Menu() {
 	free(plasmaEffectPtr);
-	free(OptionsMenuState);
-	free(GiveUpMenuWithSaveState);
-	free(VolumeMenuState);
-	free(SaveManageMenuState);
-	free(GiveUpMenuState);
-	free(MainMenuState);
-	free(AdvOptionsMenuState);
 }
 
 void Menu::plasmaEffectRenderFrame() {
@@ -391,7 +368,7 @@ int32 Menu::processMenu(MenuSettings *menuSettings) {
 		}
 
 		const int16 id = menuSettings->getActiveButtonState();
-		if (menuSettings == AdvOptionsMenuState) {
+		if (menuSettings == &advOptionsMenuState) {
 			switch (id) {
 			case MenuButtonTypes::kAgressiveMode:
 				if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft) || _engine->_input->toggleActionIfActive(TwinEActionType::UIRight)) {
@@ -448,7 +425,7 @@ int32 Menu::processMenu(MenuSettings *menuSettings) {
 			default:
 				break;
 			}
-		} else if (menuSettings == VolumeMenuState) {
+		} else if (menuSettings == &volumeMenuState) {
 			Audio::Mixer *mixer = _engine->_system->getMixer();
 			switch (id) {
 			case MenuButtonTypes::kMusicVolume: {
@@ -531,7 +508,7 @@ int32 Menu::advoptionsMenu() {
 	_engine->flip();
 
 	for (;;) {
-		switch (processMenu(AdvOptionsMenuState)) {
+		switch (processMenu(&advOptionsMenuState)) {
 		case TextId::kReturnMenu: {
 			return 0;
 		}
@@ -555,7 +532,7 @@ int32 Menu::savemanageMenu() {
 	_engine->flip();
 
 	for (;;) {
-		switch (processMenu(SaveManageMenuState)) {
+		switch (processMenu(&saveManageMenuState)) {
 		case TextId::kReturnMenu:
 			return 0;
 		case TextId::kCreateSaveGame:
@@ -580,7 +557,7 @@ int32 Menu::volumeMenu() {
 	_engine->flip();
 
 	for (;;) {
-		switch (processMenu(VolumeMenuState)) {
+		switch (processMenu(&volumeMenuState)) {
 		case TextId::kReturnMenu:
 			return 0;
 		case TextId::kSaveSettings:
@@ -602,6 +579,14 @@ int32 Menu::volumeMenu() {
 	return 0;
 }
 
+void Menu::inGameOptionsMenu() {
+	_engine->_text->initTextBank(0);
+	_engine->_menu->optionsMenuState.setButtonTextId(0, TextId::kReturnGame);
+	_engine->_menu->optionsMenu();
+	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	optionsMenuState.setButtonTextId(0, TextId::kReturnMenu);
+}
+
 int32 Menu::optionsMenu() {
 	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
 	_engine->flip();
@@ -610,7 +595,7 @@ int32 Menu::optionsMenu() {
 	//_engine->_music->playCDtrack(9);
 
 	for (;;) {
-		switch (processMenu(OptionsMenuState)) {
+		switch (processMenu(&optionsMenuState)) {
 		case TextId::kReturnGame:
 		case TextId::kReturnMenu: {
 			return 0;
@@ -650,7 +635,7 @@ EngineState Menu::run() {
 	_engine->_music->playTrackMusic(9); // LBA's Theme
 	_engine->_sound->stopSamples();
 
-	switch (processMenu(MainMenuState)) {
+	switch (processMenu(&mainMenuState)) {
 	case TextId::kNewGame: {
 		if (_engine->_menuOptions->newGameMenu()) {
 			return EngineState::GameLoop;
@@ -686,9 +671,9 @@ int32 Menu::giveupMenu() {
 
 	MenuSettings *localMenu;
 	if (_engine->cfgfile.UseAutoSaving == 1) {
-		localMenu = GiveUpMenuState;
+		localMenu = &giveUpMenuState;
 	} else {
-		localMenu = GiveUpMenuWithSaveState;
+		localMenu = &giveUpMenuWithSaveState;
 	}
 
 	int32 menuId;
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index 85c58a0d3f..a3bafe631f 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -213,17 +213,17 @@ private:
 	 */
 	void drawMagicItemsBox(int32 left, int32 top, int32 right, int32 bottom, int32 color);
 
-	MenuSettings *GiveUpMenuWithSaveState;
-	MenuSettings *VolumeMenuState;
-	MenuSettings *SaveManageMenuState;
-	MenuSettings *GiveUpMenuState;
-	MenuSettings *MainMenuState;
-	MenuSettings *AdvOptionsMenuState;
+	MenuSettings giveUpMenuWithSaveState;
+	MenuSettings volumeMenuState;
+	MenuSettings saveManageMenuState;
+	MenuSettings giveUpMenuState;
+	MenuSettings mainMenuState;
+	MenuSettings advOptionsMenuState;
+	MenuSettings optionsMenuState;
 
 public:
 	Menu(TwinEEngine *engine);
 	~Menu();
-	MenuSettings *OptionsMenuState;
 
 	int32 currMenuTextIndex = -1;
 	int32 currMenuTextBank = -1;
@@ -268,6 +268,8 @@ public:
 	/** Used to run the in-game give-up menu */
 	int32 giveupMenu();
 
+	void inGameOptionsMenu();
+
 	/** Used to run the options menu */
 	int32 optionsMenu();
 
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index ac7ca9904c..b56c14afda 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -457,11 +457,7 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 		if (_input->toggleActionIfActive(TwinEActionType::OptionsMenu)) {
 			freezeTime();
 			_sound->pauseSamples();
-			_menu->OptionsMenuState->setButtonTextId(0, TextId::kReturnGame);
-			_text->initTextBank(0);
-			_menu->optionsMenu();
-			_text->initTextBank(_text->currentTextBank + 3);
-			_menu->OptionsMenuState->setButtonTextId(0, TextId::kReturnMenu);
+			_menu->inGameOptionsMenu();
 			// TODO: play music
 			_sound->resumeSamples();
 			unfreezeTime();


Commit: a41bf7b8eea8108d4befac10cd507551cf6f363b
    https://github.com/scummvm/scummvm/commit/a41bf7b8eea8108d4befac10cd507551cf6f363b
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: toggle virtual keyboard in player name menu

Changed paths:
    engines/twine/menuoptions.cpp


diff --git a/engines/twine/menuoptions.cpp b/engines/twine/menuoptions.cpp
index 1d3ce36013..a4007d3783 100644
--- a/engines/twine/menuoptions.cpp
+++ b/engines/twine/menuoptions.cpp
@@ -171,6 +171,25 @@ void MenuOptions::drawPlayerName(int32 centerx, int32 top, int32 type) {
 	_engine->copyBlockPhys(left, top, right, top + PLASMA_HEIGHT);
 }
 
+/**
+ * @brief Toggle a given @c OSystem::Feature and restore the previous state on destruction
+ */
+class ScopedFeatureState {
+private:
+	OSystem::Feature _feature;
+	bool _changeTo;
+public:
+	ScopedFeatureState(OSystem::Feature feature, bool enable) : _feature(feature), _changeTo(enable) {
+		if (g_system->getFeatureState(feature) != enable) {
+			g_system->setFeatureState(feature, enable);
+			_changeTo = !g_system->getFeatureState(feature);
+		}
+	}
+	~ScopedFeatureState() {
+		g_system->setFeatureState(_feature, _changeTo);
+	}
+};
+
 bool MenuOptions::enterPlayerName(int32 textIdx) {
 	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
 	_engine->flip();
@@ -184,6 +203,7 @@ bool MenuOptions::enterPlayerName(int32 textIdx) {
 	_engine->copyBlockPhys(0, 0, SCREEN_WIDTH - 1, 99);
 	_engine->flip();
 
+	ScopedFeatureState scopedVirtualKeyboard(OSystem::kFeatureVirtualKeyboard, true);
 	for (;;) {
 		Common::Event event;
 		while (g_system->getEventManager()->pollEvent(event)) {


Commit: b2c114d73f1166cb7ac1ccb9b612b69756c6cca5
    https://github.com/scummvm/scummvm/commit/b2c114d73f1166cb7ac1ccb9b612b69756c6cca5
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: don't leave on empty name

Changed paths:
    engines/twine/menuoptions.cpp


diff --git a/engines/twine/menuoptions.cpp b/engines/twine/menuoptions.cpp
index a4007d3783..c9f4755dfe 100644
--- a/engines/twine/menuoptions.cpp
+++ b/engines/twine/menuoptions.cpp
@@ -217,6 +217,9 @@ bool MenuOptions::enterPlayerName(int32 textIdx) {
 				if (_engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
 					if (_onScreenKeyboardLeaveViaOkButton) {
 						if (_onScreenKeyboardX == ONSCREENKEYBOARD_WIDTH - 1 && _onScreenKeyboardY == ONSCREENKEYBOARD_HEIGHT - 1) {
+							if (playerName[0] == '\0') {
+								continue;
+							}
 							return true;
 						}
 						const size_t size = strlen(playerName);
@@ -234,6 +237,10 @@ bool MenuOptions::enterPlayerName(int32 textIdx) {
 						}
 						continue;
 					}
+					if (playerName[0] == '\0') {
+						continue;
+					}
+
 					return true;
 				}
 				if (_engine->_input->toggleActionIfActive(TwinEActionType::UIAbort)) {


Commit: f5d3e304ec81529e88a6102151e24e342ba6cdb5
    https://github.com/scummvm/scummvm/commit/f5d3e304ec81529e88a6102151e24e342ba6cdb5
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: added enum for text banks

Changed paths:
    engines/twine/text.cpp
    engines/twine/text.h


diff --git a/engines/twine/text.cpp b/engines/twine/text.cpp
index 57bfe2ac20..c15e25684d 100644
--- a/engines/twine/text.cpp
+++ b/engines/twine/text.cpp
@@ -133,7 +133,9 @@ void Text::initTextBank(int32 bankIdx) {
 
 	// get index according with language
 	const int32 size = _engine->isLBA1() ? 28 : 30;
-	const int32 languageIndex = _engine->cfgfile.LanguageId * size + bankIdx * 2;
+	// the text banks indices are split into index and dialogs - each entry thus consists of two entries in the hqr
+	// every 28 entries starts a new language
+	const int32 languageIndex = _engine->cfgfile.LanguageId * size + (int)bankIdx * 2;
 	const int32 hqrSize = HQR::getAllocEntry((uint8 **)&dialOrderPtr, Resources::HQR_TEXT_FILE, languageIndex + INDEXOFFSET);
 	if (hqrSize == 0) {
 		warning("Failed to initialize text bank %i from file %s", languageIndex, Resources::HQR_TEXT_FILE);
diff --git a/engines/twine/text.h b/engines/twine/text.h
index e66760bd3e..67acb9cc62 100644
--- a/engines/twine/text.h
+++ b/engines/twine/text.h
@@ -28,6 +28,25 @@
 
 namespace TwinE {
 
+// lba1
+enum class TextBankId {
+	None = -1,
+	Options_and_menus = 0,
+	Credits = 1,
+	Inventory_Intro_and_Holomap = 2,
+	Citadel_Island = 3,
+	Principal_Island = 4,
+	White_Leaf_Desert = 5,
+	Proxima_Island = 6,
+	Rebellion_Island = 7,
+	Hamalayi_mountains_southern_range = 8,
+	Hamalayi_mountains_northern_range = 9,
+	Tippet_Island = 10,
+	Brundle_Island = 11,
+	Fortress_Island = 12,
+	Polar_Island = 13
+};
+
 class TwinEEngine;
 class Text {
 private:


Commit: df8dc9f270eacd3b7f7bbf77428b1643da406f98
    https://github.com/scummvm/scummvm/commit/df8dc9f270eacd3b7f7bbf77428b1643da406f98
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: replaced magic numbers

Changed paths:
    engines/twine/gamestate.cpp
    engines/twine/holomap.cpp
    engines/twine/menu.cpp
    engines/twine/menu.h
    engines/twine/menuoptions.cpp
    engines/twine/script_life_v1.cpp
    engines/twine/text.h
    engines/twine/twine.cpp


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index 8e01413844..e28b785910 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -315,7 +315,7 @@ void GameState::processFoundItem(int32 item) {
 
 	// process vox play
 	_engine->_music->stopMusic();
-	_engine->_text->initTextBank(2);
+	_engine->_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 
 	_engine->_interface->resetClip();
 	_engine->_text->initText(item);
diff --git a/engines/twine/holomap.cpp b/engines/twine/holomap.cpp
index c2cb8fc921..aa98cad2c2 100644
--- a/engines/twine/holomap.cpp
+++ b/engines/twine/holomap.cpp
@@ -134,7 +134,7 @@ void Holomap::processHolomap() {
 	drawHolomapTitle(320, 25);
 	_engine->_renderer->setCameraPosition(320, 190, 128, 1024, 1024);
 
-	_engine->_text->initTextBank(2);
+	_engine->_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 	_engine->_text->setFontCrossColor(9);
 
 	// TODO
diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index e2d30eb4c0..9e062f2211 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -580,7 +580,7 @@ int32 Menu::volumeMenu() {
 }
 
 void Menu::inGameOptionsMenu() {
-	_engine->_text->initTextBank(0);
+	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 	_engine->_menu->optionsMenuState.setButtonTextId(0, TextId::kReturnGame);
 	_engine->_menu->optionsMenu();
 	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
@@ -630,7 +630,7 @@ bool Menu::init() {
 }
 
 EngineState Menu::run() {
-	_engine->_text->initTextBank(0);
+	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 
 	_engine->_music->playTrackMusic(9); // LBA's Theme
 	_engine->_sound->stopSamples();
@@ -678,7 +678,7 @@ int32 Menu::giveupMenu() {
 
 	int32 menuId;
 	do {
-		_engine->_text->initTextBank(0);
+		_engine->_text->initTextBank(TextBankId::Options_and_menus);
 		menuId = processMenu(localMenu);
 		switch (menuId) {
 		case TextId::kContinue:
@@ -855,7 +855,7 @@ void Menu::processBehaviourMenu() {
 	int32 tmpTextBank = _engine->_text->currentTextBank;
 	_engine->_text->currentTextBank = -1;
 
-	_engine->_text->initTextBank(0);
+	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 
 	drawBehaviourMenu(_engine->_scene->sceneHero->angle);
 
@@ -969,7 +969,7 @@ void Menu::processInventoryMenu() {
 
 	drawInventoryItems();
 
-	_engine->_text->initTextBank(2);
+	_engine->_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 
 	int32 bx = 3;
 
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index a3bafe631f..97f0e3d206 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -25,6 +25,7 @@
 
 #include "twine/actor.h"
 #include "twine/twine.h"
+#include "twine/text.h"
 
 namespace TwinE {
 
@@ -39,55 +40,6 @@ enum MenuSettingsType {
 	MenuSettings_FirstButton = 5
 };
 
-/** menu text ids */
-namespace TextId {
-enum _TextId {
-	kBehaviourNormal = 0,
-	kBehaviourSporty = 1,
-	kBehaviourAgressiveManual = 2,
-	kBehaviourHiding = 3,
-	kBehaviourAgressiveAuto = 4,
-	kUseProtopack = 5,
-	kMusicVolume = 10,
-	kSoundVolume = 11,
-	kCDVolume = 12,
-	kLineInVolume = 13,
-	kMasterVolume = 14,
-	kReturnGame = 15,
-	kSaveSettings = 16,
-	kNewGame = 20,
-	kContinueGame = 21,
-	kQuit = 22,
-	kOptions = 23,
-	kDelete = 24,
-	kReturnMenu = 26,
-	kGiveUp = 27,
-	kContinue = 28,
-	kVolumeSettings = 30,
-	kDetailsPolygonsHigh = 31,
-	kDetailsShadowHigh = 32,
-	//kScenaryZoomOn = 33, // duplicate with 133 - TODO check if this is the same in all languages
-	kCreateNewPlayer = 40,
-	kCreateSaveGame = 41,
-	kEnterYourName = 42,
-	kPlayerAlreadyExists = 43,
-	kEnterYourNewName = 44,
-	kDeleteSaveGame = 45,
-	kSaveManage = 46,
-	kAdvanced = 47,
-	kDelete2 = 48, // difference between 24 and 48?
-	kTransferVoices = 49,
-	kPleaseWaitWhileVoicesAreSaved = 50,
-	kRemoveProtoPack = 105,
-	kDetailsPolygonsMiddle = 131,
-	kShadowsFigures = 132,
-	kScenaryZoomOn = 133,
-	kDetailsPolygonsLow = 231,
-	kShadowsDisabled = 232,
-	kNoScenaryZoom = 233
-};
-}
-
 #define PLASMA_WIDTH 320
 #define PLASMA_HEIGHT 50
 #define kQuitEngine 9998
@@ -226,7 +178,7 @@ public:
 	~Menu();
 
 	int32 currMenuTextIndex = -1;
-	int32 currMenuTextBank = -1;
+	int32 currMenuTextBank = TextBankId::None;
 	char currMenuTextBuffer[256];
 
 	int16 itemAngle[255]; // objectRotation
diff --git a/engines/twine/menuoptions.cpp b/engines/twine/menuoptions.cpp
index c9f4755dfe..4bacb6683c 100644
--- a/engines/twine/menuoptions.cpp
+++ b/engines/twine/menuoptions.cpp
@@ -59,7 +59,7 @@ void MenuOptions::newGame() {
 	_engine->_text->newGameVar4 = 0;
 	_engine->_text->newGameVar5 = 1;
 
-	_engine->_text->initTextBank(2);
+	_engine->_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 	_engine->_text->textClipFull();
 	_engine->_text->setFontCrossColor(15);
 
@@ -194,7 +194,7 @@ bool MenuOptions::enterPlayerName(int32 textIdx) {
 	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
 	_engine->flip();
 	playerName[0] = '\0'; // TODO: read from settings?
-	_engine->_text->initTextBank(0);
+	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 	char buffer[256];
 	_engine->_text->getMenuText(textIdx, buffer, sizeof(buffer));
 	_engine->_text->setFontColor(15);
@@ -329,7 +329,7 @@ int MenuOptions::chooseSave(int textIdx) {
 	_engine->flip();
 	do {
 		// TODO: assemble menu with save slots and make then loadable.
-		_engine->_text->initTextBank(0);
+		_engine->_text->initTextBank(TextBankId::Options_and_menus);
 		char buffer[256];
 		_engine->_text->getMenuText(textIdx, buffer, sizeof(buffer));
 		_engine->_text->setFontColor(15);
diff --git a/engines/twine/script_life_v1.cpp b/engines/twine/script_life_v1.cpp
index 2e926c84fc..365c03243e 100644
--- a/engines/twine/script_life_v1.cpp
+++ b/engines/twine/script_life_v1.cpp
@@ -1450,7 +1450,7 @@ static int32 lPROJ_3D(TwinEEngine *engine, ScriptContext& ctx) {
 	engine->_renderer->setCameraAngle(0, 1500, 0, 25, -128, 0, 13000);
 	engine->_renderer->setLightVector(896, 950, 0);
 
-	engine->_text->initTextBank(1);
+	engine->_text->initTextBank(TextBankId::Credits);
 
 	return 0;
 }
diff --git a/engines/twine/text.h b/engines/twine/text.h
index 67acb9cc62..ad673adb88 100644
--- a/engines/twine/text.h
+++ b/engines/twine/text.h
@@ -28,8 +28,9 @@
 
 namespace TwinE {
 
-// lba1
-enum class TextBankId {
+// lba
+namespace TextBankId {
+enum _TextBankId {
 	None = -1,
 	Options_and_menus = 0,
 	Credits = 1,
@@ -46,6 +47,56 @@ enum class TextBankId {
 	Fortress_Island = 12,
 	Polar_Island = 13
 };
+}
+
+/** menu text ids */
+namespace TextId {
+enum _TextId {
+	kBehaviourNormal = 0,
+	kBehaviourSporty = 1,
+	kBehaviourAgressiveManual = 2,
+	kBehaviourHiding = 3,
+	kBehaviourAgressiveAuto = 4,
+	kUseProtopack = 5,
+	kMusicVolume = 10,
+	kSoundVolume = 11,
+	kCDVolume = 12,
+	kLineInVolume = 13,
+	kMasterVolume = 14,
+	kReturnGame = 15,
+	kSaveSettings = 16,
+	kNewGame = 20,
+	kContinueGame = 21,
+	kQuit = 22,
+	kOptions = 23,
+	kDelete = 24,
+	kReturnMenu = 26,
+	kGiveUp = 27,
+	kContinue = 28,
+	kVolumeSettings = 30,
+	kDetailsPolygonsHigh = 31,
+	kDetailsShadowHigh = 32,
+	//kScenaryZoomOn = 33, // duplicate with 133 - TODO check if this is the same in all languages
+	kCreateNewPlayer = 40,
+	kCreateSaveGame = 41,
+	kEnterYourName = 42,
+	kPlayerAlreadyExists = 43,
+	kEnterYourNewName = 44,
+	kDeleteSaveGame = 45,
+	kSaveManage = 46,
+	kAdvanced = 47,
+	kDelete2 = 48, // difference between 24 and 48?
+	kTransferVoices = 49,
+	kPleaseWaitWhileVoicesAreSaved = 50,
+	kRemoveProtoPack = 105,
+	kDetailsPolygonsMiddle = 131,
+	kShadowsFigures = 132,
+	kScenaryZoomOn = 133,
+	kDetailsPolygonsLow = 231,
+	kShadowsDisabled = 232,
+	kNoScenaryZoom = 233
+};
+}
 
 class TwinEEngine;
 class Text {
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index b56c14afda..9fb51255c3 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -496,7 +496,7 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 			case kiBookOfBu: {
 				_screens->fadeToBlack(_screens->paletteRGBA);
 				_screens->loadImage(RESSHQR_INTROSCREEN1IMG);
-				_text->initTextBank(2);
+				_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 				_text->newGameVar4 = 0;
 				_text->textClipFull();
 				_text->setFontCrossColor(15);
@@ -552,7 +552,7 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 				unfreezeTime();
 				_redraw->redrawEngineActions(1);
 				freezeTime();
-				_text->initTextBank(2);
+				_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 				_text->textClipFull();
 				_text->setFontCrossColor(15);
 				_text->drawTextFullscreen(162);


Commit: 7fcf6917dbc7a7c4224ded8f29905d137dc86752
    https://github.com/scummvm/scummvm/commit/7fcf6917dbc7a7c4224ded8f29905d137dc86752
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: hide enum

Changed paths:
    engines/twine/menu.h


diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index 97f0e3d206..c76865cf00 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -29,23 +29,23 @@
 
 namespace TwinE {
 
-enum MenuSettingsType {
-	// button number
-	MenuSettings_CurrentLoadedButton = 0,
-	// is used to calc the height where the first button will appear
-	MenuSettings_NumberOfButtons = 1,
-	MenuSettings_ButtonsBoxHeight = 2,
-	MenuSettings_HeaderEnd = 3, // TODO: unknown
-	MenuSettings_FirstButtonState = 4,
-	MenuSettings_FirstButton = 5
-};
-
 #define PLASMA_WIDTH 320
 #define PLASMA_HEIGHT 50
 #define kQuitEngine 9998
 
 class MenuSettings {
 private:
+	enum MenuSettingsType {
+		// button number
+		MenuSettings_CurrentLoadedButton = 0,
+		// is used to calc the height where the first button will appear
+		MenuSettings_NumberOfButtons = 1,
+		MenuSettings_ButtonsBoxHeight = 2,
+		MenuSettings_HeaderEnd = 3, // TODO: unknown
+		MenuSettings_FirstButtonState = 4,
+		MenuSettings_FirstButton = 5
+	};
+
 	int16 _settings[100] {0};
 	int8 _activeButtonIdx = 0;
 


Commit: 0a3a634dafed1562cbc7df75e409a61827b0b7f8
    https://github.com/scummvm/scummvm/commit/0a3a634dafed1562cbc7df75e409a61827b0b7f8
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: moved scene text bank id into scene class

Changed paths:
    engines/twine/gamestate.cpp
    engines/twine/holomap.cpp
    engines/twine/menu.cpp
    engines/twine/menu.h
    engines/twine/scene.cpp
    engines/twine/scene.h
    engines/twine/text.cpp
    engines/twine/text.h
    engines/twine/twine.cpp


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index e28b785910..8ce6d8c644 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -160,7 +160,7 @@ void GameState::initEngineVars() {
 
 	gameChapter = 0;
 
-	_engine->_text->currentTextBank = 0;
+	_engine->_scene->sceneTextBank = TextBankId::Options_and_menus;
 	_engine->_scene->currentlyFollowedActor = 0;
 	_engine->_actor->heroBehaviour = kNormal;
 	_engine->_actor->previousHeroAngle = 0;
@@ -401,7 +401,7 @@ void GameState::processFoundItem(int32 item) {
 	}
 
 	initEngineProjections();
-	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 	_engine->_text->stopVox(_engine->_text->currDialTextEntry);
 
 	_engine->_scene->sceneHero->animTimerData = tmpAnimTimer;
@@ -411,7 +411,7 @@ void GameState::processGameChoices(int32 choiceIdx) {
 	_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
 
 	gameChoicesSettings.reset();
-	gameChoicesSettings.setHeadlineTextId(_engine->_text->currentTextBank + 3);
+	gameChoicesSettings.setTextBankId(_engine->_scene->sceneTextBank + 3);
 
 	// filled via script
 	for (int32 i = 0; i < numChoices; i++) {
diff --git a/engines/twine/holomap.cpp b/engines/twine/holomap.cpp
index aa98cad2c2..7578b8c924 100644
--- a/engines/twine/holomap.cpp
+++ b/engines/twine/holomap.cpp
@@ -145,7 +145,7 @@ void Holomap::processHolomap() {
 	_engine->_scene->betaLight = betaLightTmp;
 	_engine->_gameState->initEngineVars();
 
-	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 
 	// TODO memcopy reset palette
 
diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index 9e062f2211..2e8609b0f2 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -583,7 +583,7 @@ void Menu::inGameOptionsMenu() {
 	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 	_engine->_menu->optionsMenuState.setButtonTextId(0, TextId::kReturnGame);
 	_engine->_menu->optionsMenu();
-	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 	optionsMenuState.setButtonTextId(0, TextId::kReturnMenu);
 }
 
@@ -695,7 +695,7 @@ int32 Menu::giveupMenu() {
 		default:
 			warning("Unknown menu button handled: %i", menuId);
 		}
-		_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+		_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 		_engine->_system->delayMillis(1000 / _engine->cfgfile.Fps);
 	} while (menuId != TextId::kGiveUp && menuId != TextId::kContinue);
 
@@ -852,8 +852,8 @@ void Menu::processBehaviourMenu() {
 
 	_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
 
-	int32 tmpTextBank = _engine->_text->currentTextBank;
-	_engine->_text->currentTextBank = -1;
+	int32 tmpTextBank = _engine->_scene->sceneTextBank;
+	_engine->_scene->sceneTextBank = TextBankId::None;
 
 	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 
@@ -901,8 +901,8 @@ void Menu::processBehaviourMenu() {
 	_engine->_actor->setBehaviour(_engine->_actor->heroBehaviour);
 	_engine->_gameState->initEngineProjections();
 
-	_engine->_text->currentTextBank = tmpTextBank;
-	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	_engine->_scene->sceneTextBank = tmpTextBank;
+	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 }
 
 void Menu::drawMagicItemsBox(int32 left, int32 top, int32 right, int32 bottom, int32 color) { // Rect
@@ -1076,7 +1076,7 @@ void Menu::processInventoryMenu() {
 
 	_engine->_gameState->initEngineProjections();
 
-	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 }
 
 } // namespace TwinE
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index c76865cf00..2d6c1ba3bd 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -104,8 +104,8 @@ public:
 		return _settings[MenuSettings_NumberOfButtons];
 	}
 
-	void setHeadlineTextId(int16 textIndex) {
-		_settings[MenuSettings_HeaderEnd] = textIndex;
+	void setTextBankId(int16 textBankIndex) {
+		_settings[MenuSettings_HeaderEnd] = textBankIndex;
 	}
 
 	void addButton(int16 textId, int16 state = 0) {
diff --git a/engines/twine/scene.cpp b/engines/twine/scene.cpp
index 6a0603bc1a..1f07896ecb 100644
--- a/engines/twine/scene.cpp
+++ b/engines/twine/scene.cpp
@@ -97,7 +97,7 @@ bool Scene::loadSceneLBA1() {
 	Common::MemoryReadStream stream(currentScene, _currentSceneSize);
 
 	// load scene ambience properties
-	_engine->_text->currentTextBank = stream.readByte();
+	sceneTextBank = stream.readByte();
 	currentGameOverScene = stream.readByte();
 	stream.skip(4);
 
@@ -277,10 +277,10 @@ void Scene::changeScene() {
 	// TODO: treat holomap trajectories
 
 	if (needChangeScene == LBA1SceneId::Citadel_Island_end_sequence_1 || needChangeScene == LBA1SceneId::Citadel_Island_end_sequence_2) {
-		_engine->_text->currentTextBank = 10;
+		_engine->_scene->sceneTextBank = TextBankId::Tippet_Island;
 	}
 
-	_engine->_text->initTextBank(_engine->_text->currentTextBank + 3);
+	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 	_engine->_grid->initGrid(needChangeScene);
 
 	if (heroPositionType == ScenePositionType::kZone) {
diff --git a/engines/twine/scene.h b/engines/twine/scene.h
index 87c940dd9b..9716a66828 100644
--- a/engines/twine/scene.h
+++ b/engines/twine/scene.h
@@ -26,6 +26,7 @@
 #include "common/scummsys.h"
 #include "common/util.h"
 #include "twine/actor.h"
+#include "twine/text.h"
 
 namespace TwinE {
 
@@ -251,6 +252,7 @@ public:
 	int32 currentSceneIdx = LBA1SceneId::Citadel_Island_Prison;
 	int32 previousSceneIdx = LBA1SceneId::Citadel_Island_Prison;
 
+	int32 sceneTextBank = TextBankId::None;
 	int32 currentGameOverScene = 0;
 	int32 alphaLight = 0;
 	int32 betaLight = 0;
diff --git a/engines/twine/text.cpp b/engines/twine/text.cpp
index c15e25684d..488b0733ee 100644
--- a/engines/twine/text.cpp
+++ b/engines/twine/text.cpp
@@ -31,6 +31,7 @@
 #include "twine/renderer.h"
 #include "twine/resources.h"
 #include "twine/screens.h"
+#include "twine/scene.h"
 #include "twine/sound.h"
 #include "twine/twine.h"
 
@@ -717,7 +718,7 @@ void Text::copyText(const char *src, char *dst, int32 size) {
 
 bool Text::getMenuText(int32 index, char *text, uint32 textSize) {
 	if (index == _engine->_menu->currMenuTextIndex) {
-		if (_engine->_menu->currMenuTextBank == currentTextBank) {
+		if (_engine->_menu->currMenuTextBank == _engine->_scene->sceneTextBank) {
 			Common::strlcpy(text, _engine->_menu->currMenuTextBuffer, textSize);
 			return true;
 		}
@@ -737,7 +738,7 @@ bool Text::getMenuText(int32 index, char *text, uint32 textSize) {
 	copyText(text, _engine->_menu->currMenuTextBuffer, currDialTextSize);
 
 	_engine->_menu->currMenuTextIndex = index;
-	_engine->_menu->currMenuTextBank = currentTextBank;
+	_engine->_menu->currMenuTextBank = _engine->_scene->sceneTextBank;
 	return true;
 }
 
diff --git a/engines/twine/text.h b/engines/twine/text.h
index ad673adb88..2da619c1c2 100644
--- a/engines/twine/text.h
+++ b/engines/twine/text.h
@@ -140,7 +140,7 @@ private:
 	void copyText(const char *src, char *dst, int32 size);
 
 	// RECHECK THIS LATER
-	int32 currentBankIdx = -1; // textVar1
+	int32 currentBankIdx = TextBankId::None; // textVar1
 	char textVar2[256] {'\0'};
 
 	/** Dialogue text pointer */
@@ -178,9 +178,6 @@ private:
 public:
 	Text(TwinEEngine *engine) : _engine(engine) {}
 
-	/** Current text bank */
-	int32 currentTextBank = -1;
-
 	/** Dialogue text size */
 	int32 dialTextSize = 0;
 	/** Pixel size between dialogue text */
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 9fb51255c3..39ee1f05d2 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -506,7 +506,7 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 				cfgfile.FlagDisplayText = tmpFlagDisplayText;
 				_text->textClipSmall();
 				_text->newGameVar4 = 1;
-				_text->initTextBank(_text->currentTextBank + 3);
+				_text->initTextBank(_scene->sceneTextBank + 3);
 				_screens->fadeToBlack(_screens->paletteRGBACustom);
 				_screens->clearScreen();
 				flip();
@@ -557,7 +557,7 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 				_text->setFontCrossColor(15);
 				_text->drawTextFullscreen(162);
 				_text->textClipSmall();
-				_text->initTextBank(_text->currentTextBank + 3);
+				_text->initTextBank(_scene->sceneTextBank + 3);
 				break;
 			}
 			case kiCloverLeaf:


Commit: f560390f7e22149ffaad0ff37872a9760bc00871
    https://github.com/scummvm/scummvm/commit/f560390f7e22149ffaad0ff37872a9760bc00871
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: implement load-savegame menu

also save game loading from launcher is supported now

Changed paths:
    engines/twine/gamestate.cpp
    engines/twine/menu.cpp
    engines/twine/menu.h
    engines/twine/menuoptions.cpp
    engines/twine/menuoptions.h
    engines/twine/metaengine.cpp
    engines/twine/twine.cpp
    engines/twine/twine.h


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index 8ce6d8c644..180e63d5ba 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -53,7 +53,7 @@ GameState::GameState(TwinEEngine *engine) : _engine(engine) {
 	Common::fill(&gameFlags[0], &gameFlags[256], 0);
 	Common::fill(&inventoryFlags[0], &inventoryFlags[NUM_INVENTORY_ITEMS], 0);
 	Common::fill(&holomapFlags[0], &holomapFlags[150], 0);
-	playerName[0] = 0;
+	playerName[0] = '\0';
 	Common::fill(&gameChoices[0], &gameChoices[10], 0);
 }
 
@@ -172,6 +172,8 @@ bool GameState::loadGame(Common::SeekableReadStream *file) {
 		return false;
 	}
 
+	initEngineVars();
+
 	file->skip(1); // skip save game id
 
 	int playerNameIdx = 0;
@@ -192,7 +194,7 @@ bool GameState::loadGame(Common::SeekableReadStream *file) {
 		warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS, numGameFlags);
 		return false;
 	}
-	file->read(gameFlags, numGameFlags);
+	file->read(gameFlags, NUM_GAME_FLAGS);
 	_engine->_scene->needChangeScene = file->readByte(); // scene index
 	gameChapter = file->readByte();
 
@@ -210,21 +212,22 @@ bool GameState::loadGame(Common::SeekableReadStream *file) {
 	_engine->_actor->previousHeroAngle = _engine->_scene->sceneHero->angle;
 	_engine->_scene->sceneHero->body = file->readByte();
 
-	const byte numHolemapFlags = file->readByte(); // number of holomap locations, always 150
-	if (numHolemapFlags != ARRAYSIZE(holomapFlags)) {
-		warning("Failed to load holomapflags");
+	const byte numHolomapFlags = file->readByte(); // number of holomap locations, always 150
+	const int32 expectedHolomapFlagsSize = sizeof(holomapFlags);
+	if (numHolomapFlags != expectedHolomapFlagsSize) {
+		warning("Failed to load holomapflags. Got %u, expected %i", numHolomapFlags, expectedHolomapFlagsSize);
 		return false;
 	}
-	file->read(holomapFlags, numHolemapFlags);
+	file->read(holomapFlags, sizeof(holomapFlags));
 
 	inventoryNumGas = file->readByte();
 
 	const byte numInventoryFlags = file->readByte(); // number of used inventory items, always 28
 	if (numInventoryFlags != NUM_INVENTORY_ITEMS) {
-		warning("Failed to load inventoryFlags");
+		warning("Failed to load inventoryFlags. Got %u, expected %i", numInventoryFlags, NUM_INVENTORY_ITEMS);
 		return false;
 	}
-	file->read(inventoryFlags, numInventoryFlags);
+	file->read(inventoryFlags, NUM_INVENTORY_ITEMS);
 
 	inventoryNumLeafs = file->readByte();
 	usingSabre = file->readByte();
@@ -243,7 +246,7 @@ bool GameState::saveGame(Common::WriteStream *file) {
 	file->writeString(playerName);
 	file->writeByte('\0');
 	file->writeByte(NUM_GAME_FLAGS);
-	file->write(gameFlags, sizeof(gameFlags));
+	file->write(gameFlags, NUM_GAME_FLAGS);
 	file->writeByte(_engine->_scene->currentSceneIdx);
 	file->writeByte(gameChapter);
 	file->writeByte(_engine->_actor->heroBehaviour);
@@ -259,14 +262,14 @@ bool GameState::saveGame(Common::WriteStream *file) {
 	file->writeByte(_engine->_scene->sceneHero->body);
 
 	// number of holomap locations
-	file->writeByte(ARRAYSIZE(holomapFlags));
+	file->writeByte(sizeof(holomapFlags));
 	file->write(holomapFlags, sizeof(holomapFlags));
 
 	file->writeByte(inventoryNumGas);
 
 	// number of inventory items
-	file->writeByte(ARRAYSIZE(inventoryFlags));
-	file->write(inventoryFlags, sizeof(inventoryFlags));
+	file->writeByte(NUM_INVENTORY_ITEMS);
+	file->write(inventoryFlags, NUM_INVENTORY_ITEMS);
 
 	file->writeByte(inventoryNumLeafs);
 	file->writeByte(usingSabre);
diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp
index 2e8609b0f2..a56c0a2135 100644
--- a/engines/twine/menu.cpp
+++ b/engines/twine/menu.cpp
@@ -147,13 +147,14 @@ static MenuSettings createVolumeMenu() {
 
 } // namespace _priv
 
-const char *MenuSettings::getButtonText(Text *text, int buttonIndex) const {
-	const int32 textId = getButtonTextId(buttonIndex);
-	static char dialText[256];
-	if (!text->getMenuText(textId, dialText, sizeof(dialText))) {
-		dialText[0] = '\0';
+const char *MenuSettings::getButtonText(Text *text, int buttonIndex) {
+	if (_buttonTexts[buttonIndex].empty()) {
+		const int32 textId = getButtonTextId(buttonIndex);
+		char dialText[256] = "";
+		text->getMenuText(textId, dialText, sizeof(dialText));
+		_buttonTexts[buttonIndex] = dialText;
 	}
-	return dialText;
+	return _buttonTexts[buttonIndex].c_str();
 }
 
 Menu::Menu(TwinEEngine *engine) {
@@ -307,7 +308,7 @@ void Menu::drawButtonGfx(int32 width, int32 topheight, int32 buttonId, const cha
 	_engine->copyBlockPhys(left, top, right, bottom);
 }
 
-void Menu::drawButtons(const MenuSettings *menuSettings, bool hover) {
+void Menu::drawButtons(MenuSettings *menuSettings, bool hover) {
 	int16 buttonNumber = menuSettings->getActiveButton();
 	const int32 maxButton = menuSettings->getButtonCount();
 	int32 topHeight = menuSettings->getButtonBoxHeight();
@@ -496,7 +497,6 @@ int32 Menu::processMenu(MenuSettings *menuSettings) {
 		if (_engine->shouldQuit()) {
 			return kQuitEngine;
 		}
-		// TODO: update volume settings
 		_engine->_system->delayMillis(10);
 	} while (!_engine->_input->toggleActionIfActive(TwinEActionType::UIEnter));
 
@@ -536,10 +536,10 @@ int32 Menu::savemanageMenu() {
 		case TextId::kReturnMenu:
 			return 0;
 		case TextId::kCreateSaveGame:
-			// TODO: implement save game handling and slot rendering
+			_engine->_menuOptions->saveGameMenu();
 			break;
 		case TextId::kDeleteSaveGame:
-			// TODO: implement save game deletion and slot rendering
+			_engine->_menuOptions->deleteSaveMenu();
 			break;
 		case kQuitEngine:
 			return kQuitEngine;
@@ -688,7 +688,7 @@ int32 Menu::giveupMenu() {
 			_engine->_sound->stopSamples();
 			return 1;
 		case TextId::kCreateSaveGame:
-			// TODO: handle save game creation
+			_engine->_menuOptions->saveGameMenu();
 			break;
 		case kQuitEngine:
 			return kQuitEngine;
diff --git a/engines/twine/menu.h b/engines/twine/menu.h
index 2d6c1ba3bd..272ad03af3 100644
--- a/engines/twine/menu.h
+++ b/engines/twine/menu.h
@@ -29,6 +29,7 @@
 
 namespace TwinE {
 
+#define MAX_BUTTONS 10
 #define PLASMA_WIDTH 320
 #define PLASMA_HEIGHT 50
 #define kQuitEngine 9998
@@ -46,7 +47,8 @@ private:
 		MenuSettings_FirstButton = 5
 	};
 
-	int16 _settings[100] {0};
+	int16 _settings[4 + MAX_BUTTONS * 2] {0};
+	Common::String _buttonTexts[MAX_BUTTONS];
 	int8 _activeButtonIdx = 0;
 
 	int16 getButtonTextId(int buttonIndex) const {
@@ -55,6 +57,9 @@ private:
 
 public:
 	void reset() {
+		for (int32 i = 0; i < MAX_BUTTONS; ++i) {
+			_buttonTexts[i] = "";
+		}
 		_settings[MenuSettings_NumberOfButtons] = 0;
 		setButtonsBoxHeight(0);
 		setActiveButton(0);
@@ -90,7 +95,7 @@ public:
 		return _settings[MenuSettings_FirstButtonState + buttonIndex * 2];
 	}
 
-	const char *getButtonText(Text *text, int buttonIndex) const;
+	const char *getButtonText(Text *text, int buttonIndex);
 
 	int16 getActiveButton() const {
 		return _activeButtonIdx;
@@ -114,6 +119,15 @@ public:
 		_settings[i * 2 + MenuSettings_FirstButton] = textId;
 		++_settings[MenuSettings_NumberOfButtons];
 	}
+
+	void addButton(const char *text, int16 state = 0) {
+		const int16 i = _settings[MenuSettings_NumberOfButtons];
+		_settings[i * 2 + MenuSettings_FirstButtonState] = state;
+		// will return the button index
+		_settings[i * 2 + MenuSettings_FirstButton] = i;
+		_buttonTexts[i] = text;
+		++_settings[MenuSettings_NumberOfButtons];
+	}
 };
 
 class Menu {
@@ -144,7 +158,7 @@ private:
 	 * @param data menu settings array
 	 * @param mode flag to know if should draw as a hover button or not
 	 */
-	void drawButtons(const MenuSettings *menuSettings, bool hover);
+	void drawButtons(MenuSettings *menuSettings, bool hover);
 	/** Used to run the advanced options menu */
 	int32 advoptionsMenu();
 	/** Used to run the volume menu */
diff --git a/engines/twine/menuoptions.cpp b/engines/twine/menuoptions.cpp
index 4bacb6683c..7d51ea56c3 100644
--- a/engines/twine/menuoptions.cpp
+++ b/engines/twine/menuoptions.cpp
@@ -26,6 +26,7 @@
 #include "common/str-array.h"
 #include "common/system.h"
 #include "common/util.h"
+#include "savestate.h"
 #include "twine/flamovies.h"
 #include "twine/gamestate.h"
 #include "twine/input.h"
@@ -191,8 +192,6 @@ public:
 };
 
 bool MenuOptions::enterPlayerName(int32 textIdx) {
-	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
-	_engine->flip();
 	playerName[0] = '\0'; // TODO: read from settings?
 	_engine->_text->initTextBank(TextBankId::Options_and_menus);
 	char buffer[256];
@@ -312,6 +311,8 @@ bool MenuOptions::enterPlayerName(int32 textIdx) {
 }
 
 bool MenuOptions::newGameMenu() {
+	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
+	_engine->flip();
 	if (!enterPlayerName(TextId::kEnterYourName)) {
 		return false;
 	}
@@ -320,38 +321,87 @@ bool MenuOptions::newGameMenu() {
 	return true;
 }
 
-int MenuOptions::chooseSave(int textIdx) {
-	const Common::StringArray& savegames = _engine->getSaveSlots();
-	if (savegames.empty()) {
+int MenuOptions::chooseSave(int textIdx, bool showEmptySlots) {
+	const SaveStateList& savegames = _engine->getSaveSlots();
+	if (savegames.empty() && !showEmptySlots) {
 		return -1;
 	}
-	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
-	_engine->flip();
-	do {
-		// TODO: assemble menu with save slots and make then loadable.
-		_engine->_text->initTextBank(TextBankId::Options_and_menus);
-		char buffer[256];
-		_engine->_text->getMenuText(textIdx, buffer, sizeof(buffer));
-		_engine->_text->setFontColor(15);
-		const int halfScreenWidth = (SCREEN_WIDTH / 2);
-		_engine->_text->drawText(halfScreenWidth - (_engine->_text->getTextSize(buffer) / 2), 20, buffer);
-		_engine->copyBlockPhys(0, 0, SCREEN_WIDTH - 1, 99);
-		_engine->flip();
 
-		if (_engine->shouldQuit()) {
-			return kQuitEngine;
+	_engine->_text->initTextBank(TextBankId::Options_and_menus);
+
+	MenuSettings saveFiles;
+	saveFiles.addButton(TextId::kReturnMenu);
+
+	const int maxButtons = _engine->getMetaEngine().getMaximumSaveSlot() + 1;
+	for (const SaveStateDescriptor& savegame : savegames) {
+		saveFiles.addButton(savegame.getDescription().encode().c_str(), savegame.getSaveSlot());
+		if (saveFiles.getButtonCount() >= maxButtons) {
+			break;
 		}
-		_engine->_system->delayMillis(1);
-	} while (_engine->_input->toggleAbortAction());
+	}
+
+	if (showEmptySlots) {
+		while (saveFiles.getButtonCount() < maxButtons) {
+			saveFiles.addButton("EMPTY");
+		}
+	}
+
+	for (;;) {
+		const int32 id = _engine->_menu->processMenu(&saveFiles);
+		switch (id) {
+		case kQuitEngine:
+		case TextId::kReturnMenu:
+			return -1;
+		default:
+			// the first button is the back button - to subtract that one again to get the real slot index
+			return saveFiles.getButtonState(id);
+		}
+	}
 
-	return 0;
+	return -1;
 }
 
 bool MenuOptions::continueGameMenu() {
+	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
+	_engine->flip();
 	const int slot = chooseSave(TextId::kContinueGame);
 	if (slot >= 0) {
-		_engine->_gameState->initEngineVars();
-		return _engine->loadGameState(slot).getCode() == Common::kNoError;
+		debug("Load slot %i", slot);
+		Common::Error state = _engine->loadGameState(slot);
+		if (state.getCode() != Common::kNoError) {
+			error("Failed to load slot %i", slot);
+			return false;
+		}
+
+		return true;
+	}
+	return false;
+}
+
+bool MenuOptions::deleteSaveMenu() {
+	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
+	_engine->flip();
+	const int slot = chooseSave(TextId::kDeleteSaveGame);
+	if (slot >= 0) {
+		_engine->wipeSaveSlot(slot);
+		return true;
+	}
+	return false;
+}
+
+bool MenuOptions::saveGameMenu() {
+	_engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer);
+	_engine->flip();
+	const int slot = chooseSave(TextId::kCreateSaveGame, true);
+	if (slot >= 0) {
+		// TODO: enter description
+		Common::Error state = _engine->saveGameState(slot, "description", false);
+		if (state.getCode() != Common::kNoError) {
+			error("Failed to save slot %i", slot);
+			return false;
+		}
+
+		return true;
 	}
 	return false;
 }
diff --git a/engines/twine/menuoptions.h b/engines/twine/menuoptions.h
index f7e044eb80..9054eb1309 100644
--- a/engines/twine/menuoptions.h
+++ b/engines/twine/menuoptions.h
@@ -40,7 +40,7 @@ private:
 	void drawSelectableCharacters();
 	void drawPlayerName(int32 centerx, int32 top, int32 type);
 	void drawSelectableCharacter(int32 x, int32 y, bool selected);
-	int chooseSave(int textIdx);
+	int chooseSave(int textIdx, bool showEmptySlots = false);
 
 public:
 	MenuOptions(TwinEEngine *engine) : _engine(engine) {}
@@ -56,6 +56,8 @@ public:
 
 	/** Main menu continue game options */
 	bool continueGameMenu();
+	bool saveGameMenu();
+	bool deleteSaveMenu();
 };
 
 } // namespace TwinE
diff --git a/engines/twine/metaengine.cpp b/engines/twine/metaengine.cpp
index 3393169a51..a7bf21fcd6 100644
--- a/engines/twine/metaengine.cpp
+++ b/engines/twine/metaengine.cpp
@@ -41,6 +41,10 @@ public:
 		return "TwinE";
 	}
 
+	int getMaximumSaveSlot() const override {
+		return 6;
+	}
+
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
 		if (desc) {
 			TwineGameType gameType = TwineGameType::GType_LBA;
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 39ee1f05d2..058904c952 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -152,6 +152,16 @@ Common::Error TwinEEngine::run() {
 	_screens->copyScreen(frontVideoBuffer, workVideoBuffer);
 
 	_menu->init();
+
+	if (ConfMan.hasKey("save_slot")) {
+		const int saveSlot = ConfMan.getInt("save_slot");
+		if (saveSlot >= 0 && saveSlot <= 999) {
+			Common::Error state = loadGameState(saveSlot);
+			if (state.getCode() != Common::kNoError) {
+				return state;
+			}
+		}
+	}
 	while (!shouldQuit()) {
 		readKeys();
 		switch (_state) {
@@ -207,10 +217,8 @@ bool TwinEEngine::hasFeature(EngineFeature f) const {
 	return false;
 }
 
-Common::StringArray TwinEEngine::getSaveSlots() {
-	Common::SaveFileManager *saveFileMan = getSaveFileManager();
-	const Common::String pattern(getMetaEngine().getSavegameFilePattern(_targetName.c_str()));
-	return saveFileMan->listSavefiles(pattern);
+SaveStateList TwinEEngine::getSaveSlots() const {
+	return getMetaEngine().listSaves(_targetName.c_str());
 }
 
 void TwinEEngine::wipeSaveSlot(int slot) {
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index 735f5c661b..071cc99289 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -184,7 +184,7 @@ public:
 	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 
 	void wipeSaveSlot(int slot);
-	Common::StringArray getSaveSlots();
+	SaveStateList getSaveSlots() const;
 	void autoSave();
 
 	bool isLBA1() const { return _gameType == TwineGameType::GType_LBA; };


Commit: d5e0874557b3b2966531d5c0edb80deb2262ddaa
    https://github.com/scummvm/scummvm/commit/d5e0874557b3b2966531d5c0edb80deb2262ddaa
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:36+01:00

Commit Message:
TWINE: reduced visibility

Changed paths:
    engines/twine/grid.cpp
    engines/twine/interface.h


diff --git a/engines/twine/grid.cpp b/engines/twine/grid.cpp
index 53b7735a59..6b549e1472 100644
--- a/engines/twine/grid.cpp
+++ b/engines/twine/grid.cpp
@@ -573,9 +573,9 @@ void Grid::getBrickPos(int32 x, int32 y, int32 z) {
 void Grid::drawColumnGrid(int32 blockIdx, int32 brickBlockIdx, int32 x, int32 y, int32 z) {
 	const uint8 *blockPtr = getBlockLibrary(blockIdx) + 3 + brickBlockIdx * 4;
 
-	uint8 brickShape = *((const uint8 *)(blockPtr + 0));
-	uint8 brickSound = *((const uint8 *)(blockPtr + 1));
-	uint16 brickIdx = *((const uint16 *)(blockPtr + 2));
+	const uint8 brickShape = *((const uint8 *)(blockPtr + 0));
+	const uint8 brickSound = *((const uint8 *)(blockPtr + 1));
+	const uint16 brickIdx = *((const uint16 *)(blockPtr + 2));
 	if (!brickIdx) {
 		return;
 	}
diff --git a/engines/twine/interface.h b/engines/twine/interface.h
index b3108194f1..d0e848dba7 100644
--- a/engines/twine/interface.h
+++ b/engines/twine/interface.h
@@ -42,17 +42,17 @@ class Interface {
 private:
 	TwinEEngine *_engine;
 	int32 checkClipping(int32 x, int32 y);
+	int32 textWindowTopSave = 0;
+	int32 textWindowLeftSave = 0;
+	int32 textWindowRightSave = 0;
+	int32 textWindowBottomSave = 0;
 
 public:
 	Interface(TwinEEngine *engine);
 	int32 textWindowTop = 0;
-	int32 textWindowTopSave = 0;
 	int32 textWindowLeft = 0;
-	int32 textWindowLeftSave = 0;
 	int32 textWindowRight = 0;
-	int32 textWindowRightSave = 0;
 	int32 textWindowBottom = 0;
-	int32 textWindowBottomSave = 0;
 
 	/**
 	 * Draw button line


Commit: d3c2f7cd9132a773d899e51f564092c212099a75
    https://github.com/scummvm/scummvm/commit/d3c2f7cd9132a773d899e51f564092c212099a75
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: added ScopedEngineFreeze

Changed paths:
    engines/twine/extra.cpp
    engines/twine/holomap.cpp
    engines/twine/scene.cpp
    engines/twine/script_life_v1.cpp
    engines/twine/twine.cpp
    engines/twine/twine.h


diff --git a/engines/twine/extra.cpp b/engines/twine/extra.cpp
index 7280ff8754..3794a08d45 100644
--- a/engines/twine/extra.cpp
+++ b/engines/twine/extra.cpp
@@ -534,7 +534,6 @@ void Extra::processMagicballBounce(ExtraListStruct *extra, int32 x, int32 y, int
 	extra->lifeTime = _engine->lbaTime;
 }
 
-/** Process extras */
 void Extra::processExtras() {
 	int32 currentExtraX = 0;
 	int32 currentExtraY = 0;
diff --git a/engines/twine/holomap.cpp b/engines/twine/holomap.cpp
index 7578b8c924..f15ee1afcf 100644
--- a/engines/twine/holomap.cpp
+++ b/engines/twine/holomap.cpp
@@ -116,7 +116,7 @@ void Holomap::drawHolomapTrajectory(int32 trajectoryIndex) {
 }
 
 void Holomap::processHolomap() {
-	_engine->freezeTime();
+	ScopedEngineFreeze freeze(_engine);
 
 	// TODO memcopy palette
 
@@ -148,8 +148,6 @@ void Holomap::processHolomap() {
 	_engine->_text->initTextBank(_engine->_scene->sceneTextBank + 3);
 
 	// TODO memcopy reset palette
-
-	_engine->unfreezeTime();
 }
 
 } // namespace TwinE
diff --git a/engines/twine/scene.cpp b/engines/twine/scene.cpp
index 1f07896ecb..0b93ab2f69 100644
--- a/engines/twine/scene.cpp
+++ b/engines/twine/scene.cpp
@@ -464,9 +464,8 @@ void Scene::processActorZones(int32 actorIdx) {
 
 						_engine->_grid->useCellingGrid = zone->infoData.CeillingGrid.newGrid;
 						_engine->_grid->cellingGridIdx = z;
-						_engine->freezeTime();
+						ScopedEngineFreeze freeze(_engine);
 						_engine->_grid->initCellingGrid(_engine->_grid->useCellingGrid);
-						_engine->unfreezeTime();
 					}
 				}
 				break;
diff --git a/engines/twine/script_life_v1.cpp b/engines/twine/script_life_v1.cpp
index 365c03243e..ef08c1cc15 100644
--- a/engines/twine/script_life_v1.cpp
+++ b/engines/twine/script_life_v1.cpp
@@ -1176,9 +1176,8 @@ static int32 lSAY_MESSAGE(TwinEEngine *engine, ScriptContext& ctx) {
 
 	engine->_redraw->addOverlay(koText, textEntry, 0, 0, ctx.actorIdx, koFollowActor, 2);
 
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	engine->_text->initVoxToPlay(textEntry);
-	engine->unfreezeTime();
 
 	return 0;
 }
@@ -1191,9 +1190,8 @@ static int32 lSAY_MESSAGE_OBJ(TwinEEngine *engine, ScriptContext& ctx) {
 
 	engine->_redraw->addOverlay(koText, textEntry, 0, 0, otherActorIdx, koFollowActor, 2);
 
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	engine->_text->initVoxToPlay(textEntry);
-	engine->unfreezeTime();
 
 	return 0;
 }
@@ -1228,63 +1226,57 @@ static int32 lGRM_OFF(TwinEEngine *engine, ScriptContext& ctx) {
 
 /*0x52*/
 static int32 lFADE_PAL_RED(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	engine->_screens->fadePalRed(engine->_screens->mainPaletteRGBA);
 	engine->_screens->useAlternatePalette = false;
-	engine->unfreezeTime();
 	return 0;
 }
 
 /*0x53*/
 static int32 lFADE_ALARM_RED(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	HQR::getEntry(engine->_screens->palette, Resources::HQR_RESS_FILE, RESSHQR_ALARMREDPAL);
 	engine->_screens->convertPalToRGBA(engine->_screens->palette, engine->_screens->paletteRGBA);
 	engine->_screens->fadePalRed(engine->_screens->paletteRGBA);
 	engine->_screens->useAlternatePalette = true;
-	engine->unfreezeTime();
 	return 0;
 }
 
 /*0x54*/
 static int32 lFADE_ALARM_PAL(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	HQR::getEntry(engine->_screens->palette, Resources::HQR_RESS_FILE, RESSHQR_ALARMREDPAL);
 	engine->_screens->convertPalToRGBA(engine->_screens->palette, engine->_screens->paletteRGBA);
 	engine->_screens->adjustCrossPalette(engine->_screens->paletteRGBA, engine->_screens->mainPaletteRGBA);
 	engine->_screens->useAlternatePalette = false;
-	engine->unfreezeTime();
 	return 0;
 }
 
 /*0x55*/
 static int32 lFADE_RED_PAL(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	engine->_screens->fadeRedPal(engine->_screens->mainPaletteRGBA);
 	engine->_screens->useAlternatePalette = false;
-	engine->unfreezeTime();
 	return 0;
 }
 
 /*0x56*/
 static int32 lFADE_RED_ALARM(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	HQR::getEntry(engine->_screens->palette, Resources::HQR_RESS_FILE, RESSHQR_ALARMREDPAL);
 	engine->_screens->convertPalToRGBA(engine->_screens->palette, engine->_screens->paletteRGBA);
 	engine->_screens->fadeRedPal(engine->_screens->paletteRGBA);
 	engine->_screens->useAlternatePalette = true;
-	engine->unfreezeTime();
 	return 0;
 }
 
 /*0x57*/
 static int32 lFADE_PAL_ALARM(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	HQR::getEntry(engine->_screens->palette, Resources::HQR_RESS_FILE, RESSHQR_ALARMREDPAL);
 	engine->_screens->convertPalToRGBA(engine->_screens->palette, engine->_screens->paletteRGBA);
 	engine->_screens->adjustCrossPalette(engine->_screens->mainPaletteRGBA, engine->_screens->paletteRGBA);
 	engine->_screens->useAlternatePalette = true;
-	engine->unfreezeTime();
 	return 0;
 }
 
@@ -1331,14 +1323,13 @@ static int32 lASK_CHOICE_OBJ(TwinEEngine *engine, ScriptContext& ctx) {
 
 /*0x5C*/
 static int32 lSET_DARK_PAL(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	HQR::getEntry(engine->_screens->palette, Resources::HQR_RESS_FILE, RESSHQR_DARKPAL);
 	if (!engine->_screens->lockPalette) {
 		engine->_screens->convertPalToRGBA(engine->_screens->palette, engine->_screens->paletteRGBA);
 		engine->setPalette(engine->_screens->paletteRGBA);
 	}
 	engine->_screens->useAlternatePalette = true;
-	engine->unfreezeTime();
 	return 0;
 }
 
@@ -1353,7 +1344,7 @@ static int32 lSET_NORMAL_PAL(TwinEEngine *engine, ScriptContext& ctx) {
 
 /*0x5E*/
 static int32 lMESSAGE_SENDELL(TwinEEngine *engine, ScriptContext& ctx) {
-	engine->freezeTime();
+	ScopedEngineFreeze scoped(engine);
 	engine->_screens->fadeToBlack(engine->_screens->paletteRGBA);
 	engine->_screens->loadImage(25);
 	engine->_text->textClipFull();
@@ -1374,8 +1365,6 @@ static int32 lMESSAGE_SENDELL(TwinEEngine *engine, ScriptContext& ctx) {
 		engine->_system->delayMillis(1);
 	} while (engine->_input->toggleAbortAction());
 
-	engine->unfreezeTime();
-
 	return 0;
 }
 
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 058904c952..b99d32cba1 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -71,6 +71,14 @@
 
 namespace TwinE {
 
+ScopedEngineFreeze::ScopedEngineFreeze(TwinEEngine* engine) : _engine(engine) {
+	_engine->freezeTime();
+}
+
+ScopedEngineFreeze::~ScopedEngineFreeze() {
+	_engine->unfreezeTime();
+}
+
 TwinEEngine::TwinEEngine(OSystem *system, Common::Language language, uint32 flags, TwineGameType gameType)
     : Engine(system), _gameType(gameType), _gameLang(language), _gameFlags(flags), _rnd("twine") {
 	// Add default file directories
@@ -452,10 +460,9 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 			if (giveUp == 1) {
 				unfreezeTime();
 				_redraw->redrawEngineActions(1);
-				freezeTime();
+				ScopedEngineFreeze freeze(this);
 				autoSave();
 				quitGame = 0;
-				unfreezeTime();
 				return 0;
 			}
 			unfreezeTime();
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index 071cc99289..a445a93754 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -161,6 +161,12 @@ enum class EngineState {
 	QuitGame
 };
 
+struct ScopedEngineFreeze {
+	TwinEEngine* _engine;
+	ScopedEngineFreeze(TwinEEngine* engine);
+	~ScopedEngineFreeze();
+};
+
 class TwinEEngine : public Engine {
 private:
 	int32 isTimeFreezed = 0;


Commit: a7f3a6345402f47f471718854946e0bc2f104917
    https://github.com/scummvm/scummvm/commit/a7f3a6345402f47f471718854946e0bc2f104917
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: reduced scoped + const

Changed paths:
    engines/twine/movements.cpp
    engines/twine/redraw.cpp


diff --git a/engines/twine/movements.cpp b/engines/twine/movements.cpp
index 740c338930..264d843322 100644
--- a/engines/twine/movements.cpp
+++ b/engines/twine/movements.cpp
@@ -87,17 +87,16 @@ int32 Movements::getAngleAndSetTargetActorDistance(int32 x1, int32 z1, int32 x2,
     return (256 + ((int32)floor((-1024 * atan2((int64)(z2-z1), (int32)(x2-x1))) / (2*M_PI)))) % 1024;
 	*/
 
-	int32 newX, newZ, difX, difZ, tmpX, tmpZ, tmpEx, flag, destAngle, startAngle, finalAngle;
+	int32 difZ = z2 - z1;
+	const int32 newZ = difZ * difZ;
 
-	difZ = tmpZ = z2 - z1;
-	newZ = tmpZ * tmpZ;
-
-	difX = tmpX = x2 - x1;
-	newX = tmpX * tmpX;
+	int32 difX = x2 - x1;
+	const int32 newX = difX * difX;
 
+	int32 flag;
 	// Exchange X and Z
 	if (newX < newZ) {
-		tmpEx = difX;
+		const int32 tmpEx = difX;
 		difX = difZ;
 		difZ = tmpEx;
 
@@ -112,9 +111,9 @@ int32 Movements::getAngleAndSetTargetActorDistance(int32 x1, int32 z1, int32 x2,
 		return 0;
 	}
 
-	destAngle = (difZ << 14) / targetActorDistance;
+	const int32 destAngle = (difZ << 14) / targetActorDistance;
 
-	startAngle = 0;
+	int32 startAngle = 0;
 	//	stopAngle  = 0x100;
 
 	while (_engine->_renderer->shadeAngleTab3[startAngle] > destAngle) {
@@ -127,13 +126,13 @@ int32 Movements::getAngleAndSetTargetActorDistance(int32 x1, int32 z1, int32 x2,
 		}
 	}
 
-	finalAngle = 128 + startAngle;
+	int32 finalAngle = 128 + startAngle;
 
 	if (difX <= 0) {
 		finalAngle = -finalAngle;
 	}
 
-	if (flag & 1) {
+	if (flag == 1) {
 		finalAngle = -finalAngle + 0x100;
 	}
 
@@ -142,7 +141,7 @@ int32 Movements::getAngleAndSetTargetActorDistance(int32 x1, int32 z1, int32 x2,
 
 int32 Movements::getRealAngle(ActorMoveStruct *movePtr) {
 	if (movePtr->numOfStep) {
-		int32 timePassed = _engine->lbaTime - movePtr->timeOfChange;
+		const int32 timePassed = _engine->lbaTime - movePtr->timeOfChange;
 
 		if (timePassed >= movePtr->numOfStep) { // rotation is finished
 			movePtr->numOfStep = 0;
@@ -161,7 +160,7 @@ int32 Movements::getRealAngle(ActorMoveStruct *movePtr) {
 		remainingAngle /= movePtr->numOfStep;
 		remainingAngle += movePtr->from;
 
-		return (remainingAngle);
+		return remainingAngle;
 	}
 
 	return movePtr->to;
diff --git a/engines/twine/redraw.cpp b/engines/twine/redraw.cpp
index ba89437f3c..ed8635b9e9 100644
--- a/engines/twine/redraw.cpp
+++ b/engines/twine/redraw.cpp
@@ -439,12 +439,12 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 			}
 			// Drawing sprite actors
 			else if (flags == 0x1000) {
-				int32 spriteWidth, spriteHeight;
-				uint8 *spritePtr = _engine->_resources->spriteTable[actor2->entity];
+				const uint8 *spritePtr = _engine->_resources->spriteTable[actor2->entity];
 
 				// get actor position on screen
 				_engine->_renderer->projectPositionOnScreen(actor2->x - _engine->_grid->cameraX, actor2->y - _engine->_grid->cameraY, actor2->z - _engine->_grid->cameraZ);
 
+				int32 spriteWidth, spriteHeight;
 				_engine->_grid->getSpriteSize(0, &spriteWidth, &spriteHeight, spritePtr);
 
 				// calculate sprite position on screen
@@ -562,9 +562,9 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 			// process overlay type
 			switch (overlay->type) {
 			case koSprite: {
-				int32 spriteWidth, spriteHeight;
-				uint8 *spritePtr = _engine->_resources->spriteTable[overlay->info0];
+				const uint8 *spritePtr = _engine->_resources->spriteTable[overlay->info0];
 
+				int32 spriteWidth, spriteHeight;
 				_engine->_grid->getSpriteSize(0, &spriteWidth, &spriteHeight, spritePtr);
 
 				const int16 offsetX = *((const int16 *)(_engine->_resources->spriteBoundingBoxPtr + (overlay->info0 * 16)));
@@ -586,8 +586,8 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 				char text[10];
 				snprintf(text, sizeof(text), "%d", overlay->info0);
 
-				int32 textLength = _engine->_text->getTextSize(text);
-				int32 textHeight = 48;
+				const int32 textLength = _engine->_text->getTextSize(text);
+				const int32 textHeight = 48;
 
 				renderLeft = overlay->x - (textLength / 2);
 				renderTop = overlay->y - 24;
@@ -606,14 +606,13 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 				break;
 			}
 			case koNumberRange: {
-				char text[10];
-
-				int32 range = _engine->_collision->getAverageValue(overlay->info1, overlay->info0, 100, overlay->lifeTime - _engine->lbaTime - 50);
+				const int32 range = _engine->_collision->getAverageValue(overlay->info1, overlay->info0, 100, overlay->lifeTime - _engine->lbaTime - 50);
 
+				char text[10];
 				sprintf(text, "%d", range);
 
-				int32 textLength = _engine->_text->getTextSize(text);
-				int32 textHeight = 48;
+				const int32 textLength = _engine->_text->getTextSize(text);
+				const int32 textHeight = 48;
 
 				renderLeft = overlay->x - (textLength / 2);
 				renderTop = overlay->y - 24;
@@ -632,7 +631,7 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 				break;
 			}
 			case koInventoryItem: {
-				int32 item = overlay->info0;
+				const int32 item = overlay->info0;
 
 				_engine->_interface->drawSplittedBox(10, 10, 69, 69, 0);
 				_engine->_interface->setClip(10, 10, 69, 69);
@@ -651,11 +650,10 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 			}
 			case koText: {
 				char text[256];
-
 				_engine->_text->getMenuText(overlay->info0, text, sizeof(text));
 
-				int32 textLength = _engine->_text->getTextSize(text);
-				int32 textHeight = 48;
+				const int32 textLength = _engine->_text->getTextSize(text);
+				const int32 textHeight = 48;
 
 				renderLeft = overlay->x - (textLength / 2);
 				renderTop = overlay->y - 24;
@@ -725,8 +723,6 @@ void Redraw::redrawEngineActions(int32 bgRedraw) { // fullRedraw
 }
 
 void Redraw::drawBubble(int32 actorIdx) {
-	int32 spriteWidth, spriteHeight;
-	uint8 *spritePtr;
 	ActorStruct *actor = _engine->_scene->getActor(actorIdx);
 
 	// get actor position on screen
@@ -737,7 +733,8 @@ void Redraw::drawBubble(int32 actorIdx) {
 		bubbleActor = actorIdx;
 	}
 
-	spritePtr = _engine->_resources->spriteTable[bubbleSpriteIndex];
+	const uint8 *spritePtr = _engine->_resources->spriteTable[bubbleSpriteIndex];
+	int32 spriteWidth, spriteHeight;
 	_engine->_grid->getSpriteSize(0, &spriteWidth, &spriteHeight, spritePtr);
 
 	// calculate sprite position on screen


Commit: d66db9ad128b8d79048fee07c4f7117a7ea65856
    https://github.com/scummvm/scummvm/commit/d66db9ad128b8d79048fee07c4f7117a7ea65856
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: started to implement holomap location loading

Changed paths:
    engines/twine/holomap.cpp
    engines/twine/holomap.h


diff --git a/engines/twine/holomap.cpp b/engines/twine/holomap.cpp
index f15ee1afcf..04412e3dc8 100644
--- a/engines/twine/holomap.cpp
+++ b/engines/twine/holomap.cpp
@@ -21,7 +21,9 @@
  */
 
 #include "twine/holomap.h"
+#include "common/memstream.h"
 #include "twine/gamestate.h"
+#include "twine/hqr.h"
 #include "twine/interface.h"
 #include "twine/renderer.h"
 #include "twine/resources.h"
@@ -35,6 +37,30 @@ namespace TwinE {
 
 Holomap::Holomap(TwinEEngine *engine) : _engine(engine) {}
 
+bool Holomap::loadLocations() {
+	uint8 *locationsPtr;
+	const int32 locationsSize = HQR::getAllocEntry(&locationsPtr, Resources::HQR_RESS_FILE, RESSHQR_HOLOARROWINFO);
+	if (locationsSize == 0) {
+		warning("Could not find holomap locations at index %i in %s", RESSHQR_HOLOARROWINFO, Resources::HQR_RESS_FILE);
+		return false;
+	}
+
+	Common::MemoryReadStream stream(locationsPtr, locationsSize);
+	_numLocations = locationsSize / sizeof(Location);
+	if (_numLocations > NUM_LOCATIONS) {
+		warning("Amount of locations (%i) exceeds the maximum of %i", _numLocations, NUM_LOCATIONS);
+		return false;
+	}
+
+	for (int i = 0; i < _numLocations; i++) {
+		_locations[i].x = stream.readUint16LE();
+		_locations[i].y = stream.readUint16LE();
+		_locations[i].z = stream.readUint16LE();
+		_locations[i].textIndex = stream.readUint16LE();
+	}
+	return true;
+}
+
 void Holomap::setHolomapPosition(int32 locationIdx) {
 	assert(locationIdx >= 0 && locationIdx <= ARRAYSIZE(_engine->_gameState->holomapFlags));
 	_engine->_gameState->holomapFlags[locationIdx] = 0x81;
diff --git a/engines/twine/holomap.h b/engines/twine/holomap.h
index 09c3fd46f5..00d8abe270 100644
--- a/engines/twine/holomap.h
+++ b/engines/twine/holomap.h
@@ -28,12 +28,24 @@
 
 namespace TwinE {
 
+#define NUM_LOCATIONS 150
+
 class TwinEEngine;
 
 class Holomap {
 private:
 	TwinEEngine *_engine;
 
+	struct Location {
+		uint16 x = 0;
+		uint16 y = 0;
+		uint16 z = 0;
+		uint16 textIndex = 0;
+	};
+
+	int32 _numLocations = 0;
+	Location _locations[NUM_LOCATIONS];
+
 	int32 needToLoadHolomapGFX = 0;
 	uint8 paletteHolomap[NUMOFCOLORS * 3]{0};
 
@@ -46,6 +58,8 @@ public:
 	 */
 	void setHolomapPosition(int32 locationIdx);
 
+	bool loadLocations();
+
 	/**
 	 * Clear Holomap location position
 	 * @param locationIdx Scene where position must be cleared


Commit: 688e4a73495833a0c651d76d3a378892dce918a3
    https://github.com/scummvm/scummvm/commit/688e4a73495833a0c651d76d3a378892dce918a3
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: replaced magic number

Changed paths:
    engines/twine/gamestate.cpp
    engines/twine/gamestate.h


diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index 180e63d5ba..e4b155abe2 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -212,13 +212,12 @@ bool GameState::loadGame(Common::SeekableReadStream *file) {
 	_engine->_actor->previousHeroAngle = _engine->_scene->sceneHero->angle;
 	_engine->_scene->sceneHero->body = file->readByte();
 
-	const byte numHolomapFlags = file->readByte(); // number of holomap locations, always 150
-	const int32 expectedHolomapFlagsSize = sizeof(holomapFlags);
-	if (numHolomapFlags != expectedHolomapFlagsSize) {
-		warning("Failed to load holomapflags. Got %u, expected %i", numHolomapFlags, expectedHolomapFlagsSize);
+	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, sizeof(holomapFlags));
+	file->read(holomapFlags, NUM_LOCATIONS);
 
 	inventoryNumGas = file->readByte();
 
@@ -262,8 +261,8 @@ bool GameState::saveGame(Common::WriteStream *file) {
 	file->writeByte(_engine->_scene->sceneHero->body);
 
 	// number of holomap locations
-	file->writeByte(sizeof(holomapFlags));
-	file->write(holomapFlags, sizeof(holomapFlags));
+	file->writeByte(NUM_LOCATIONS);
+	file->write(holomapFlags, NUM_LOCATIONS);
 
 	file->writeByte(inventoryNumGas);
 
diff --git a/engines/twine/gamestate.h b/engines/twine/gamestate.h
index 2a6e5f2b03..97dbb72d83 100644
--- a/engines/twine/gamestate.h
+++ b/engines/twine/gamestate.h
@@ -27,6 +27,7 @@
 #include "common/scummsys.h"
 #include "twine/actor.h"
 #include "twine/menu.h"
+#include "twine/holomap.h"
 
 namespace TwinE {
 
@@ -123,7 +124,7 @@ public:
 	/** Inventory used flags */
 	uint8 inventoryFlags[NUM_INVENTORY_ITEMS];
 
-	uint8 holomapFlags[150]; // GV14
+	uint8 holomapFlags[NUM_LOCATIONS]; // GV14
 
 	char playerName[30];
 


Commit: 0488fe67c4d658d05ed229a0772403f736e25b39
    https://github.com/scummvm/scummvm/commit/0488fe67c4d658d05ed229a0772403f736e25b39
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: reduced cyclic complexity

Changed paths:
    engines/twine/animations.cpp
    engines/twine/collision.cpp
    engines/twine/extra.cpp
    engines/twine/gamestate.cpp


diff --git a/engines/twine/animations.cpp b/engines/twine/animations.cpp
index bb41cd4a3b..00235bfbb9 100644
--- a/engines/twine/animations.cpp
+++ b/engines/twine/animations.cpp
@@ -359,7 +359,7 @@ int32 Animations::getBodyAnimIndex(AnimationTypes animIdx, int32 actorIdx) {
 		int8 type = *(bodyPtr++);
 
 		if (type == -1) {
-			currentActorAnimExtraPtr = NULL;
+			currentActorAnimExtraPtr = nullptr;
 			return -1;
 		}
 
@@ -533,15 +533,15 @@ void Animations::processAnimActions(int32 actorIdx) {
 		case ACTION_THROW_EXTRA_BONUS: {
 			animPos = stream.readByte();
 			const int32 yHeight = stream.readSint16LE();
-			const int32 var_C = stream.readByte();
+			const int32 sprite = stream.readByte();
 			const int32 cx = stream.readSint16LE();
 			const int32 dx = actor->angle + stream.readSint16LE();
 			const int32 var_24 = stream.readSint16LE();
 			const int32 var_14 = stream.readByte();
-			const int32 var = stream.readByte();
+			const int32 strengthOfHit = stream.readByte();
 
 			if (animPos == actor->animPosition) {
-				_engine->_extra->addExtraThrow(actorIdx, actor->x, actor->y + yHeight, actor->z, var_C, cx, dx, var_24, var_14, var);
+				_engine->_extra->addExtraThrow(actorIdx, actor->x, actor->y + yHeight, actor->z, sprite, cx, dx, var_24, var_14, strengthOfHit);
 			}
 			break;
 		}
diff --git a/engines/twine/collision.cpp b/engines/twine/collision.cpp
index ec6fa13391..dc122bd38a 100644
--- a/engines/twine/collision.cpp
+++ b/engines/twine/collision.cpp
@@ -184,16 +184,16 @@ void Collision::reajustActorPosition(int32 brickShape) {
 	if (brickShape >= kStairsTopLeft && brickShape <= kStairsBottomRight) {
 		switch (brickShape) {
 		case kStairsTopLeft:
-			_engine->_movements->processActorY = brkY + getAverageValue(0, 0x100, 0x200, _engine->_movements->processActorX - brkX);
+			_engine->_movements->processActorY = brkY + getAverageValue(0, 256, 512, _engine->_movements->processActorX - brkX);
 			break;
 		case kStairsTopRight:
-			_engine->_movements->processActorY = brkY + getAverageValue(0, 0x100, 0x200, _engine->_movements->processActorZ - brkZ);
+			_engine->_movements->processActorY = brkY + getAverageValue(0, 256, 512, _engine->_movements->processActorZ - brkZ);
 			break;
 		case kStairsBottomLeft:
-			_engine->_movements->processActorY = brkY + getAverageValue(0x100, 0, 0x200, _engine->_movements->processActorZ - brkZ);
+			_engine->_movements->processActorY = brkY + getAverageValue(256, 0, 512, _engine->_movements->processActorZ - brkZ);
 			break;
 		case kStairsBottomRight:
-			_engine->_movements->processActorY = brkY + getAverageValue(0x100, 0, 0x200, _engine->_movements->processActorX - brkX);
+			_engine->_movements->processActorY = brkY + getAverageValue(256, 0, 512, _engine->_movements->processActorX - brkX);
 			break;
 		default:
 			break;
diff --git a/engines/twine/extra.cpp b/engines/twine/extra.cpp
index 3794a08d45..6a4f5bbbea 100644
--- a/engines/twine/extra.cpp
+++ b/engines/twine/extra.cpp
@@ -196,7 +196,7 @@ void Extra::addExtraSpecial(int32 x, int32 y, int32 z, ExtraSpecialType type) {
 			extra->z = z;
 
 			// same as InitFly
-			throwExtra(extra, _engine->getRandomNumber(0x100) + 0x80, _engine->getRandomNumber(0x400), 50, 20);
+			throwExtra(extra, _engine->getRandomNumber(256) + 128, _engine->getRandomNumber(1024), 50, 20);
 
 			extra->strengthOfHit = 0;
 			extra->lifeTime = _engine->lbaTime;
@@ -543,182 +543,203 @@ void Extra::processExtras() {
 
 	for (int32 i = 0; i < EXTRA_MAX_ENTRIES; i++) {
 		ExtraListStruct *extra = &extraList[i];
-		if (extra->info0 != -1) {
-			// process extra life time
-			if (extra->type & 0x1) {
-				if (extra->actorIdx + extra->lifeTime <= _engine->lbaTime) {
-					extra->info0 = -1;
-					continue;
-				}
-			}
-			// reset extra
-			if (extra->type & 0x800) {
+		if (extra->info0 == -1) {
+			continue;
+		}
+		// process extra life time
+		if (extra->type & 0x1) {
+			if (extra->actorIdx + extra->lifeTime <= _engine->lbaTime) {
 				extra->info0 = -1;
 				continue;
 			}
-			//
-			if (extra->type & 0x1000) {
-				extra->info0 = _engine->_collision->getAverageValue(97, 100, 30, _engine->lbaTime - extra->lifeTime);
-				continue;
-			}
-			// process extra moving
-			if (extra->type & 0x2) {
-				currentExtraX = extra->x;
-				currentExtraY = extra->y;
-				currentExtraZ = extra->z;
-
-				currentExtraSpeedX = extra->destX * (_engine->lbaTime - extra->lifeTime);
-				extra->x = currentExtraSpeedX + extra->lastX;
+		}
+		// reset extra
+		if (extra->type & 0x800) {
+			extra->info0 = -1;
+			continue;
+		}
+		//
+		if (extra->type & 0x1000) {
+			extra->info0 = _engine->_collision->getAverageValue(97, 100, 30, _engine->lbaTime - extra->lifeTime);
+			continue;
+		}
+		// process extra moving
+		if (extra->type & 0x2) {
+			currentExtraX = extra->x;
+			currentExtraY = extra->y;
+			currentExtraZ = extra->z;
 
-				currentExtraSpeedY = extra->destY * (_engine->lbaTime - extra->lifeTime);
-				currentExtraSpeedY += extra->lastY;
-				extra->y = currentExtraSpeedY - ABS(((extra->angle * (_engine->lbaTime - extra->lifeTime)) * (_engine->lbaTime - extra->lifeTime)) >> 4);
+			currentExtraSpeedX = extra->destX * (_engine->lbaTime - extra->lifeTime);
+			extra->x = currentExtraSpeedX + extra->lastX;
 
-				extra->z = extra->destZ * (_engine->lbaTime - extra->lifeTime) + extra->lastZ;
+			currentExtraSpeedY = extra->destY * (_engine->lbaTime - extra->lifeTime);
+			currentExtraSpeedY += extra->lastY;
+			extra->y = currentExtraSpeedY - ABS(((extra->angle * (_engine->lbaTime - extra->lifeTime)) * (_engine->lbaTime - extra->lifeTime)) >> 4);
 
-				// check if extra is out of scene
-				if (extra->y < 0 || extra->x < 0 || extra->x > 0x7E00 || extra->z < 0 || extra->z > 0x7E00) {
-					// if extra is Magic Ball
-					if (i == _engine->_gameState->magicBallIdx) {
-						int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
+			extra->z = extra->destZ * (_engine->lbaTime - extra->lifeTime) + extra->lastZ;
 
-						if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
-							spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
-						}
-						if (extra->info0 == SPRITEHQR_MAGICBALL_RED) {
-							spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
-						}
+			// check if extra is out of scene
+			if (extra->y < 0 || extra->x < 0 || extra->x > 0x7E00 || extra->z < 0 || extra->z > 0x7E00) {
+				// if extra is Magic Ball
+				if (i == _engine->_gameState->magicBallIdx) {
+					int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
 
-						_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 10000, 0);
+					if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
+						spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
 					}
-
-					// if can take extra on ground
-					if (extra->type & 0x20) {
-						extra->type &= 0xFFED;
-					} else {
-						extra->info0 = -1;
+					if (extra->info0 == SPRITEHQR_MAGICBALL_RED) {
+						spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
 					}
 
-					continue;
+					_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 10000, 0);
 				}
-			}
-			//
-			if (extra->type & 0x4000) {
-				if (_engine->lbaTime - extra->lifeTime > 40) {
-					extra->type &= 0xBFFF;
+
+				// if can take extra on ground
+				if (extra->type & 0x20) {
+					extra->type &= 0xFFED;
+				} else {
+					extra->info0 = -1;
 				}
+
 				continue;
 			}
-			// process actor target hit
-			if (extra->type & 0x80) {
-				int32 actorIdxAttacked = extra->lifeTime;
-				int32 actorIdx = extra->actorIdx;
-
-				const ActorStruct *actor = _engine->_scene->getActor(actorIdxAttacked);
-				currentExtraX = actor->x;
-				currentExtraY = actor->y + 1000;
-				currentExtraZ = actor->z;
-
-				int32 tmpAngle = _engine->_movements->getAngleAndSetTargetActorDistance(extra->x, extra->z, currentExtraX, currentExtraZ);
-				int32 angle = (tmpAngle - extra->angle) & 0x3FF;
-
-				if (angle > 400 && angle < 600) {
-					if (extra->strengthOfHit) {
-						_engine->_actor->hitActor(actorIdx, actorIdxAttacked, extra->strengthOfHit, -1);
-					}
-
-					if (i == _engine->_gameState->magicBallIdx) {
-						_engine->_gameState->magicBallIdx = -1;
-					}
-
-					extra->info0 = -1;
-					continue;
+		}
+		//
+		if (extra->type & 0x4000) {
+			if (_engine->lbaTime - extra->lifeTime > 40) {
+				extra->type &= 0xBFFF;
+			}
+			continue;
+		}
+		// process actor target hit
+		if (extra->type & 0x80) {
+			int32 actorIdxAttacked = extra->lifeTime;
+			int32 actorIdx = extra->actorIdx;
+
+			const ActorStruct *actor = _engine->_scene->getActor(actorIdxAttacked);
+			currentExtraX = actor->x;
+			currentExtraY = actor->y + 1000;
+			currentExtraZ = actor->z;
+
+			int32 tmpAngle = _engine->_movements->getAngleAndSetTargetActorDistance(extra->x, extra->z, currentExtraX, currentExtraZ);
+			int32 angle = (tmpAngle - extra->angle) & 0x3FF;
+
+			if (angle > 400 && angle < 600) {
+				if (extra->strengthOfHit) {
+					_engine->_actor->hitActor(actorIdx, actorIdxAttacked, extra->strengthOfHit, -1);
 				}
 
-				const int32 angle2 = _engine->_movements->getAngleAndSetTargetActorDistance(extra->y, 0, currentExtraY, _engine->_movements->targetActorDistance);
-				int32 pos = _engine->_movements->getRealAngle(&extra->trackActorMove);
-				if (!pos) {
-					pos = 1;
+				if (i == _engine->_gameState->magicBallIdx) {
+					_engine->_gameState->magicBallIdx = -1;
 				}
 
-				_engine->_movements->rotateActor(pos, 0, angle2);
-				extra->y -= _engine->_renderer->destZ;
+				extra->info0 = -1;
+				continue;
+			}
 
-				_engine->_movements->rotateActor(0, _engine->_renderer->destX, tmpAngle);
-				extra->x += _engine->_renderer->destX;
-				extra->z += _engine->_renderer->destZ;
+			const int32 angle2 = _engine->_movements->getAngleAndSetTargetActorDistance(extra->y, 0, currentExtraY, _engine->_movements->targetActorDistance);
+			int32 pos = _engine->_movements->getRealAngle(&extra->trackActorMove);
+			if (!pos) {
+				pos = 1;
+			}
 
-				_engine->_movements->setActorAngle(0, extra->destZ, 50, &extra->trackActorMove);
+			_engine->_movements->rotateActor(pos, 0, angle2);
+			extra->y -= _engine->_renderer->destZ;
 
-				if (actorIdxAttacked == _engine->_collision->checkExtraCollisionWithActors(extra, actorIdx)) {
-					if (i == _engine->_gameState->magicBallIdx) {
-						_engine->_gameState->magicBallIdx = -1;
-					}
+			_engine->_movements->rotateActor(0, _engine->_renderer->destX, tmpAngle);
+			extra->x += _engine->_renderer->destX;
+			extra->z += _engine->_renderer->destZ;
 
-					extra->info0 = -1;
-					continue;
+			_engine->_movements->setActorAngle(0, extra->destZ, 50, &extra->trackActorMove);
+
+			if (actorIdxAttacked == _engine->_collision->checkExtraCollisionWithActors(extra, actorIdx)) {
+				if (i == _engine->_gameState->magicBallIdx) {
+					_engine->_gameState->magicBallIdx = -1;
 				}
+
+				extra->info0 = -1;
+				continue;
 			}
-			// process magic ball extra aiming for key
-			if (extra->type & 0x200) {
-				//				int32 actorIdxAttacked = extra->lifeTime;
-				ExtraListStruct *extraKey = &extraList[extra->actorIdx];
-				int32 actorIdx = extra->actorIdx;
+		}
+		// process magic ball extra aiming for key
+		if (extra->type & 0x200) {
+			//				int32 actorIdxAttacked = extra->lifeTime;
+			ExtraListStruct *extraKey = &extraList[extra->actorIdx];
+			int32 actorIdx = extra->actorIdx;
 
-				int32 tmpAngle = _engine->_movements->getAngleAndSetTargetActorDistance(extra->x, extra->z, extraKey->x, extraKey->z);
-				int32 angle = (tmpAngle - extra->angle) & 0x3FF;
+			int32 tmpAngle = _engine->_movements->getAngleAndSetTargetActorDistance(extra->x, extra->z, extraKey->x, extraKey->z);
+			int32 angle = (tmpAngle - extra->angle) & 0x3FF;
 
-				if (angle > 400 && angle < 600) {
-					_engine->_sound->playSample(Samples::ItemFound, 4096, 1, _engine->_scene->sceneHero->x, _engine->_scene->sceneHero->y, _engine->_scene->sceneHero->z, 0);
+			if (angle > 400 && angle < 600) {
+				_engine->_sound->playSample(Samples::ItemFound, 4096, 1, _engine->_scene->sceneHero->x, _engine->_scene->sceneHero->y, _engine->_scene->sceneHero->z, 0);
 
-					if (extraKey->info1 > 1) {
-						_engine->_renderer->projectPositionOnScreen(extraKey->x - _engine->_grid->cameraX, extraKey->y - _engine->_grid->cameraY, extraKey->z - _engine->_grid->cameraZ);
-						_engine->_redraw->addOverlay(koNumber, extraKey->info1, _engine->_renderer->projPosX, _engine->_renderer->projPosY, koNormal, 0, 2);
-					}
+				if (extraKey->info1 > 1) {
+					_engine->_renderer->projectPositionOnScreen(extraKey->x - _engine->_grid->cameraX, extraKey->y - _engine->_grid->cameraY, extraKey->z - _engine->_grid->cameraZ);
+					_engine->_redraw->addOverlay(koNumber, extraKey->info1, _engine->_renderer->projPosX, _engine->_renderer->projPosY, koNormal, 0, 2);
+				}
 
-					_engine->_redraw->addOverlay(koSprite, SPRITEHQR_KEY, 10, 30, koNormal, 0, 2);
+				_engine->_redraw->addOverlay(koSprite, SPRITEHQR_KEY, 10, 30, koNormal, 0, 2);
 
-					_engine->_gameState->inventoryNumKeys += extraKey->info1;
-					extraKey->info0 = -1;
+				_engine->_gameState->inventoryNumKeys += extraKey->info1;
+				extraKey->info0 = -1;
 
-					extra->info0 = -1;
-					_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, SPRITEHQR_KEY, 0, 8000, 0);
-					continue;
-				}
-				int32 angle2 = _engine->_movements->getAngleAndSetTargetActorDistance(extra->y, 0, extraKey->y, _engine->_movements->targetActorDistance);
-				int32 pos = _engine->_movements->getRealAngle(&extra->trackActorMove);
+				extra->info0 = -1;
+				_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, SPRITEHQR_KEY, 0, 8000, 0);
+				continue;
+			}
+			int32 angle2 = _engine->_movements->getAngleAndSetTargetActorDistance(extra->y, 0, extraKey->y, _engine->_movements->targetActorDistance);
+			int32 pos = _engine->_movements->getRealAngle(&extra->trackActorMove);
 
-				if (!pos) {
-					pos = 1;
-				}
+			if (!pos) {
+				pos = 1;
+			}
 
-				_engine->_movements->rotateActor(pos, 0, angle2);
-				extra->y -= _engine->_renderer->destZ;
+			_engine->_movements->rotateActor(pos, 0, angle2);
+			extra->y -= _engine->_renderer->destZ;
 
-				_engine->_movements->rotateActor(0, _engine->_renderer->destX, tmpAngle);
-				extra->x += _engine->_renderer->destX;
-				extra->z += _engine->_renderer->destZ;
+			_engine->_movements->rotateActor(0, _engine->_renderer->destX, tmpAngle);
+			extra->x += _engine->_renderer->destX;
+			extra->z += _engine->_renderer->destZ;
 
-				_engine->_movements->setActorAngle(0, extra->destZ, 50, &extra->trackActorMove);
+			_engine->_movements->setActorAngle(0, extra->destZ, 50, &extra->trackActorMove);
 
-				if (actorIdx == _engine->_collision->checkExtraCollisionWithExtra(extra, _engine->_gameState->magicBallIdx)) {
-					_engine->_sound->playSample(Samples::ItemFound, 4096, 1, _engine->_scene->sceneHero->x, _engine->_scene->sceneHero->y, _engine->_scene->sceneHero->z, 0);
+			if (actorIdx == _engine->_collision->checkExtraCollisionWithExtra(extra, _engine->_gameState->magicBallIdx)) {
+				_engine->_sound->playSample(Samples::ItemFound, 4096, 1, _engine->_scene->sceneHero->x, _engine->_scene->sceneHero->y, _engine->_scene->sceneHero->z, 0);
 
-					if (extraKey->info1 > 1) {
-						_engine->_renderer->projectPositionOnScreen(extraKey->x - _engine->_grid->cameraX, extraKey->y - _engine->_grid->cameraY, extraKey->z - _engine->_grid->cameraZ);
-						_engine->_redraw->addOverlay(koNumber, extraKey->info1, _engine->_renderer->projPosX, _engine->_renderer->projPosY, koNormal, 0, 2);
-					}
+				if (extraKey->info1 > 1) {
+					_engine->_renderer->projectPositionOnScreen(extraKey->x - _engine->_grid->cameraX, extraKey->y - _engine->_grid->cameraY, extraKey->z - _engine->_grid->cameraZ);
+					_engine->_redraw->addOverlay(koNumber, extraKey->info1, _engine->_renderer->projPosX, _engine->_renderer->projPosY, koNormal, 0, 2);
+				}
 
-					_engine->_redraw->addOverlay(koSprite, SPRITEHQR_KEY, 10, 30, koNormal, 0, 2);
+				_engine->_redraw->addOverlay(koSprite, SPRITEHQR_KEY, 10, 30, koNormal, 0, 2);
 
-					_engine->_gameState->inventoryNumKeys += extraKey->info1;
-					extraKey->info0 = -1;
+				_engine->_gameState->inventoryNumKeys += extraKey->info1;
+				extraKey->info0 = -1;
 
-					extra->info0 = -1;
-					_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, SPRITEHQR_KEY, 0, 8000, 0);
-					continue;
+				extra->info0 = -1;
+				_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, SPRITEHQR_KEY, 0, 8000, 0);
+				continue;
+			}
+			if (extraKey->info0 == -1) {
+				int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
+
+				if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
+					spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
+				}
+				if (extra->info0 == SPRITEHQR_MAGICBALL_RED) {
+					spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
 				}
-				if (extraKey->info0 == -1) {
+
+				extra->info0 = -1;
+				_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 8000, 0);
+				continue;
+			}
+		}
+		// process extra collision with actors
+		if (extra->type & 0x4) {
+			if (_engine->_collision->checkExtraCollisionWithActors(extra, extra->actorIdx) != -1) {
+				// if extra is Magic Ball
+				if (i == _engine->_gameState->magicBallIdx) {
 					int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
 
 					if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
@@ -728,16 +749,40 @@ void Extra::processExtras() {
 						spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
 					}
 
-					extra->info0 = -1;
-					_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 8000, 0);
-					continue;
+					_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 10000, 0);
+				}
+
+				extra->info0 = -1;
+				continue;
+			}
+		}
+		// process extra collision with scene ground
+		if (extra->type & 0x8) {
+			int32 process = 0;
+
+			if (_engine->_collision->checkExtraCollisionWithBricks(currentExtraX, currentExtraY, currentExtraZ, extra->x, extra->y, extra->z)) {
+				// if not touch the ground
+				if (!(extra->type & 0x2000)) {
+					process = 1;
+				}
+			} else {
+				// if touch the ground
+				if (extra->type & 0x2000) {
+					extra->type &= 0xDFFF; // set flag out of ground
 				}
 			}
-			// process extra collision with actors
-			if (extra->type & 0x4) {
-				if (_engine->_collision->checkExtraCollisionWithActors(extra, extra->actorIdx) != -1) {
-					// if extra is Magic Ball
-					if (i == _engine->_gameState->magicBallIdx) {
+
+			if (process) {
+				// show explode cloud
+				if (extra->type & 0x100) {
+					addExtraSpecial(currentExtraX, currentExtraY, currentExtraZ, kExplodeCloud);
+				}
+				// if extra is magic ball
+				if (i == _engine->_gameState->magicBallIdx) {
+					_engine->_sound->playSample(Samples::Hit, _engine->getRandomNumber(300) + 3946, 1, extra->x, extra->y, extra->z);
+
+					// cant bounce with not magic points
+					if (_engine->_gameState->magicBallNumBounce <= 0) {
 						int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
 
 						if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
@@ -748,39 +793,14 @@ void Extra::processExtras() {
 						}
 
 						_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 10000, 0);
-					}
-
-					extra->info0 = -1;
-					continue;
-				}
-			}
-			// process extra collision with scene ground
-			if (extra->type & 0x8) {
-				int32 process = 0;
-
-				if (_engine->_collision->checkExtraCollisionWithBricks(currentExtraX, currentExtraY, currentExtraZ, extra->x, extra->y, extra->z)) {
-					// if not touch the ground
-					if (!(extra->type & 0x2000)) {
-						process = 1;
-					}
-				} else {
-					// if touch the ground
-					if (extra->type & 0x2000) {
-						extra->type &= 0xDFFF; // set flag out of ground
-					}
-				}
 
-				if (process) {
-					// show explode cloud
-					if (extra->type & 0x100) {
-						addExtraSpecial(currentExtraX, currentExtraY, currentExtraZ, kExplodeCloud);
+						extra->info0 = -1;
+						continue;
 					}
-					// if extra is magic ball
-					if (i == _engine->_gameState->magicBallIdx) {
-						_engine->_sound->playSample(Samples::Hit, _engine->getRandomNumber(300) + 3946, 1, extra->x, extra->y, extra->z);
 
-						// cant bounce with not magic points
-						if (_engine->_gameState->magicBallNumBounce <= 0) {
+					// if has magic points
+					if (_engine->_gameState->magicBallNumBounce == 1) {
+						if (!_engine->_gameState->magicBallAuxBounce--) {
 							int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
 
 							if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
@@ -795,94 +815,75 @@ void Extra::processExtras() {
 							extra->info0 = -1;
 							continue;
 						}
-
-						// if has magic points
-						if (_engine->_gameState->magicBallNumBounce == 1) {
-							if (!_engine->_gameState->magicBallAuxBounce--) {
-								int32 spriteIdx = SPRITEHQR_MAGICBALL_YELLOW_TRANS;
-
-								if (extra->info0 == SPRITEHQR_MAGICBALL_GREEN) {
-									spriteIdx = SPRITEHQR_MAGICBALL_GREEN_TRANS;
-								}
-								if (extra->info0 == SPRITEHQR_MAGICBALL_RED) {
-									spriteIdx = SPRITEHQR_MAGICBALL_RED_TRANS;
-								}
-
-								_engine->_gameState->magicBallIdx = addExtra(-1, extra->x, extra->y, extra->z, spriteIdx, 0, 10000, 0);
-
-								extra->info0 = -1;
-								continue;
-							}
-							processMagicballBounce(extra, currentExtraX, currentExtraY, currentExtraZ);
-						}
-					} else {
-						extra->info0 = -1;
-						continue;
+						processMagicballBounce(extra, currentExtraX, currentExtraY, currentExtraZ);
 					}
+				} else {
+					extra->info0 = -1;
+					continue;
 				}
 			}
-			// extra stop moving while collision with bricks
-			if (extra->type & 0x10) {
-				int32 process = 0;
-
-				if (_engine->_collision->checkExtraCollisionWithBricks(currentExtraX, currentExtraY, currentExtraZ, extra->x, extra->y, extra->z)) {
-					// if not touch the ground
-					if (!(extra->type & 0x2000)) {
-						process = 1;
-					}
-				} else {
-					// if touch the ground
-					if (extra->type & 0x2000) {
-						extra->type &= 0xDFFF; // set flag out of ground
-					}
+		}
+		// extra stop moving while collision with bricks
+		if (extra->type & 0x10) {
+			int32 process = 0;
+
+			if (_engine->_collision->checkExtraCollisionWithBricks(currentExtraX, currentExtraY, currentExtraZ, extra->x, extra->y, extra->z)) {
+				// if not touch the ground
+				if (!(extra->type & 0x2000)) {
+					process = 1;
 				}
-
-				if (process) {
-					const int16 *spriteBounds = (const int16 *)(_engine->_resources->spriteBoundingBoxPtr + extra->info0 * 16 + 8);
-					extra->y = (_engine->_collision->collisionY << 8) + 0x100 - *(spriteBounds);
-					extra->type &= 0xFFED;
-					continue;
+			} else {
+				// if touch the ground
+				if (extra->type & 0x2000) {
+					extra->type &= 0xDFFF; // set flag out of ground
 				}
 			}
-			// get extras on ground
-			if ((extra->type & 0x20) && !(extra->type & 0x2)) {
-				// if hero touch extra
-				if (_engine->_collision->checkExtraCollisionWithActors(extra, -1) == 0) {
-					_engine->_sound->playSample(Samples::ItemFound, 4096, 1, extra->x, extra->y, extra->z);
-
-					if (extra->info1 > 1 && !_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
-						_engine->_renderer->projectPositionOnScreen(extra->x - _engine->_grid->cameraX, extra->y - _engine->_grid->cameraY, extra->z - _engine->_grid->cameraZ);
-						_engine->_redraw->addOverlay(koNumber, extra->info1, _engine->_renderer->projPosX, _engine->_renderer->projPosY, 158, koNormal, 2);
-					}
 
-					_engine->_redraw->addOverlay(koSprite, extra->info0, 10, 30, 0, koNormal, 2);
+			if (process) {
+				const int16 *spriteBounds = (const int16 *)(_engine->_resources->spriteBoundingBoxPtr + extra->info0 * 16 + 8);
+				extra->y = (_engine->_collision->collisionY << 8) + 0x100 - *(spriteBounds);
+				extra->type &= 0xFFED;
+				continue;
+			}
+		}
+		// get extras on ground
+		if ((extra->type & 0x20) && !(extra->type & 0x2)) {
+			// if hero touch extra
+			if (_engine->_collision->checkExtraCollisionWithActors(extra, -1) == 0) {
+				_engine->_sound->playSample(Samples::ItemFound, 4096, 1, extra->x, extra->y, extra->z);
+
+				if (extra->info1 > 1 && !_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
+					_engine->_renderer->projectPositionOnScreen(extra->x - _engine->_grid->cameraX, extra->y - _engine->_grid->cameraY, extra->z - _engine->_grid->cameraZ);
+					_engine->_redraw->addOverlay(koNumber, extra->info1, _engine->_renderer->projPosX, _engine->_renderer->projPosY, 158, koNormal, 2);
+				}
 
-					if (extra->info0 == SPRITEHQR_KASHES) {
-						_engine->_gameState->inventoryNumKashes += extra->info1;
-						if (_engine->_gameState->inventoryNumKashes > 999) {
-							_engine->_gameState->inventoryNumKashes = 999;
-						}
-					} else if (extra->info0 == SPRITEHQR_LIFEPOINTS) {
-						_engine->_scene->sceneHero->life += extra->info1;
-						if (_engine->_scene->sceneHero->life > 50) {
-							_engine->_scene->sceneHero->life = 50;
-						}
-					} else if (extra->info0 == SPRITEHQR_MAGICPOINTS && _engine->_gameState->magicLevelIdx) {
-						_engine->_gameState->inventoryMagicPoints += extra->info1 * 2;
-						if (_engine->_gameState->inventoryMagicPoints > _engine->_gameState->magicLevelIdx * 20) {
-							_engine->_gameState->inventoryMagicPoints = _engine->_gameState->magicLevelIdx * 20;
-						}
-					} else if (extra->info0 == SPRITEHQR_KEY) {
-						_engine->_gameState->inventoryNumKeys += extra->info1;
-					} else if (extra->info0 == SPRITEHQR_CLOVERLEAF) {
-						_engine->_gameState->inventoryNumLeafs += extra->info1;
-						if (_engine->_gameState->inventoryNumLeafs > _engine->_gameState->inventoryNumLeafsBox) {
-							_engine->_gameState->inventoryNumLeafs = _engine->_gameState->inventoryNumLeafsBox;
-						}
-					}
+				_engine->_redraw->addOverlay(koSprite, extra->info0, 10, 30, 0, koNormal, 2);
 
-					extra->info0 = -1;
+				if (extra->info0 == SPRITEHQR_KASHES) {
+					_engine->_gameState->inventoryNumKashes += extra->info1;
+					if (_engine->_gameState->inventoryNumKashes > 999) {
+						_engine->_gameState->inventoryNumKashes = 999;
+					}
+				} else if (extra->info0 == SPRITEHQR_LIFEPOINTS) {
+					_engine->_scene->sceneHero->life += extra->info1;
+					if (_engine->_scene->sceneHero->life > 50) {
+						_engine->_scene->sceneHero->life = 50;
+					}
+				} else if (extra->info0 == SPRITEHQR_MAGICPOINTS && _engine->_gameState->magicLevelIdx) {
+					_engine->_gameState->inventoryMagicPoints += extra->info1 * 2;
+					if (_engine->_gameState->inventoryMagicPoints > _engine->_gameState->magicLevelIdx * 20) {
+						_engine->_gameState->inventoryMagicPoints = _engine->_gameState->magicLevelIdx * 20;
+					}
+				} else if (extra->info0 == SPRITEHQR_KEY) {
+					_engine->_gameState->inventoryNumKeys += extra->info1;
+				} else if (extra->info0 == SPRITEHQR_CLOVERLEAF) {
+					_engine->_gameState->inventoryNumLeafs += extra->info1;
+					if (_engine->_gameState->inventoryNumLeafs > _engine->_gameState->inventoryNumLeafsBox) {
+						_engine->_gameState->inventoryNumLeafs = _engine->_gameState->inventoryNumLeafsBox;
+					}
 				}
+
+				extra->info0 = -1;
 			}
 		}
 	}
diff --git a/engines/twine/gamestate.cpp b/engines/twine/gamestate.cpp
index e4b155abe2..041b67658a 100644
--- a/engines/twine/gamestate.cpp
+++ b/engines/twine/gamestate.cpp
@@ -474,7 +474,7 @@ void GameState::processGameoverAnimation() {
 		}
 
 		const int32 avg = _engine->_collision->getAverageValue(40000, 3200, 500, _engine->lbaTime - startLbaTime);
-		const int32 cdot = _engine->_screens->crossDot(1, 1024, 100, (_engine->lbaTime - startLbaTime) % 0x64);
+		const int32 cdot = _engine->_screens->crossDot(1, 1024, 100, (_engine->lbaTime - startLbaTime) % 100);
 
 		_engine->_interface->blitBox(left, top, right, bottom, (int8 *)_engine->workVideoBuffer.getPixels(), 120, 120, (int8 *)_engine->frontVideoBuffer.getPixels());
 		_engine->_renderer->setCameraAngle(0, 0, 0, 0, -cdot, 0, avg);


Commit: 3f6828a7a42409cb37fbbbd1633bc2d319181e63
    https://github.com/scummvm/scummvm/commit/3f6828a7a42409cb37fbbbd1633bc2d319181e63
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: allow to activate debug options via console

Changed paths:
    engines/twine/console.cpp
    engines/twine/console.h


diff --git a/engines/twine/console.cpp b/engines/twine/console.cpp
index dd07154124..5275032e30 100644
--- a/engines/twine/console.cpp
+++ b/engines/twine/console.cpp
@@ -24,6 +24,8 @@
 #include "twine/twine.h"
 #include "twine/gamestate.h"
 #include "twine/scene.h"
+#include "twine/debug_scene.h"
+#include "twine/debug_grid.h"
 #include "twine/text.h"
 
 namespace TwinE {
@@ -34,11 +36,29 @@ TwinEConsole::TwinEConsole(TwinEEngine *engine) : _engine(engine), GUI::Debugger
 	registerCmd("change_scene", WRAP_METHOD(TwinEConsole, doChangeScene));
 	registerCmd("list_menutext", WRAP_METHOD(TwinEConsole, doListMenuText));
 	registerCmd("toggle_debug", WRAP_METHOD(TwinEConsole, doToggleDebug));
+	registerCmd("toggle_zones", WRAP_METHOD(TwinEConsole, doToggleZoneRendering));
+	registerCmd("toggle_freecamera", WRAP_METHOD(TwinEConsole, doToggleFreeCamera));
+	registerCmd("toggle_scenechanges", WRAP_METHOD(TwinEConsole, doToggleSceneChanges));
 }
 
 TwinEConsole::~TwinEConsole() {
 }
 
+bool TwinEConsole::doToggleZoneRendering(int argc, const char **argv) {
+	_engine->_debugScene->showingZones = !_engine->_debugScene->showingZones;
+	return true;
+}
+
+bool TwinEConsole::doToggleFreeCamera(int argc, const char **argv) {
+	_engine->_debugGrid->useFreeCamera = !_engine->_debugGrid->useFreeCamera;
+	return true;
+}
+
+bool TwinEConsole::doToggleSceneChanges(int argc, const char **argv) {
+	_engine->_debugGrid->canChangeScenes = !_engine->_debugGrid->canChangeScenes;
+	return true;
+}
+
 bool TwinEConsole::doGiveKey(int argc, const char **argv) {
 	int amount = 1;
 	if (argc >= 1) {
diff --git a/engines/twine/console.h b/engines/twine/console.h
index be14df760f..454c1a1dc1 100644
--- a/engines/twine/console.h
+++ b/engines/twine/console.h
@@ -39,6 +39,9 @@ private:
 	bool doListMenuText(int argc, const char **argv);
 	bool doToggleDebug(int argc, const char **argv);
 	bool doGiveKey(int argc, const char **argv);
+	bool doToggleZoneRendering(int argc, const char **argv);
+	bool doToggleFreeCamera(int argc, const char **argv);
+	bool doToggleSceneChanges(int argc, const char **argv);
 public:
 	TwinEConsole(TwinEEngine *engine);
 	~TwinEConsole() override;


Commit: cf8e7339d01d886556b67b3102fb9018e74f2e80
    https://github.com/scummvm/scummvm/commit/cf8e7339d01d886556b67b3102fb9018e74f2e80
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: implemented drawText for debug window

Changed paths:
    engines/twine/twine.cpp


diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index b99d32cba1..7aab9bf1fd 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -35,6 +35,9 @@
 #include "common/translation.h"
 #include "engines/metaengine.h"
 #include "engines/util.h"
+#include "graphics/colormasks.h"
+#include "graphics/fontman.h"
+#include "graphics/font.h"
 #include "graphics/managed_surface.h"
 #include "graphics/palette.h"
 #include "graphics/pixelformat.h"
@@ -975,24 +978,16 @@ void TwinEEngine::readKeys() {
 }
 
 void TwinEEngine::drawText(int32 x, int32 y, const char *string, int32 center) {
-#if 0 // TODO
-	SDL_Color white = {0xFF, 0xFF, 0xFF, 0};
-	SDL_Color *forecol = &white;
-	SDL_Rect rectangle;
-	Graphics::ManagedSurface *text = TTF_RenderText_Solid(font, string, *forecol);
-
-	if (center) {
-		rectangle.x = x - (text->w / 2);
-	} else {
-		rectangle.x = x;
+	const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
+	if (!font) {
+		return;
 	}
-	rectangle.y = y - 2;
-	rectangle.w = text->w;
-	rectangle.h = text->h;
-
-	SDL_BlitSurface(text, NULL, screenBuffer, &rectangle);
-	SDL_FreeSurface(text);
-#endif
+	int width = 100;
+	const Common::String text(string);
+	font->drawString(&frontVideoBuffer, text,
+	                 x, y, width,
+	                 frontVideoBuffer.format.RGBToColor(255, 255, 255),
+	                 center ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft);
 }
 
 } // namespace TwinE


Commit: 92b68a1132eb1272a70c62a266cc1e945eed3ff9
    https://github.com/scummvm/scummvm/commit/92b68a1132eb1272a70c62a266cc1e945eed3ff9
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2020-11-03T18:29:37+01:00

Commit Message:
TWINE: cleanup in debug window

Changed paths:
    engines/twine/debug.cpp
    engines/twine/debug_scene.cpp
    engines/twine/movements.cpp


diff --git a/engines/twine/debug.cpp b/engines/twine/debug.cpp
index 3e6ce4f914..1cf70ec667 100644
--- a/engines/twine/debug.cpp
+++ b/engines/twine/debug.cpp
@@ -392,13 +392,10 @@ void Debug::debugLeftMenu() {
 	debugAddButton(ZONES_MENU, 205, 244, 350, 264, "Ladder Zones", 215, 249, 7, 87, 119, 0, SHOW_ZONE_LADDER);
 }
 
-int32 Debug::debugProcessButton(int32 X, int32 Y) {
-	int32 i;
-	int32 j;
-
-	for (i = 0; i < numDebugWindows; i++) {
-		for (j = 0; j < debugWindows[i].numButtons; j++) {
-			if (X > (debugWindows[i].debugButtons[j].left) && X < (debugWindows[i].debugButtons[j].right) && Y > (debugWindows[i].debugButtons[j].top) && Y < (debugWindows[i].debugButtons[j].bottom)) {
+int32 Debug::debugProcessButton(int32 x, int32 y) {
+	for (int32 i = 0; i < numDebugWindows; i++) {
+		for (int32 j = 0; j < debugWindows[i].numButtons; j++) {
+			if (x > (debugWindows[i].debugButtons[j].left) && x < (debugWindows[i].debugButtons[j].right) && y > (debugWindows[i].debugButtons[j].top) && y < (debugWindows[i].debugButtons[j].bottom)) {
 				return (debugWindows[i].debugButtons[j].type);
 			}
 		}
@@ -433,8 +430,9 @@ void Debug::debugProcessWindow() {
 		_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
 
 		debugResetButtonsState();
-		if (numDebugWindows == 0)
+		if (numDebugWindows == 0) {
 			debugLeftMenu();
+		}
 		debugDrawWindows();
 
 		do {
diff --git a/engines/twine/debug_scene.cpp b/engines/twine/debug_scene.cpp
index 3a6aa51576..fa5d3ecbb6 100644
--- a/engines/twine/debug_scene.cpp
+++ b/engines/twine/debug_scene.cpp
@@ -39,17 +39,21 @@ void DebugScene::drawBoundingBoxProjectPoints(ScenePoint *pPoint3d, ScenePoint *
 	pPoint3dProjected->y = _engine->_renderer->projPosY;
 	pPoint3dProjected->z = _engine->_renderer->projPosZ;
 
-	if (_engine->_redraw->renderLeft > _engine->_renderer->projPosX)
+	if (_engine->_redraw->renderLeft > _engine->_renderer->projPosX) {
 		_engine->_redraw->renderLeft = _engine->_renderer->projPosX;
+	}
 
-	if (_engine->_redraw->renderRight < _engine->_renderer->projPosX)
+	if (_engine->_redraw->renderRight < _engine->_renderer->projPosX) {
 		_engine->_redraw->renderRight = _engine->_renderer->projPosX;
+	}
 
-	if (_engine->_redraw->renderTop > _engine->_renderer->projPosY)
+	if (_engine->_redraw->renderTop > _engine->_renderer->projPosY) {
 		_engine->_redraw->renderTop = _engine->_renderer->projPosY;
+	}
 
-	if (_engine->_redraw->renderBottom < _engine->_renderer->projPosY)
+	if (_engine->_redraw->renderBottom < _engine->_renderer->projPosY) {
 		_engine->_redraw->renderBottom = _engine->_renderer->projPosY;
+	}
 }
 
 int32 DebugScene::checkZoneType(int32 type) {
@@ -123,8 +127,6 @@ void DebugScene::displayZones() {
 		ScenePoint backTopLeftPoint2D;
 		ScenePoint backTopRightPoint2D;
 
-		uint8 color;
-
 		// compute the points in 3D
 
 		frontBottomLeftPoint.x = zonePtr->bottomLeft.x - _engine->_grid->cameraX;
@@ -172,7 +174,7 @@ void DebugScene::displayZones() {
 
 		// draw all lines
 
-		color = 15 * 3 + zonePtr->type * 16;
+		uint8 color = 15 * 3 + zonePtr->type * 16;
 
 		// draw front part
 		_engine->_interface->drawLine(frontBottomLeftPoint2D.x, frontBottomLeftPoint2D.y, frontTopLeftPoint2D.x, frontTopLeftPoint2D.y, color);
@@ -198,6 +200,7 @@ void DebugScene::displayZones() {
 		_engine->_interface->drawLine(backBottomRightPoint2D.x, backBottomRightPoint2D.y, frontBottomRightPoint2D.x, frontBottomRightPoint2D.y, color);
 		_engine->_interface->drawLine(frontBottomRightPoint2D.x, frontBottomRightPoint2D.y, frontBottomLeftPoint2D.x, frontBottomLeftPoint2D.y, color);
 	}
+	_engine->flip();
 }
 
 } // namespace TwinE
diff --git a/engines/twine/movements.cpp b/engines/twine/movements.cpp
index 264d843322..474f06b5c4 100644
--- a/engines/twine/movements.cpp
+++ b/engines/twine/movements.cpp
@@ -362,12 +362,12 @@ void Movements::processActorMovements(int32 actorIdx) {
 
 				heroMoved = false;
 
-				if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) { // walk forward
+				if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) {
 					if (!_engine->_scene->currentActorInZone) {
 						_engine->_animations->initAnim(kForward, 0, 255, actorIdx);
 					}
 					heroMoved = true;
-				} else if (_engine->_input->isActionActive(TwinEActionType::MoveBackward)) { // walk backward
+				} else if (_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
 					_engine->_animations->initAnim(kBackward, 0, 255, actorIdx);
 					heroMoved = true;
 				}




More information about the Scummvm-git-logs mailing list