Raw File
menu.cpp
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */


#include "common/events.h"
#include "common/system.h"
#include "common/savefile.h"

#include "touche/graphics.h"
#include "touche/midi.h"
#include "touche/touche.h"

namespace Touche {

static void drawArrow(uint8 *dst, int dstPitch, int x, int y, int delta, uint8 color) {
	static const int8 arrowCoordsTable[7][4] = {
		{  5,  0,  9,  0 },
		{  5,  0,  5,  4 },
		{ -5,  4,  5,  4 },
		{ -5,  0, -5,  4 },
		{ -9,  0, -5,  0 },
		{ -9,  0,  0, -9 },
		{  0, -9,  9,  0 }
	};
	for (uint i = 0; i < 7; ++i) {
		const int x1 = x + arrowCoordsTable[i][0];
		const int y1 = y + arrowCoordsTable[i][1] * delta;
		const int x2 = x + arrowCoordsTable[i][2];
		const int y2 = y + arrowCoordsTable[i][3] * delta;
		Graphics::drawLine(dst, dstPitch, x1, y1, x2, y2, color);
	}
}

void ToucheEngine::drawButton(Button *button) {
	if (button->flags & kButtonBorder) {
		Graphics::drawRect(_offscreenBuffer, kScreenWidth, button->x, button->y, button->w, button->h, 0xF7, 0xF9);
	}
	if (button->flags & kButtonText) {
		if (button->data != 0) {
			const char *str = getString(button->data);
			const int w = getStringWidth(button->data);
			const int h = kTextHeight;
			const int x = button->x + (button->w - w) / 2;
			const int y = button->y + (button->h - h) / 2;
			Graphics::drawString16(_offscreenBuffer, kScreenWidth, 0xFF, x, y, str);
		}
	}
	if (button->flags & kButtonArrow) {
		int dx = 0;
		int dy = 0;
		switch (button->data) {
		case 2000: // up arrow
			dx = 1;
			dy = 2;
			break;
		case 2001: // down arrow
			dx = -1;
			dy = -2;
			break;
		}
		const int x = button->x + button->w / 2;
		const int y = button->y + button->h / 2;
		drawArrow(_offscreenBuffer, kScreenWidth, x, y + dy + 1, dx, 0xD2);
		drawArrow(_offscreenBuffer, kScreenWidth, x, y + dy,     dx, 0xFF);
	}
}

static void drawVolumeSlideBar(uint8 *dst, int dstPitch, int volume) {
	const int w = volume * 232 / 255;
	if (w > 0) {
		Graphics::fillRect(dst, dstPitch, 157, 259, w, 6, 0xF0);
	}
	if (w < 232) {
		Graphics::fillRect(dst, dstPitch, 157 + w, 259, 232 - w, 6, 0xD2);
	}
}

static void drawSaveGameStateDescriptions(uint8 *dst, int dstPitch, MenuData *menuData, int currentPage, int currentSlot) {
	for (int i = 0, slot = currentPage * 10; i < 10; ++i, ++slot) {
		const Button *b = &menuData->buttonsTable[i];
		const uint8 color = (slot == currentSlot) ? 0xCB : 0xD9;
		char buf[64];
		sprintf(buf, "%d.", slot);
		Graphics::drawString16(dst, dstPitch, color, b->x, b->y, buf);
		strcpy(buf, menuData->saveLoadDescriptionsTable[slot]);
		if (slot == currentSlot && menuData->mode == kMenuSaveStateMode) {
			strcat(buf, "_");
		}
		Graphics::drawString16(dst, dstPitch, color, b->x + 30, b->y, buf);
	}
}

static void setupMenu(MenuMode mode, MenuData *menuData) {
	static Button settingsButtonsTable[] = {
		{ 452, 120,  94, 24, kActionLoadMenu,     -52, kButtonBorder | kButtonText },
		{ 452, 152,  94, 24, kActionSaveMenu,     -53, kButtonBorder | kButtonText },
		{ 452, 184,  94, 24, kActionRestartGame,  -90, kButtonBorder | kButtonText },
		{ 452, 216,  94, 24, kActionPlayGame,     -54, kButtonBorder | kButtonText },
		{ 452, 248,  94, 24, kActionQuitGame,     -55, kButtonBorder | kButtonText },
		{ 396, 130,  24, 24, kActionTextOnly,       0, kButtonBorder | kButtonText },
		{ 396, 160,  24, 24, kActionVoiceOnly,      0, kButtonBorder | kButtonText },
		{ 396, 190,  24, 24, kActionTextAndVoice,   0, kButtonBorder | kButtonText },
		{ 126, 130, 254, 24, kActionNone,         -92, kButtonBorder | kButtonText },
		{ 126, 160, 254, 24, kActionNone,         -93, kButtonBorder | kButtonText },
		{ 126, 190, 254, 24, kActionNone,         -94, kButtonBorder | kButtonText },
		{ 126, 222, 294, 20, kActionNone,         -91, kButtonBorder | kButtonText },
		{ 126, 250,  24, 24, kActionLowerVolume,  -87, kButtonBorder | kButtonText },
		{ 396, 250,  24, 24, kActionUpperVolume,  -88, kButtonBorder | kButtonText },
		{ 154, 256, 238, 12, kActionNone,           0, kButtonBorder }
	};
	static Button saveLoadButtonsTable[] = {
		{ 108, 120, 336, 15, kActionGameState1,         0, 0 },
		{ 108, 136, 336, 15, kActionGameState2,         0, 0 },
		{ 108, 152, 336, 15, kActionGameState3,         0, 0 },
		{ 108, 168, 336, 15, kActionGameState4,         0, 0 },
		{ 108, 184, 336, 15, kActionGameState5,         0, 0 },
		{ 108, 200, 336, 15, kActionGameState6,         0, 0 },
		{ 108, 216, 336, 15, kActionGameState7,         0, 0 },
		{ 108, 232, 336, 15, kActionGameState8,         0, 0 },
		{ 108, 248, 336, 15, kActionGameState9,         0, 0 },
		{ 108, 264, 336, 15, kActionGameState10,        0, 0 },
		{ 452, 120,  94, 24, kActionScrollUpSaves,   2000, kButtonBorder | kButtonArrow },
		{ 452, 152,  94, 24, kActionCancelSaveLoad,   -56, kButtonBorder | kButtonText  },
		{ 452, 216,  94, 24, kActionPerformSaveLoad,    0, kButtonBorder | kButtonText  },
		{ 452, 248,  94, 24, kActionScrollDownSaves, 2001, kButtonBorder | kButtonArrow }
	};

	switch (mode) {
	case kMenuSettingsMode:
		menuData->buttonsTable = settingsButtonsTable;
		menuData->buttonsCount = ARRAYSIZE(settingsButtonsTable);
		break;
	case kMenuLoadStateMode:
		saveLoadButtonsTable[12].data = -52;
		menuData->buttonsTable = saveLoadButtonsTable;
		menuData->buttonsCount = ARRAYSIZE(saveLoadButtonsTable);
		break;
	case kMenuSaveStateMode:
		saveLoadButtonsTable[12].data = -53;
		menuData->buttonsTable = saveLoadButtonsTable;
		menuData->buttonsCount = ARRAYSIZE(saveLoadButtonsTable);
		break;
	}
}

void ToucheEngine::redrawMenu(MenuData *menu) {
	Graphics::fillRect(_offscreenBuffer, kScreenWidth, 90, 102, 460, 196, 0xF8);
	Graphics::drawRect(_offscreenBuffer, kScreenWidth, 90, 102, 460, 196, 0xF7, 0xF9);
	Graphics::drawRect(_offscreenBuffer, kScreenWidth, 106, 118, 340, 164, 0xF9, 0xF7);
	switch (menu->mode) {
	case kMenuSettingsMode:
		drawVolumeSlideBar(_offscreenBuffer, kScreenWidth, getMusicVolume());
		menu->buttonsTable[5].data = 0;
		menu->buttonsTable[6].data = 0;
		menu->buttonsTable[7].data = 0;
		menu->buttonsTable[5 + _talkTextMode].data = -86;
		break;
	case kMenuLoadStateMode:
	case kMenuSaveStateMode:
		drawSaveGameStateDescriptions(_offscreenBuffer, kScreenWidth, menu, _saveLoadCurrentPage, _saveLoadCurrentSlot);
		break;
	}
	for (uint i = 0; i < menu->buttonsCount; ++i) {
		drawButton(&menu->buttonsTable[i]);
	}
}

void ToucheEngine::handleMenuAction(MenuData *menu, int actionId) {
	switch (actionId) {
	case kActionLoadMenu:
		menu->mode = kMenuLoadStateMode;
		break;
	case kActionSaveMenu:
		_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
		menu->mode = kMenuSaveStateMode;
		break;
	case kActionRestartGame:
		restart();
		menu->quit = true;
		break;
	case kActionPlayGame:
		menu->quit = true;
		break;
	case kActionQuitGame:
		quitGame();
		menu->quit = true;
		break;
	case kActionTextOnly:
		_talkTextMode = kTalkModeTextOnly;
		break;
	case kActionVoiceOnly:
		_talkTextMode = kTalkModeVoiceOnly;
		break;
	case kActionTextAndVoice:
		_talkTextMode = kTalkModeVoiceAndText;
		break;
	case kActionLowerVolume:
		adjustMusicVolume(-16);
		break;
	case kActionUpperVolume:
		adjustMusicVolume(+16);
		break;
	case kActionScrollUpSaves:
		--_saveLoadCurrentPage;
		if (_saveLoadCurrentPage < 0) {
			_saveLoadCurrentPage = 9;
		}
		_saveLoadCurrentSlot = _saveLoadCurrentPage * 10 + (_saveLoadCurrentSlot % 10);
		break;
	case kActionScrollDownSaves:
		++_saveLoadCurrentPage;
		if (_saveLoadCurrentPage > 9) {
			_saveLoadCurrentPage = 0;
		}
		_saveLoadCurrentSlot = _saveLoadCurrentPage * 10 + (_saveLoadCurrentSlot % 10);
		break;
	case kActionPerformSaveLoad:
		if (menu->mode == kMenuLoadStateMode) {
			if (loadGameState(_saveLoadCurrentSlot).getCode() == Common::kNoError) {
				menu->quit = true;
			}
		} else if (menu->mode == kMenuSaveStateMode) {
			_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
			const char *description = menu->saveLoadDescriptionsTable[_saveLoadCurrentSlot];
			if (strlen(description) > 0) {
				if (saveGameState(_saveLoadCurrentSlot, description).getCode() == Common::kNoError) {
					menu->quit = true;
				}
			}
		}
		break;
	case kActionCancelSaveLoad:
		_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
		menu->mode = kMenuSettingsMode;
		break;
	default:
		if (actionId >= kActionGameState1 && actionId <= kActionGameState10) {
			_saveLoadCurrentSlot = _saveLoadCurrentPage * 10 + (actionId - kActionGameState1);
		}
		break;
	}
}

void ToucheEngine::handleOptions(int forceDisplay) {
	if (_disabledInputCounter == 0 || forceDisplay != 0) {
		setDefaultCursor(_currentKeyCharNum);
		_gameState = kGameStateOptionsDialog;
		MenuData menuData;
		memset(&menuData, 0, sizeof(MenuData));
		menuData.quit = false;
		menuData.exit = false;
		menuData.mode = kMenuSettingsMode;
		int curMode = -1;
		bool doRedraw = false;
		while (!menuData.quit) {
			if (menuData.mode != curMode) {
				doRedraw = true;
				setupMenu(menuData.mode, &menuData);
				curMode = menuData.mode;
				if (menuData.mode == kMenuLoadStateMode || menuData.mode == kMenuSaveStateMode) {
					for (int i = 0; i < kMaxSaveStates; ++i) {
						menuData.saveLoadDescriptionsTable[i][0] = 0;
					}
					Common::String gameStateFileName = generateGameStateFileName(_targetName.c_str(), 0, true);
					Common::StringArray filenames = _saveFileMan->listSavefiles(gameStateFileName);
					for (Common::StringArray::const_iterator it = filenames.begin(); it != filenames.end(); ++it) {
						int i = getGameStateFileSlot(it->c_str());
						if (i >= 0 && i < kMaxSaveStates) {
							Common::InSaveFile *f = _saveFileMan->openForLoading(*it);
							if (f) {
								readGameStateDescription(f, menuData.saveLoadDescriptionsTable[i], 32);
								delete f;
							}
						}
					}
				}
			}
			if (doRedraw) {
				redrawMenu(&menuData);
				updateScreenArea(90, 102, 460, 196);
				doRedraw = false;
			}
			Common::Event event;
			while (_eventMan->pollEvent(event)) {
				const Button *button = 0;
				switch (event.type) {
				case Common::EVENT_RTL:
				case Common::EVENT_QUIT:
					menuData.quit = true;
					menuData.exit = true;
					break;
				case Common::EVENT_LBUTTONDOWN:
					button = menuData.findButtonUnderCursor(event.mouse.x, event.mouse.y);
					if (button) {
						handleMenuAction(&menuData, button->action);
						doRedraw = true;
					}
					break;
				case Common::EVENT_KEYDOWN:
					if (menuData.mode == kMenuSaveStateMode) {
						if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
							menuData.removeLastCharFromDescription(_saveLoadCurrentSlot);
						} else {
							menuData.addCharToDescription(_saveLoadCurrentSlot, (char)event.kbd.ascii);
						}
						doRedraw = true;
					}
					break;
				case Common::EVENT_WHEELUP:
					handleMenuAction(&menuData, kActionScrollUpSaves);
					doRedraw = true;
					break;
				case Common::EVENT_WHEELDOWN:
					handleMenuAction(&menuData, kActionScrollDownSaves);
					doRedraw = true;
					break;
				default:
					break;
				}
			}
			_system->updateScreen();
			_system->delayMillis(10);
		}
		_fullRedrawCounter = 2;
		if (!menuData.exit && shouldQuit()) {
			if (displayQuitDialog())
				quitGame();
		}
		_gameState = kGameStateGameLoop;
	}
}

