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 "bladerunner/mouse.h"
24
25 #include "bladerunner/actor.h"
26 #include "bladerunner/bladerunner.h"
27 #include "bladerunner/combat.h"
28 #include "bladerunner/dialogue_menu.h"
29 #include "bladerunner/game_constants.h"
30 #include "bladerunner/items.h"
31 #include "bladerunner/regions.h"
32 #include "bladerunner/scene.h"
33 #include "bladerunner/scene_objects.h"
34 #include "bladerunner/settings.h"
35 #include "bladerunner/shape.h"
36 #include "bladerunner/time.h"
37 #include "bladerunner/view.h"
38 #include "bladerunner/zbuffer.h"
39
40 #include "graphics/surface.h"
41
42 namespace BladeRunner {
43
Mouse(BladeRunnerEngine * vm)44 Mouse::Mouse(BladeRunnerEngine *vm) {
45 _vm = vm;
46 _cursor = 0;
47 _frame = 3;
48 _hotspotX = 0;
49 _hotspotY = 0;
50 _x = 0;
51 _y = 0;
52 _disabledCounter = 0;
53 _lastFrameTime = 0;
54 _animCounter = 0;
55
56 _randomCountdownX = 0;
57 _randomCountdownY = 0;
58 _randomX = 0;
59 _randomY = 0;
60 }
61
~Mouse()62 Mouse::~Mouse() {
63 }
64
setCursor(int cursor)65 void Mouse::setCursor(int cursor) {
66 assert(cursor >= 0 && cursor <= 16);
67 if (cursor == _cursor) {
68 return;
69 }
70
71 _cursor = cursor;
72
73 switch (_cursor) {
74 case 0:
75 _frame = 3;
76 _hotspotX = 0;
77 _hotspotY = 0;
78 break;
79 case 1:
80 _frame = 4;
81 _hotspotX = 0;
82 _hotspotY = 0;
83 break;
84 case 2:
85 _frame = 12;
86 _hotspotX = 12;
87 _hotspotY = 0;
88 break;
89 case 3:
90 _frame = 15;
91 _hotspotX = 23;
92 _hotspotY = 12;
93 break;
94 case 4:
95 _frame = 13;
96 _hotspotX = 12;
97 _hotspotY = 23;
98 break;
99 case 5:
100 _frame = 14;
101 _hotspotX = 0;
102 _hotspotY = 12;
103 break;
104 case 6:
105 _frame = 16;
106 _hotspotX = 19;
107 _hotspotY = 19;
108 break;
109 case 7:
110 _frame = 17;
111 _hotspotX = 19;
112 _hotspotY = 19;
113 break;
114 case 8:
115 _frame = 25;
116 _hotspotX = 19;
117 _hotspotY = 19;
118 break;
119 case 9:
120 _frame = 26;
121 _hotspotX = 19;
122 _hotspotY = 19;
123 break;
124 case 10:
125 _frame = 34;
126 _hotspotX = 19;
127 _hotspotY = 19;
128 break;
129 case 11:
130 _frame = 35;
131 _hotspotX = 19;
132 _hotspotY = 19;
133 break;
134 case 12:
135 _frame = 12;
136 _hotspotX = 12;
137 _hotspotY = 0;
138 _animCounter = 0;
139 break;
140 case 13:
141 _frame = 15;
142 _hotspotX = 23;
143 _hotspotY = 12;
144 _animCounter = 0;
145 break;
146 case 14:
147 _frame = 13;
148 _hotspotX = 12;
149 _hotspotY = 23;
150 _animCounter = 0;
151 break;
152 case 15:
153 _frame = 14;
154 _hotspotX = 0;
155 _hotspotY = 12;
156 _animCounter = 0;
157 break;
158 case 16:
159 _frame = 0;
160 _hotspotX = 11;
161 _hotspotY = 11;
162 default:
163 break;
164 }
165 }
166
getXY(int * x,int * y) const167 void Mouse::getXY(int *x, int *y) const {
168 *x = _x;
169 *y = _y;
170 }
171
setMouseJitterUp()172 void Mouse::setMouseJitterUp() {
173 switch (_vm->_settings->getDifficulty()) {
174 default:
175 // fallthrough intended
176 case kGameDifficultyEasy:
177 _randomCountdownX = 2;
178 _randomX = _vm->_rnd.getRandomNumberRng(0, 6) - 3;
179 _randomY = _vm->_rnd.getRandomNumberRng(0, 10) - 20;
180 break;
181
182 case kGameDifficultyMedium:
183 _randomCountdownX = 3;
184 _randomX = _vm->_rnd.getRandomNumberRng(0, 8) - 4;
185 _randomY = _vm->_rnd.getRandomNumberRng(0, 10) - 25;
186 break;
187
188 case kGameDifficultyHard:
189 _randomCountdownX = 4;
190 _randomX = _vm->_rnd.getRandomNumberRng(0, 10) - 5;
191 _randomY = _vm->_rnd.getRandomNumberRng(0, 10) - 30;
192 break;
193 }
194 }
195
setMouseJitterDown()196 void Mouse::setMouseJitterDown() {
197 switch (_vm->_settings->getDifficulty()) {
198 default:
199 // fallthrough intended
200 case kGameDifficultyEasy:
201 _randomCountdownY = 2;
202 _randomX = _vm->_rnd.getRandomNumberRng(0, 6) - 3;
203 _randomY = _vm->_rnd.getRandomNumberRng(10, 20);
204 break;
205
206 case kGameDifficultyMedium:
207 _randomCountdownY = 3;
208 _randomX = _vm->_rnd.getRandomNumberRng(0, 8) - 4;
209 _randomY = _vm->_rnd.getRandomNumberRng(15, 25);
210 break;
211
212 case kGameDifficultyHard:
213 _randomCountdownY = 4;
214 _randomX = _vm->_rnd.getRandomNumberRng(0, 10) - 5;
215 _randomY = _vm->_rnd.getRandomNumberRng(20, 30);
216 break;
217 }
218 }
219
disable()220 void Mouse::disable() {
221 ++_disabledCounter;
222
223 _randomCountdownX = 0;
224 _randomCountdownY = 0;
225 }
226
enable(bool force)227 void Mouse::enable(bool force) {
228 if (force || --_disabledCounter <= 0) {
229 _disabledCounter = 0;
230 }
231 }
232
isDisabled() const233 bool Mouse::isDisabled() const {
234 return _disabledCounter > 0;
235 }
236
draw(Graphics::Surface & surface,int x,int y)237 void Mouse::draw(Graphics::Surface &surface, int x, int y) {
238 if (_disabledCounter) {
239 _randomCountdownX = 0;
240 _randomCountdownY = 0;
241 return;
242 }
243
244 if (_randomCountdownX > 0) {
245 --_randomCountdownX;
246 x += _randomX;
247 y += _randomY;
248
249 if (!_randomCountdownX)
250 setMouseJitterDown();
251 } else if (_randomCountdownY > 0) {
252 --_randomCountdownY;
253 x += _randomX;
254 y += _randomY;
255 }
256
257 _x = CLIP(x, 0, surface.w - 1);
258 _y = CLIP(y, 0, surface.h - 1);
259
260 _vm->_shapes->get(_frame)->draw(surface, _x - _hotspotX, _y - _hotspotY);
261
262 updateCursorFrame();
263 }
264
updateCursorFrame()265 void Mouse::updateCursorFrame() {
266 uint32 now = _vm->_time->current();
267 const int offset[4] = { 0, 6, 12, 6 };
268
269 if (now - _lastFrameTime < 66) {
270 return;
271 }
272 _lastFrameTime = now;
273
274 switch (_cursor) {
275 case 0:
276 break;
277 case 1:
278 if (++_frame > 11)
279 _frame = 4;
280 break;
281 case 2:
282 case 3:
283 case 4:
284 case 5:
285 case 6:
286 break;
287 case 7:
288 if (++_frame > 24)
289 _frame = 17;
290 break;
291 case 8:
292 break;
293 case 9:
294 if (++_frame > 33)
295 _frame = 26;
296 break;
297 case 10:
298 break;
299 case 11:
300 if (++_frame > 42)
301 _frame = 35;
302 break;
303 case 12:
304 if (++_animCounter >= 4) {
305 _animCounter = 0;
306 }
307 _hotspotY = -offset[_animCounter];
308 break;
309 case 13:
310 if (++_animCounter >= 4) {
311 _animCounter = 0;
312 }
313 _hotspotX = 23 + offset[_animCounter];
314 break;
315 case 14:
316 if (++_animCounter >= 4) {
317 _animCounter = 0;
318 }
319 _hotspotY = 23 + offset[_animCounter];
320 break;
321 case 15:
322 if (++_animCounter >= 4) {
323 _animCounter = 0;
324 }
325 _hotspotX = -offset[_animCounter];
326 break;
327 case 16:
328 if (++_frame > 2)
329 _frame = 0;
330 break;
331 default:
332 break;
333 }
334 }
335
tick(int x,int y)336 void Mouse::tick(int x, int y) {
337 if (!_vm->playerHasControl() || isDisabled()) {
338 return;
339 }
340
341 if (_vm->_dialogueMenu->isVisible()) {
342 setCursor(0);
343 return;
344 }
345
346 Vector3 scenePosition = getXYZ(x, y);
347 int cursorId = 0;
348
349 bool isClickable = false;
350 bool isObstacle = false;
351 bool isTarget = false;
352
353 int sceneObjectId = _vm->_sceneObjects->findByXYZ(&isClickable, &isObstacle, &isTarget, scenePosition, true, false, true);
354 int exitType = _vm->_scene->_exits->getTypeAtXY(x, y);
355
356 if (sceneObjectId >= kSceneObjectOffsetActors && sceneObjectId < kSceneObjectOffsetItems) {
357 exitType = -1;
358 }
359
360 if (exitType != -1) {
361 switch (exitType) {
362 case 0:
363 cursorId = 12;
364 break;
365 case 1:
366 cursorId = 13;
367 break;
368 case 2:
369 cursorId = 14;
370 break;
371 case 3:
372 cursorId = 15;
373 break;
374 default:
375 break;
376 }
377 setCursor(cursorId);
378 return;
379 }
380
381 if (!_vm->_combat->isActive()) {
382 if (sceneObjectId == kActorMcCoy + kSceneObjectOffsetActors
383 || (sceneObjectId > 0 && isClickable)
384 || _vm->_scene->_regions->getRegionAtXY(x, y) >= 0) {
385 cursorId = 1;
386 }
387 setCursor(cursorId);
388 return;
389 }
390
391 int animationMode = _vm->_playerActor->getAnimationMode();
392 int actorId = Actor::findTargetUnderMouse(_vm, x, y);
393 int itemId = _vm->_items->findTargetUnderMouse(x, y);
394
395 bool isObject = isTarget && sceneObjectId >= kSceneObjectOffsetObjects && sceneObjectId <= (95 + kSceneObjectOffsetObjects);
396
397 if (!_vm->_playerActor->isMoving()) {
398 if (actorId >= 0) {
399 _vm->_playerActor->faceActor(actorId, false);
400 } else if (itemId >= 0) {
401 _vm->_playerActor->faceItem(itemId, false);
402 } else if (isObject) {
403 _vm->_playerActor->faceXYZ(scenePosition, false);
404 }
405 }
406
407 if (actorId >= 0 || itemId >= 0 || isObject) {
408 switch (_vm->_settings->getAmmoType()) {
409 case 0:
410 cursorId = 7;
411 break;
412 case 1:
413 cursorId = 9;
414 break;
415 case 2:
416 cursorId = 11;
417 break;
418 default:
419 break;
420 }
421
422 if (!_vm->_playerActor->isMoving() && animationMode != kAnimationModeCombatAim && animationMode != kAnimationModeCombatHit && animationMode != kAnimationModeCombatDie) {
423 _vm->_playerActor->changeAnimationMode(kAnimationModeCombatAim, false);
424 }
425 } else {
426 switch (_vm->_settings->getAmmoType()) {
427 case 0:
428 cursorId = 6;
429 break;
430 case 1:
431 cursorId = 8;
432 break;
433 case 2:
434 cursorId = 10;
435 break;
436 default:
437 break;
438 }
439 if (!_vm->_playerActor->isMoving() && animationMode != kAnimationModeCombatIdle && animationMode != kAnimationModeCombatHit && animationMode != kAnimationModeCombatDie) {
440 _vm->_playerActor->changeAnimationMode(kAnimationModeCombatIdle, false);
441 }
442 }
443 setCursor(cursorId);
444 }
445
isInactive() const446 bool Mouse::isInactive() const {
447 return _cursor == 6 || _cursor == 8 || _cursor == 10;
448 }
449
450 // TEST: RC01 after intro: [290, 216] -> [-204.589249 51.450668 7.659241]
getXYZ(int x,int y) const451 Vector3 Mouse::getXYZ(int x, int y) const {
452 if (_vm->_scene->getSetId() == -1)
453 return Vector3();
454
455 int screenRight = 640 - x;
456 int screenDown = 480 - y;
457
458 float zcoef = 1.0f / tan(_vm->_view->_fovX / 2.0f);
459
460 float x3d = (2.0f / 640.0f * screenRight - 1.0f);
461 float y3d = (2.0f / 480.0f * screenDown - 1.0f) * 0.75f;
462
463 uint16 zbufval = _vm->_zbuffer->getZValue(x, y);
464
465 Vector3 pos;
466 pos.z = zbufval / 25.5f;
467 pos.x = pos.z / zcoef * x3d;
468 pos.y = pos.z / zcoef * y3d;
469
470 Matrix4x3 matrix = _vm->_view->_frameViewMatrix;
471
472 matrix.unknown();
473
474 return matrix * pos;
475 }
476
477 } // End of namespace BladeRunner
478