1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/events.h"
24 #include "common/stream.h"
25 #include "common/system.h"
26 
27 #include "engines/stark/services/userinterface.h"
28 
29 #include "engines/stark/gfx/driver.h"
30 
31 #include "engines/stark/services/gameinterface.h"
32 #include "engines/stark/services/global.h"
33 #include "engines/stark/services/services.h"
34 #include "engines/stark/services/staticprovider.h"
35 
36 #include "engines/stark/ui/actionmenu.h"
37 #include "engines/stark/ui/cursor.h"
38 #include "engines/stark/ui/dialogpanel.h"
39 #include "engines/stark/ui/fmvplayer.h"
40 #include "engines/stark/ui/gamewindow.h"
41 #include "engines/stark/ui/inventorywindow.h"
42 #include "engines/stark/ui/topmenu.h"
43 
44 namespace Stark {
45 
UserInterface(Gfx::Driver * gfx)46 UserInterface::UserInterface(Gfx::Driver *gfx) :
47 		_gfx(gfx),
48 		_cursor(nullptr),
49 		_topMenu(nullptr),
50 		_dialogPanel(nullptr),
51 		_inventoryWindow(nullptr),
52 		_exitGame(false),
53 		_fmvPlayer(nullptr),
54 		_actionMenu(nullptr),
55 		_gameWindow(nullptr),
56 		_interactive(true),
57 		_interactionAttemptDenied(false),
58 		_currentScreen(kScreenGame),
59 		_gameWindowThumbnail(nullptr) {
60 }
61 
~UserInterface()62 UserInterface::~UserInterface() {
63 	freeGameScreenThumbnail();
64 
65 	delete _gameWindow;
66 	delete _actionMenu;
67 	delete _topMenu;
68 	delete _dialogPanel;
69 	delete _inventoryWindow;
70 	delete _fmvPlayer;
71 	delete _cursor;
72 }
73 
init()74 void UserInterface::init() {
75 	// Game screen windows
76 	_cursor = new Cursor(_gfx);
77 	_topMenu = new TopMenu(_gfx, _cursor);
78 	_dialogPanel = new DialogPanel(_gfx, _cursor);
79 	_actionMenu = new ActionMenu(_gfx, _cursor);
80 	_inventoryWindow = new InventoryWindow(_gfx, _cursor, _actionMenu);
81 	_actionMenu->setInventory(_inventoryWindow);
82 	_gameWindow = new GameWindow(_gfx, _cursor, _actionMenu, _inventoryWindow);
83 
84 	_gameScreenWindows.push_back(_actionMenu);
85 	_gameScreenWindows.push_back(_inventoryWindow);
86 	_gameScreenWindows.push_back(_gameWindow);
87 	_gameScreenWindows.push_back(_topMenu);
88 	_gameScreenWindows.push_back(_dialogPanel);
89 
90 	// FMV Screen window
91 	_fmvPlayer = new FMVPlayer(_gfx, _cursor);
92 }
93 
update()94 void UserInterface::update() {
95 	StarkStaticProvider->onGameLoop();
96 
97 	// Check for UI mouse overs
98 	dispatchEvent(&Window::handleMouseMove);
99 }
100 
handleMouseMove(const Common::Point & pos)101 void UserInterface::handleMouseMove(const Common::Point &pos) {
102 	_cursor->setMousePosition(pos);
103 }
104 
handleClick()105 void UserInterface::handleClick() {
106 	dispatchEvent(&Window::handleClick);
107 }
108 
handleRightClick()109 void UserInterface::handleRightClick() {
110 	dispatchEvent(&Window::handleRightClick);
111 }
112 
handleDoubleClick()113 void UserInterface::handleDoubleClick() {
114 	dispatchEvent(&Window::handleDoubleClick);
115 }
116 
dispatchEvent(WindowHandler handler)117 void UserInterface::dispatchEvent(WindowHandler handler) {
118 	switch (_currentScreen) {
119 		case kScreenGame:
120 			for (uint i = 0; i < _gameScreenWindows.size(); i++) {
121 				if (_gameScreenWindows[i]->isMouseInside()) {
122 					(*_gameScreenWindows[i].*handler)();
123 					return;
124 				}
125 			}
126 			break;
127 		case kScreenFMV:
128 			if (_fmvPlayer->isMouseInside()) {
129 				(*_fmvPlayer.*handler)();
130 				return;
131 			}
132 			break;
133 		default: // Nothing goes here
134 			break;
135 	}
136 }
137 
inventoryOpen(bool open)138 void UserInterface::inventoryOpen(bool open) {
139 	// Make the inventory update its contents.
140 	if (open) {
141 		_inventoryWindow->open();
142 	} else {
143 		_inventoryWindow->close();
144 	}
145 }
146 
getSelectedInventoryItem() const147 int16 UserInterface::getSelectedInventoryItem() const {
148 	if (_inventoryWindow) {
149 		return _inventoryWindow->getSelectedInventoryItem();
150 	} else {
151 		return -1;
152 	}
153 }
154 
selectInventoryItem(int16 itemIndex)155 void UserInterface::selectInventoryItem(int16 itemIndex) {
156 	_inventoryWindow->setSelectedInventoryItem(itemIndex);
157 }
158 
requestFMVPlayback(const Common::String & name)159 void UserInterface::requestFMVPlayback(const Common::String &name) {
160 	// TODO: Save the current screen so that it can be restored when the playback ends
161 	changeScreen(kScreenFMV);
162 
163 	_fmvPlayer->play(name);
164 }
165 
onFMVStopped()166 void UserInterface::onFMVStopped() {
167 	// TODO: Restore the previous screen
168 	changeScreen(kScreenGame);
169 }
170 
changeScreen(Screen screen)171 void UserInterface::changeScreen(Screen screen) {
172 	_currentScreen = screen;
173 }
174 
isInGameScreen() const175 bool UserInterface::isInGameScreen() const {
176 	return _currentScreen == kScreenGame;
177 }
178 
isInventoryOpen() const179 bool UserInterface::isInventoryOpen() const {
180 	return _inventoryWindow->isVisible();
181 }
182 
skipFMV()183 bool UserInterface::skipFMV() {
184 	if (_currentScreen == kScreenFMV) {
185 		_fmvPlayer->stop();
186 		return true;
187 	}
188 	return false;
189 }
190 
render()191 void UserInterface::render() {
192 	switch (_currentScreen) {
193 		case kScreenGame:
194 			for (int i = _gameScreenWindows.size() - 1; i >= 0; i--) {
195 				_gameScreenWindows[i]->render();
196 			}
197 			break;
198 		case kScreenFMV:
199 			_fmvPlayer->render();
200 			break;
201 		default: // Nothing goes here
202 			break;
203 	}
204 
205 	// The cursor depends on the UI being done.
206 	_cursor->render();
207 }
208 
isInteractive() const209 bool UserInterface::isInteractive() const {
210 	return _interactive;
211 }
212 
setInteractive(bool interactive)213 void UserInterface::setInteractive(bool interactive) {
214 	if (interactive && !_interactive) {
215 		StarkGlobal->setNormalSpeed();
216 	} else if (!interactive && _interactive) {
217 		_interactionAttemptDenied = false;
218 	}
219 
220 	_interactive = interactive;
221 }
222 
markInteractionDenied()223 void UserInterface::markInteractionDenied() {
224 	if (!_interactive) {
225 		_interactionAttemptDenied = true;
226 	}
227 }
228 
wasInteractionDenied() const229 bool UserInterface::wasInteractionDenied() const {
230 	return !_interactive && _interactionAttemptDenied;
231 }
232 
clearLocationDependentState()233 void UserInterface::clearLocationDependentState() {
234 	_dialogPanel->reset();
235 	_gameWindow->reset();
236 	_inventoryWindow->reset();
237 }
238 
optionsOpen()239 void UserInterface::optionsOpen() {
240 	// TODO: Open the TLJ menu instead of the ResidualVM one
241 	Common::Event event;
242 	event.type = Common::EVENT_MAINMENU;
243 	g_system->getEventManager()->pushEvent(event);
244 }
245 
saveGameScreenThumbnail()246 void UserInterface::saveGameScreenThumbnail() {
247 	freeGameScreenThumbnail();
248 
249 	Graphics::Surface *big = _gameWindow->getScreenshot();
250 	assert(big->format.bytesPerPixel == 4);
251 
252 	_gameWindowThumbnail = new Graphics::Surface();
253 	_gameWindowThumbnail->create(kThumbnailWidth, kThumbnailHeight, big->format);
254 
255 	uint32 *dst = (uint32 *)_gameWindowThumbnail->getPixels();
256 	for (uint i = 0; i < _gameWindowThumbnail->h; i++) {
257 		for (uint j = 0; j < _gameWindowThumbnail->w; j++) {
258 			uint32 srcX = big->w * j / _gameWindowThumbnail->w;
259 			uint32 srcY = big->h * i / _gameWindowThumbnail->h;
260 			uint32 *src = (uint32 *)big->getBasePtr(srcX, srcY);
261 
262 			// Copy RGBA pixel
263 			*dst++ = *src;
264 		}
265 	}
266 
267 	big->free();
268 	delete big;
269 }
270 
freeGameScreenThumbnail()271 void UserInterface::freeGameScreenThumbnail() {
272 	if (_gameWindowThumbnail) {
273 		_gameWindowThumbnail->free();
274 		delete _gameWindowThumbnail;
275 		_gameWindowThumbnail = nullptr;
276 	}
277 }
278 
getGameWindowThumbnail() const279 const Graphics::Surface *UserInterface::getGameWindowThumbnail() const {
280 	return _gameWindowThumbnail;
281 }
282 
onScreenChanged()283 void UserInterface::onScreenChanged() {
284 	_dialogPanel->onScreenChanged();
285 }
286 
287 } // End of namespace Stark
288 
289