void ToucheEngine::drawActionsPanel(int dstX, int dstY, int deltaX, int deltaY) {
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, dstY,
	  _menuKitData, 42, 0, 0,
	  14, 24,
	  Graphics::kTransparent);
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, deltaX - 14 + dstX, dstY,
	  _menuKitData, 42, 0, 40,
	  14, 24,
	  Graphics::kTransparent);
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, deltaY - 16 + dstY,
	  _menuKitData, 42, 0, 24,
	  14, 16,
	  Graphics::kTransparent);
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, deltaX - 14 + dstX, deltaY - 16 + dstY,
	  _menuKitData, 42, 0, 64,
	  14, 16,
	  Graphics::kTransparent);
	int x1 = deltaX - 28;
	int x2 = dstX + 14;
	while (x1 > 0) {
		int w = (x1 > 14) ? 14 : x1;
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, x2, dstY,
		  _menuKitData, 42, 0, 80,
		  w, 24,
		  Graphics::kTransparent);
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, x2, deltaY - 16 + dstY,
		  _menuKitData, 42, 0, 104,
		  w, 16,
		  Graphics::kTransparent);
		x1 -= 14;
		x2 += 14;
	}
	x1 = deltaY - 40;
	x2 = dstY + 24;
	while (x1 > 0) {
		int w = (x1 > 120) ? 120 : x1;
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, x2,
		  _menuKitData, 42, 14, 0,
		  14, w,
		  Graphics::kTransparent);
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, deltaX - 14 + dstX, x2,
		  _menuKitData, 42, 28, 0,
		  14, w,
		  Graphics::kTransparent);
		x1 -= 120;
		x2 += 120;
	}
}

