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