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/actor_walk.h"
24 
25 #include "bladerunner/bladerunner.h"
26 
27 #include "bladerunner/actor.h"
28 #include "bladerunner/game_constants.h"
29 #include "bladerunner/game_info.h"
30 #include "bladerunner/obstacles.h"
31 #include "bladerunner/savefile.h"
32 #include "bladerunner/scene.h"
33 #include "bladerunner/scene_objects.h"
34 #include "bladerunner/set.h"
35 
36 namespace BladeRunner {
37 
ActorWalk(BladeRunnerEngine * vm)38 ActorWalk::ActorWalk(BladeRunnerEngine *vm) {
39 	_vm = vm;
40 
41 	reset();
42 }
43 
~ActorWalk()44 ActorWalk::~ActorWalk() {}
45 
46 // added method for bug fix (bad new game state for player actor) and better management of object
reset()47 void ActorWalk::reset() {
48 	_walking = false;
49 	_running = false;
50 	_facing = -1;
51 	_status = 0;
52 
53 	_destination = Vector3(0.0f, 0.0f, 0.0f);
54 	_originalDestination = Vector3(0.0f, 0.0f, 0.0f);
55 	_current = Vector3(0.0f, 0.0f, 0.0f);
56 	_next = Vector3(0.0f, 0.0f, 0.0f);
57 
58 	_nearActors.clear();
59 }
60 
setup(int actorId,bool runFlag,const Vector3 & from,const Vector3 & to,bool mustReach,bool * arrived)61 bool ActorWalk::setup(int actorId, bool runFlag, const Vector3 &from, const Vector3 &to, bool mustReach, bool *arrived) {
62 	Vector3 next;
63 
64 	*arrived = false;
65 
66 	int r = nextOnPath(actorId, from, to, next);
67 
68 	if (r == 0) {
69 		if (actorId != 0) {
70 			_current = from;
71 			_destination = to;
72 			stop(actorId, false, kAnimationModeCombatIdle, kAnimationModeIdle);
73 		} else {
74 			stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
75 		}
76 //		debug("actor id: %d, arrived: %d - false setup 01", actorId, (*arrived)? 1:0);
77 		return false;
78 	}
79 
80 	if (r == -1) {
81 		stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
82 		*arrived = true;
83 //		debug("actor id: %d, arrived: %d - false setup 02", actorId, (*arrived)? 1:0);
84 		return false;
85 	}
86 
87 	_nearActors.clear();
88 	_vm->_sceneObjects->setMoving(actorId + kSceneObjectOffsetActors, true);
89 	_vm->_actors[actorId]->setMoving(true);
90 
91 	if (_running) {
92 		runFlag = true;
93 	}
94 
95 	int animationMode;
96 	if (_vm->_actors[actorId]->inCombat()) {
97 		animationMode = runFlag ? kAnimationModeCombatRun : kAnimationModeCombatWalk;
98 	} else {
99 		animationMode = runFlag ? kAnimationModeRun : kAnimationModeWalk;
100 	}
101 
102 	_vm->_actors[actorId]->changeAnimationMode(animationMode);
103 
104 	_destination = to;
105 	_originalDestination = to;
106 	_current = from;
107 	_next = next;
108 
109 	if (next.x == _current.x && next.z == _current.z) {
110 		stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
111 		*arrived = true;
112 //		debug("actor id: %d, arrived: %d - false setup 03", actorId, (*arrived)? 1:0);
113 		return false;
114 	}
115 
116 	_facing = angle_1024(_current, next);
117 	_walking = true;
118 	_running = runFlag;
119 	_status = 2;
120 
121 //	debug("actor id: %d, arrived: %d - true setup 01", actorId, (*arrived)? 1:0);
122 	return true;
123 }
124 
tick(int actorId,float stepDistance,bool mustReachWalkDestination)125 bool ActorWalk::tick(int actorId, float stepDistance, bool mustReachWalkDestination) {
126 	bool walkboxFound;
127 
128 	if (_status == 5) {
129 		if (mustReachWalkDestination) {
130 			stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
131 			return true;
132 		}
133 
134 		if (actorId != 0 && _vm->_rnd.getRandomNumberRng(1, 15) != 1) { // why random?
135 			return false;
136 		}
137 		_status = 3;
138 	}
139 
140 	bool nearActorExists = addNearActors(actorId);
141 	if (_nearActors.size() > 0) {
142 		nearActorExists = true;
143 		if (_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, _destination.x, _destination.z, true, true)) {
144 			if (actorId > 0) {
145 				if (_vm->_actors[actorId]->mustReachWalkDestination()) {
146 					stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
147 					_nearActors.clear();
148 					return true;
149 				} else {
150 					Vector3 newDestination;
151 					findEmptyPositionAroundToOriginalDestination(actorId, newDestination);
152 					_destination = newDestination;
153 					return false;
154 				}
155 			} else {
156 				if (_vm->_playerActor->mustReachWalkDestination()) {
157 					_destination = _current;
158 				}
159 				stop(0, true, kAnimationModeCombatIdle, kAnimationModeIdle);
160 				_nearActors.clear();
161 				return true;
162 			}
163 		}
164 	}
165 	_status = 3;
166 
167 	if (stepDistance > distance(_current, _destination)) {
168 		stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
169 		_current = _destination;
170 		_current.y = _vm->_scene->_set->getAltitudeAtXZ(_current.x, _current.z, &walkboxFound);
171 		return true;
172 	}
173 
174 	float distanceToNext = distance(_current, _next);
175 	if (1.0f < distanceToNext) {
176 		_facing = angle_1024(_current, _next);
177 	}
178 
179 	bool nextIsCloseEnough = stepDistance > distanceToNext;
180 
181 	if (nextIsCloseEnough || nearActorExists || _status == 3) {
182 		if (nextIsCloseEnough) {
183 			_current = _next;
184 		}
185 		_status = 1;
186 		Vector3 next;
187 		obstaclesAddNearActors(actorId);
188 		int r = nextOnPath(actorId, _current, _destination, next);
189 		obstaclesRestore();
190 		if (r == 0) {
191 			stop(actorId, actorId == kActorMcCoy, kAnimationModeCombatIdle, kAnimationModeIdle);
192 			return false;
193 		}
194 		if (r != -1) {
195 			_next = next;
196 			_facing = angle_1024(_current, _next);
197 			_status = 2;
198 			int animationMode;
199 			if (_vm->_actors[actorId]->inCombat()) {
200 				animationMode = _running ? kAnimationModeCombatRun : kAnimationModeCombatWalk;
201 			} else {
202 				animationMode = _running ? kAnimationModeRun : kAnimationModeWalk;
203 			}
204 			_vm->_actors[actorId]->changeAnimationMode(animationMode);
205 			if (nextIsCloseEnough) {
206 				return false;
207 			}
208 		} else {
209 			stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle); // too close
210 			return true;
211 		}
212 	}
213 
214 #if !BLADERUNNER_ORIGINAL_BUGS
215 	// safety-guard / validator  check
216 	if (_facing >= 1024) {
217 		_facing = (_facing % 1024);
218 	} else if (_facing < 0) {
219 		_facing  = (-1) * _facing;
220 		_facing = (_facing % 1024);
221 		if (_facing > 0) {
222 			_facing  = 1024 - _facing; // this will always be in [1, 1023]
223 		}
224 	}
225 #endif
226 	_current.x += stepDistance * _vm->_sinTable1024->at(_facing);
227 	_current.z -= stepDistance * _vm->_cosTable1024->at(_facing);
228 	_current.y = _vm->_scene->_set->getAltitudeAtXZ(_current.x, _current.z, &walkboxFound);
229 
230 	return false;
231 }
232 
getCurrentPosition(int actorId,Vector3 * pos,int * facing) const233 void ActorWalk::getCurrentPosition(int actorId, Vector3 *pos, int *facing) const {
234 	*pos = _current;
235 	*facing = _facing;
236 }
237 
stop(int actorId,bool immediately,int combatAnimationMode,int animationMode)238 void ActorWalk::stop(int actorId, bool immediately, int combatAnimationMode, int animationMode) {
239 	_vm->_sceneObjects->setMoving(actorId + kSceneObjectOffsetActors, false);
240 	_vm->_actors[actorId]->setMoving(false);
241 
242 	if (_vm->_actors[actorId]->inCombat()) {
243 		_vm->_actors[actorId]->changeAnimationMode(combatAnimationMode, false);
244 	} else {
245 		_vm->_actors[actorId]->changeAnimationMode(animationMode, false);
246 	}
247 
248 	if (immediately) {
249 		_walking = false;
250 		_running = false;
251 		_status = 0;
252 	} else {
253 		_walking = true;
254 		_running = false;
255 		_status = 5;
256 	}
257 }
258 
run(int actorId)259 void ActorWalk::run(int actorId) {
260 	_running = true;
261 
262 	int animationMode = kAnimationModeRun;
263 	if (_vm->_actors[actorId]->inCombat()) {
264 		animationMode = kAnimationModeCombatRun;
265 	}
266 	_vm->_actors[actorId]->changeAnimationMode(animationMode, false);
267 }
268 
save(SaveFileWriteStream & f)269 void ActorWalk::save(SaveFileWriteStream &f) {
270 	f.writeInt(_walking);
271 	f.writeInt(_running);
272 	f.writeVector3(_destination);
273 	// _originalDestination is not saved
274 	f.writeVector3(_current);
275 	f.writeVector3(_next);
276 	f.writeInt(_facing);
277 
278 	assert(_nearActors.size() <= 20);
279 	for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) {
280 		f.writeInt(it->_key);
281 		f.writeBool(it->_value);
282 	}
283 	f.padBytes(8 * (20 - _nearActors.size()));
284 	f.writeInt(_nearActors.size());
285 
286 	f.writeInt(0); // _notUsed
287 	f.writeInt(_status);
288 }
289 
load(SaveFileReadStream & f)290 void ActorWalk::load(SaveFileReadStream &f) {
291 	_walking = f.readInt();
292 	_running = f.readInt();
293 	_destination = f.readVector3();
294 	// _originalDestination is not saved
295 	_current = f.readVector3();
296 	_next = f.readVector3();
297 	_facing = f.readInt();
298 
299 	int actorId[20];
300 	bool isNear[20];
301 
302 	for (int i = 0; i < 20; ++i) {
303 		actorId[i] = f.readInt();
304 		isNear[i] = f.readBool();
305 	}
306 
307 	int count = f.readInt();
308 	for (int i = 0; i < count; ++i) {
309 		_nearActors.setVal(actorId[i], isNear[i]);
310 	}
311 
312 	f.skip(4); // _notUsed
313 	_status = f.readInt();
314 }
315 
isXYZOccupied(float x,float y,float z,int actorId) const316 bool ActorWalk::isXYZOccupied(float x, float y, float z, int actorId) const {
317 	if (_vm->_scene->_set->findWalkbox(x, z) == -1) {
318 		return true;
319 	}
320 	if (_vm->_actors[actorId]->isImmuneToObstacles()) {
321 		return false;
322 	}
323 	return _vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, x, z, false, false);
324 }
325 
findEmptyPositionAround(int actorId,const Vector3 & destination,int dist,Vector3 & out) const326 bool ActorWalk::findEmptyPositionAround(int actorId, const Vector3 &destination, int dist, Vector3 &out) const {
327 	bool inWalkbox;
328 
329 	int facingToMinDistance = -1;
330 	float minDistance = -1.0f;
331 	float x = 0.0f;
332 	float z = 0.0f;
333 
334 	out.x = 0.0f;
335 	out.y = 0.0f;
336 	out.z = 0.0f;
337 
338 	for (int facing = 0; facing < 1024; facing += 128) {
339 		x = destination.x + _vm->_sinTable1024->at(facing) * dist;
340 		z = destination.z - _vm->_cosTable1024->at(facing) * dist;
341 		float distanceBetweenActorAndDestination = distance(x, z, _vm->_actors[actorId]->getX(), _vm->_actors[actorId]->getZ());
342 
343 		if (minDistance == -1.0f || minDistance > distanceBetweenActorAndDestination) {
344 			minDistance = distanceBetweenActorAndDestination;
345 			facingToMinDistance = facing;
346 		}
347 	}
348 
349 	int facingLeft = facingToMinDistance;
350 	int facingRight = facingToMinDistance;
351 	int facing = -1024;
352 	while (facing < 0) {
353 		x = destination.x + _vm->_sinTable1024->at(facingRight) * dist;
354 		z = destination.z - _vm->_cosTable1024->at(facingRight) * dist;
355 
356 		if (!_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, x, z, true, true) && _vm->_scene->_set->findWalkbox(x, z) >= 0) {
357 			break;
358 		}
359 
360 		x = destination.x + _vm->_sinTable1024->at(facingLeft) * dist;
361 		z = destination.z - _vm->_cosTable1024->at(facingLeft) * dist;
362 
363 		if (!_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, x, z, true, true) && _vm->_scene->_set->findWalkbox(x, z) >= 0) {
364 			break;
365 		}
366 
367 		facingRight -= 64;
368 		if (facingRight < 0) {
369 			facingRight += 1024;
370 		}
371 		facingLeft += 64;
372 		if (facingLeft >= 1024) {
373 			facingLeft -= 1024;
374 		}
375 		facing += 64;
376 	}
377 
378 	float y = _vm->_scene->_set->getAltitudeAtXZ(x, z, &inWalkbox);
379 	if (inWalkbox) {
380 		out.x = x;
381 		out.y = y;
382 		out.z = z;
383 		return true;
384 	}
385 	return false;
386 }
387 
findEmptyPositionAroundToOriginalDestination(int actorId,Vector3 & out) const388 bool ActorWalk::findEmptyPositionAroundToOriginalDestination(int actorId, Vector3 &out) const {
389 	return findEmptyPositionAround(actorId, _originalDestination, 30, out);
390 }
391 
addNearActors(int skipActorId)392 bool ActorWalk::addNearActors(int skipActorId) {
393 	bool added = false;
394 	int setId = _vm->_scene->getSetId();
395 	for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); ++i) {
396 		assert(_vm->_actors[i] != nullptr);
397 
398 		if (_vm->_actors[skipActorId] != nullptr
399 		 && _vm->_actors[i]->getSetId() == setId
400 		 && i != skipActorId
401 		) {
402 			if (_nearActors.contains(i)) {
403 				_nearActors.setVal(i, false);
404 			} else if (_vm->_actors[skipActorId]->distanceFromActor(i) <= 48.0f) {
405 				_nearActors.setVal(i, true);
406 				added = true;
407 			}
408 		}
409 	}
410 	return added;
411 }
412 
obstaclesAddNearActors(int actorId) const413 void ActorWalk::obstaclesAddNearActors(int actorId) const {
414 	Vector3 position = _vm->_actors[actorId]->getPosition();
415 	for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) {
416 		Actor *otherActor = _vm->_actors[it->_key];
417 		assert(otherActor != nullptr);
418 
419 		if ( otherActor->isRetired()) {
420 			continue;
421 		}
422 		Vector3 otherPosition = otherActor->getPosition();
423 		float x0 = otherPosition.x - 12.0f;
424 		float z0 = otherPosition.z - 12.0f;
425 		float x1 = otherPosition.x + 12.0f;
426 		float z1 = otherPosition.z + 12.0f;
427 		if (position.x < (x0 - 12.0f) || position.z < (z0 - 12.0f) || position.x > (x1 + 12.0f) || position.z > (z1 + 12.0f)) {
428 			_vm->_obstacles->add(x0, z0, x1, z1);
429 		}
430 	}
431 }
432 
obstaclesRestore() const433 void ActorWalk::obstaclesRestore() const {
434 	_vm->_obstacles->restore();
435 }
436 
nextOnPath(int actorId,const Vector3 & from,const Vector3 & to,Vector3 & next) const437 int ActorWalk::nextOnPath(int actorId, const Vector3 &from, const Vector3 &to, Vector3 &next) const {
438 	next = from;
439 
440 	if (distance(from, to) < 6.0) {
441 //		debug("Id: %d Distance: %f::Result -1", actorId, distance(from, to));
442 		return -1;
443 	}
444 
445 	if (_vm->_actors[actorId]->isImmuneToObstacles()) {
446 		next = to;
447 		return 1;
448 	}
449 	if (_vm->_scene->_set->findWalkbox(to.x, to.z) == -1) {
450 //		debug("Id: %d No walkbox::Result 0", actorId);
451 		return 0;
452 	}
453 	if (_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, to.x, to.z, false, false)) {
454 //		debug("Actor Id: %d existsOnXZ::Result 0", actorId);
455 		return 0;
456 	}
457 	Vector3 next1;
458 	if (_vm->_obstacles->findNextWaypoint(from, to, &next1)) {
459 		next = next1;
460 		return 1;
461 	}
462 //	debug("Id: %d DEFAULTED::Result 0", actorId);
463 	return 0;
464 }
465 
466 } // End of namespace BladeRunner
467