1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/events.h"
24 #include "common/singleton.h"
25 #include "graphics/cursorman.h"
26 #include "common/system.h"
27 
28 #include "tsage/events.h"
29 #include "tsage/core.h"
30 #include "tsage/staticres.h"
31 #include "tsage/tsage.h"
32 #include "tsage/globals.h"
33 
34 namespace TsAGE {
35 
EventsClass()36 EventsClass::EventsClass() {
37 	_currentCursor = CURSOR_NONE;
38 	_lastCursor = CURSOR_NONE;
39 	_frameNumber = 0;
40 	_priorFrameTime = 0;
41 	_prevDelayFrame = 0;
42 	g_saver->addListener(this);
43 	g_saver->addLoadNotifier(&EventsClass::loadNotifierProc);
44 }
45 
pollEvent()46 bool EventsClass::pollEvent() {
47 	uint32 milli = g_system->getMillis();
48 	if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
49 		_priorFrameTime = milli;
50 		++_frameNumber;
51 
52 		// Update screen
53 		GLOBALS._screen.update();
54 	}
55 
56 	if (!g_system->getEventManager()->pollEvent(_event)) return false;
57 
58 	// Handle keypress
59 	switch (_event.type) {
60 	case Common::EVENT_QUIT:
61 	case Common::EVENT_RTL:
62 		break;
63 
64 	case Common::EVENT_MOUSEMOVE:
65 	case Common::EVENT_LBUTTONDOWN:
66 	case Common::EVENT_LBUTTONUP:
67 	case Common::EVENT_RBUTTONDOWN:
68 	case Common::EVENT_RBUTTONUP:
69 		// Keep a copy of the current mouse position
70 		_mousePos = _event.mouse;
71 		break;
72 
73 	default:
74 		break;
75 	}
76 
77 	return true;
78 }
79 
waitForPress(int eventMask)80 void EventsClass::waitForPress(int eventMask) {
81 	Event evt;
82 	while (!g_vm->shouldQuit() && !getEvent(evt, eventMask))
83 		g_system->delayMillis(10);
84 }
85 
86 /**
87  * Standard event retrieval, which only returns keyboard and mouse clicks
88  */
getEvent(Event & evt,int eventMask)89 bool EventsClass::getEvent(Event &evt, int eventMask) {
90 	while (pollEvent() && !g_vm->shouldQuit()) {
91 		evt.handled = false;
92 		evt.eventType = EVENT_NONE;
93 		evt.mousePos = _event.mouse;
94 		evt.kbd = _event.kbd;
95 
96 		switch (_event.type) {
97 		case Common::EVENT_MOUSEMOVE:
98 			evt.eventType = EVENT_MOUSE_MOVE;
99 			break;
100 		case Common::EVENT_LBUTTONDOWN:
101 			evt.eventType = EVENT_BUTTON_DOWN;
102 			evt.btnState = BTNSHIFT_LEFT;
103 			break;
104 		case Common::EVENT_RBUTTONDOWN:
105 			evt.eventType = EVENT_BUTTON_DOWN;
106 			evt.btnState = BTNSHIFT_RIGHT;
107 			break;
108 		case Common::EVENT_MBUTTONDOWN:
109 			evt.eventType = EVENT_BUTTON_DOWN;
110 			evt.btnState = BTNSHIFT_MIDDLE;
111 			break;
112 		case Common::EVENT_LBUTTONUP:
113 		case Common::EVENT_RBUTTONUP:
114 		case Common::EVENT_MBUTTONUP:
115 			evt.eventType = EVENT_BUTTON_UP;
116 			evt.btnState = BTNSHIFT_LEFT;
117 			break;
118 		case Common::EVENT_KEYDOWN:
119 			evt.eventType = EVENT_KEYPRESS;
120 			evt.kbd = _event.kbd;
121 			break;
122 		default:
123 			break;
124 		}
125 
126 		if (evt.eventType & eventMask)
127 			return true;
128 	}
129 
130 	evt.handled = false;
131 	evt.eventType = EVENT_NONE;
132 
133 	return false;
134 }
135 
136 /**
137  * Sets the specified cursor
138  *
139  * @cursorType Specified cursor number
140  */
setCursor(CursorType cursorType)141 void EventsClass::setCursor(CursorType cursorType) {
142 	if (cursorType == _lastCursor)
143 		return;
144 
145 	_lastCursor = cursorType;
146 	g_globals->clearFlag(122);
147 	CursorMan.showMouse(true);
148 
149 	const byte *cursor;
150 	bool delFlag = true;
151 	uint size;
152 	bool questionEnabled = false;
153 
154 	switch (cursorType) {
155 	case CURSOR_NONE:
156 		// No cursor
157 		g_globals->setFlag(122);
158 
159 		if ((g_vm->getGameID() != GType_Ringworld) || ((g_vm->getGameID() == GType_Ringworld) && (g_vm->getFeatures() & GF_DEMO)))  {
160 			CursorMan.showMouse(false);
161 			return;
162 		}
163 		cursor = g_resourceManager->getSubResource(4, 1, 6, &size);
164 		break;
165 
166 	case CURSOR_LOOK:
167 		// Look cursor
168 		if (g_vm->getGameID() == GType_BlueForce) {
169 			cursor = g_resourceManager->getSubResource(1, 5, 3, &size);
170 		} else if (g_vm->getGameID() == GType_Ringworld2) {
171 			cursor = g_resourceManager->getSubResource(5, 1, 5, &size);
172 		} else {
173 			cursor = g_resourceManager->getSubResource(4, 1, 5, &size);
174 		}
175 		_currentCursor = CURSOR_LOOK;
176 		break;
177 
178 	case CURSOR_USE:
179 		// Use cursor
180 		if (g_vm->getGameID() == GType_BlueForce) {
181 			cursor = g_resourceManager->getSubResource(1, 5, 2, &size);
182 		} else if (g_vm->getGameID() == GType_Ringworld2) {
183 			cursor = g_resourceManager->getSubResource(5, 1, 4, &size);
184 		} else {
185 			cursor = g_resourceManager->getSubResource(4, 1, 4, &size);
186 		}
187 		_currentCursor = CURSOR_USE;
188 		break;
189 
190 	case CURSOR_TALK:
191 		// Talk cursor
192 		if (g_vm->getGameID() == GType_BlueForce) {
193 			cursor = g_resourceManager->getSubResource(1, 5, 4, &size);
194 		} else if (g_vm->getGameID() == GType_Ringworld2) {
195 			cursor = g_resourceManager->getSubResource(5, 1, 6, &size);
196 		} else {
197 			cursor = g_resourceManager->getSubResource(4, 1, 3, &size);
198 		}
199 		_currentCursor = CURSOR_TALK;
200 		break;
201 
202 	case CURSOR_EXIT:
203 		// Exit cursor (Blue Force)
204 		assert(g_vm->getGameID() == GType_BlueForce);
205 		cursor = g_resourceManager->getSubResource(1, 5, 7, &size);
206 		_currentCursor = CURSOR_EXIT;
207 		break;
208 
209 	case CURSOR_PRINTER:
210 		// Printer cursor (Blue Force)
211 		assert(g_vm->getGameID() == GType_BlueForce);
212 		cursor = g_resourceManager->getSubResource(1, 7, 6, &size);
213 		_currentCursor = CURSOR_PRINTER;
214 		break;
215 
216 	case CURSOR_ARROW:
217 		// Arrow cursor
218 		cursor = CURSOR_ARROW_DATA;
219 		delFlag = false;
220 		break;
221 
222 	case CURSOR_WALK:
223 	default:
224 		switch (g_vm->getGameID()) {
225 		case GType_BlueForce:
226 			if (cursorType == CURSOR_WALK) {
227 				cursor = g_resourceManager->getSubResource(1, 5, 1, &size);
228 			} else {
229 				// Inventory icon
230 				cursor = g_resourceManager->getSubResource(10, ((int)cursorType - 1) / 20 + 1,
231 					((int)cursorType - 1) % 20 + 1, &size);
232 				questionEnabled = true;
233 			}
234 			_currentCursor = cursorType;
235 			break;
236 		case GType_Ringworld2:
237 			if (cursorType == CURSOR_WALK) {
238 				cursor = CURSOR_WALK_DATA;
239 				delFlag = false;
240 			} else {
241 				// Inventory icon
242 				InvObject *invObject = g_globals->_inventory->getItem((int)cursorType);
243 				cursor = g_resourceManager->getSubResource(6, invObject->_strip, invObject->_frame, &size);
244 				questionEnabled = true;
245 			}
246 			_currentCursor = cursorType;
247 			break;
248 		default:
249 			// For Ringworld, always treat as the walk cursor
250 			cursor = CURSOR_WALK_DATA;
251 			_currentCursor = CURSOR_WALK;
252 			delFlag = false;
253 			break;
254 		}
255 		break;
256 
257 	// Ringworld 2 specific cursors
258 	case EXITCURSOR_N:
259 	case EXITCURSOR_S:
260 	case EXITCURSOR_W:
261 	case EXITCURSOR_E:
262 	case EXITCURSOR_LEFT_HAND:
263 	case CURSOR_INVALID:
264 	case EXITCURSOR_NE:
265 	case EXITCURSOR_SE:
266 	case EXITCURSOR_SW:
267 	case EXITCURSOR_NW:
268 	case SHADECURSOR_UP:
269 	case SHADECURSOR_DOWN:
270 	case SHADECURSOR_HAND:
271 		_currentCursor = cursorType;
272 		cursor = g_resourceManager->getSubResource(5, 1, cursorType - R2CURSORS_START, &size);
273 		break;
274 
275 	case R2_CURSOR_ROPE:
276 		_currentCursor = cursorType;
277 		cursor = g_resourceManager->getSubResource(5, 4, 1, &size);
278 		break;
279 	}
280 
281 	// Decode the cursor
282 	GfxSurface s = surfaceFromRes(cursor);
283 
284 	Graphics::Surface surface = s.lockSurface();
285 	const byte *cursorData = (const byte *)surface.getPixels();
286 	CursorMan.replaceCursor(cursorData, surface.w, surface.h, s._centroid.x, s._centroid.y, s._transColor);
287 	s.unlockSurface();
288 
289 	if (delFlag)
290 		DEALLOCATE(cursor);
291 
292 	// For Blue Force and Return to Ringworld, enable the question button when an inventory icon is selected
293 	if (g_vm->getGameID() != GType_Ringworld)
294 		T2_GLOBALS._uiElements._question.setEnabled(questionEnabled);
295 }
296 
pushCursor(CursorType cursorType)297 void EventsClass::pushCursor(CursorType cursorType) {
298 	const byte *cursor;
299 	bool delFlag = true;
300 	uint size;
301 
302 	switch (cursorType) {
303 	case CURSOR_NONE:
304 		// No cursor
305 		cursor = g_resourceManager->getSubResource(4, 1, 6, &size);
306 		break;
307 
308 	case CURSOR_LOOK:
309 		// Look cursor
310 		cursor = g_resourceManager->getSubResource(4, 1, 5, &size);
311 		break;
312 
313 	case CURSOR_USE:
314 		// Use cursor
315 		cursor = g_resourceManager->getSubResource(4, 1, 4, &size);
316 		break;
317 
318 	case CURSOR_TALK:
319 		// Talk cursor
320 		cursor = g_resourceManager->getSubResource(4, 1, 3, &size);
321 		break;
322 
323 	case CURSOR_ARROW:
324 		// Arrow cursor
325 		cursor = CURSOR_ARROW_DATA;
326 		delFlag = false;
327 		break;
328 
329 	case CURSOR_WALK:
330 	default:
331 		// Walk cursor
332 		cursor = CURSOR_WALK_DATA;
333 		delFlag = false;
334 		break;
335 	}
336 
337 	// Decode the cursor
338 	GfxSurface s = surfaceFromRes(cursor);
339 
340 	Graphics::Surface surface = s.lockSurface();
341 	const byte *cursorData = (const byte *)surface.getPixels();
342 	CursorMan.pushCursor(cursorData, surface.w, surface.h, s._centroid.x, s._centroid.y, s._transColor);
343 	s.unlockSurface();
344 
345 	if (delFlag)
346 		DEALLOCATE(cursor);
347 }
348 
popCursor()349 void EventsClass::popCursor() {
350 	CursorMan.popCursor();
351 }
352 
setCursor(Graphics::Surface & cursor,int transColor,const Common::Point & hotspot,CursorType cursorId)353 void EventsClass::setCursor(Graphics::Surface &cursor, int transColor, const Common::Point &hotspot, CursorType cursorId) {
354 	const byte *cursorData = (const byte *)cursor.getPixels();
355 	CursorMan.replaceCursor(cursorData, cursor.w, cursor.h, hotspot.x, hotspot.y, transColor);
356 
357 	_currentCursor = cursorId;
358 }
359 
setCursor(GfxSurface & cursor)360 void EventsClass::setCursor(GfxSurface &cursor) {
361 	Graphics::Surface s = cursor.lockSurface();
362 
363 	const byte *cursorData = (const byte *)s.getPixels();
364 	CursorMan.replaceCursor(cursorData, cursor.getBounds().width(), cursor.getBounds().height(),
365 		cursor._centroid.x, cursor._centroid.y, cursor._transColor);
366 
367 	_lastCursor = CURSOR_NONE;
368 }
369 
setCursorFromFlag()370 void EventsClass::setCursorFromFlag() {
371 	setCursor(isCursorVisible() ? _currentCursor : CURSOR_NONE);
372 }
373 
showCursor()374 void EventsClass::showCursor() {
375 	setCursor(_currentCursor);
376 }
377 
hideCursor()378 CursorType EventsClass::hideCursor() {
379 	CursorType oldCursor = _currentCursor;
380 	setCursor(CURSOR_NONE);
381 	return oldCursor;
382 }
383 
isCursorVisible() const384 bool EventsClass::isCursorVisible() const {
385 	return !g_globals->getFlag(122);
386 }
387 
388 /**
389  * Delays the game for the specified number of frames, if necessary, from the
390  * previous time the delay method was called
391  */
delay(int numFrames)392 void EventsClass::delay(int numFrames) {
393 	while (_frameNumber < (_prevDelayFrame + numFrames)) {
394 		uint32 delayAmount = CLIP(_priorFrameTime + GAME_SCRIPT_TIME - g_system->getMillis(),
395 			(uint32)0, (uint32)GAME_FRAME_TIME);
396 		if (delayAmount > 0)
397 			g_system->delayMillis(delayAmount);
398 
399 		++_frameNumber;
400 		_priorFrameTime = g_system->getMillis();
401 	}
402 
403 	GLOBALS._screen.update();
404 	_prevDelayFrame = _frameNumber;
405 	_priorFrameTime = g_system->getMillis();
406 }
407 
listenerSynchronize(Serializer & s)408 void EventsClass::listenerSynchronize(Serializer &s) {
409 	s.syncAsUint32LE(_frameNumber);
410 	s.syncAsUint32LE(_prevDelayFrame);
411 
412 	if (s.getVersion() >= 5) {
413 		s.syncAsSint16LE(_currentCursor);
414 		s.syncAsSint16LE(_lastCursor);
415 	}
416 }
417 
loadNotifierProc(bool postFlag)418 void EventsClass::loadNotifierProc(bool postFlag) {
419 	if (postFlag) {
420 		if (g_globals->_events._lastCursor == CURSOR_NONE)
421 			g_globals->_events._lastCursor = g_globals->_events._currentCursor;
422 		else
423 			g_globals->_events._lastCursor = CURSOR_NONE;
424 	}
425 }
426 
427 } // end of namespace TsAGE
428