void ToucheEngine::drawConversationPanelBorder(int dstY, int srcX, int srcY) {
	int dstX = 24;
	int w = 48;
	for (int i = 0; i < 13; ++i) {
		if (i == 12) {
			w = 34;
		}
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, dstY, _convKitData, 152, srcX, srcY, w, 6);
		dstX += w;
	}
}

void ToucheEngine::drawConversationPanel() {
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 320, _convKitData, 152, 0, 0, 72, 80);
	int dstX = 54;
	int dstY = 326;
	int w = 96;
	for (int i = 0; i < 7; ++i) {
		if (i == 5) {
			w = 50;
		}
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, dstY, _convKitData, 152, 24, 6, w, 68);
		dstX += w;
	}
	--dstX;
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, 320, _convKitData, 152, 120, 0, 7, 80);
	dstX -= 3;
	if (_scrollConversationChoiceOffset != 0) {
		drawConversationPanelBorder(320, 72, 0);
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 320, _convKitData, 152, 128, 0, 24, 21);
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, 320, _convKitData, 152, 128, 34, 10, 10);
	} else {
		drawConversationPanelBorder(320, 24, 0);
	}
	if (_conversationChoicesTable[_scrollConversationChoiceOffset + 4].msg != 0) {
		drawConversationPanelBorder(394, 72, 74);
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 379, _convKitData, 152, 128, 59, 24, 21);
		Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, 394, _convKitData, 152, 128, 46, 10, 6);
	} else {
		drawConversationPanelBorder(394, 24, 74);
	}
}

