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