void ToucheEngine::printStatusString(const char *str) {
	Graphics::fillRect(_offscreenBuffer, kScreenWidth, 0, 0, kScreenWidth, kTextHeight, 0xD7);
	Graphics::drawRect(_offscreenBuffer, kScreenWidth, 0, 0, kScreenWidth, kTextHeight, 0xD6, 0xD8);
	Graphics::drawString16(_offscreenBuffer, kScreenWidth, 0xFF, 0, 0, str);
	updateScreenArea(0, 0, kScreenWidth, kTextHeight);
	_system->updateScreen();
}

void ToucheEngine::clearStatusString() {
	Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 0,
	  _backdropBuffer, _currentBitmapWidth, _flagsTable[614], _flagsTable[615],
	  kScreenWidth, kTextHeight);
	updateScreenArea(0, 0, kScreenWidth, kTextHeight);
}

int ToucheEngine::displayQuitDialog() {
	debug(kDebugMenu, "ToucheEngine::displayQuitDialog()");
	_gameState = kGameStateQuitDialog;
	_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
	printStatusString(getString(-85));
	int ret = 0;
	bool quitLoop = false;
	while (!quitLoop) {
		Common::Event event;
		while (_eventMan->pollEvent(event)) {
			switch (event.type) {
			case Common::EVENT_RTL:
			case Common::EVENT_QUIT:
				quitLoop = true;
				ret = 1;
				break;
			case Common::EVENT_KEYDOWN:
				quitLoop = true;
				switch (_language) {
				case Common::FR_FRA:
					if (event.kbd.keycode == Common::KEYCODE_o) {
						ret = 1;
					}
					break;
				case Common::DE_DEU:
					if (event.kbd.keycode == Common::KEYCODE_j) {
						ret = 1;
					}
					break;
				case Common::ES_ESP:
					if (event.kbd.keycode == Common::KEYCODE_s) {
						ret = 1;
					}
					break;
				case Common::PL_POL:
					if (event.kbd.keycode == Common::KEYCODE_s || event.kbd.keycode == Common::KEYCODE_t) {
						ret = 1;
					}
					break;
				default:
					// According to cyx, the Italian version uses the same
					// keys as the English one.
					if (event.kbd.keycode == Common::KEYCODE_y) {
						ret = 1;
					}
					break;
				}
				break;
			default:
				break;
			}
		}
		_system->delayMillis(10);
		_system->updateScreen();
	}
	clearStatusString();
	_gameState = kGameStateGameLoop;
	_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
	return ret;
}

void ToucheEngine::displayTextMode(int str) {
	debug(kDebugMenu, "ToucheEngine::displayTextMode(%d)", str);
	printStatusString(getString(str));
	_system->delayMillis(1000);
	clearStatusString();
}

} // namespace Touche
back to top