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_combat.h"
24
25 #include "bladerunner/actor.h"
26 #include "bladerunner/audio_speech.h"
27 #include "bladerunner/bladerunner.h"
28 #include "bladerunner/combat.h"
29 #include "bladerunner/game_constants.h"
30 #include "bladerunner/game_info.h"
31 #include "bladerunner/movement_track.h"
32 #include "bladerunner/savefile.h"
33 #include "bladerunner/scene.h"
34 #include "bladerunner/scene_objects.h"
35 #include "bladerunner/script/ai_script.h"
36 #include "bladerunner/set.h"
37 #include "bladerunner/settings.h"
38
39 namespace BladeRunner {
40
ActorCombat(BladeRunnerEngine * vm)41 ActorCombat::ActorCombat(BladeRunnerEngine *vm) {
42 _vm = vm;
43 reset();
44 }
45
~ActorCombat()46 ActorCombat::~ActorCombat() {
47 }
48
setup()49 void ActorCombat::setup() {
50 reset();
51 }
52
combatOn(int actorId,int initialState,bool rangedAttackFlag,int enemyId,int waypointType,int fleeRatio,int coverRatio,int attackRatio,int damage,int range,bool unstoppable)53 void ActorCombat::combatOn(int actorId, int initialState, bool rangedAttackFlag, int enemyId, int waypointType, int fleeRatio, int coverRatio, int attackRatio, int damage, int range, bool unstoppable) {
54 _actorId = actorId;
55 _state = initialState;
56 _rangedAttack = rangedAttackFlag;
57 _enemyId = enemyId;
58 _waypointType = waypointType;
59 _damage = damage;
60 _fleeRatioConst = fleeRatio;
61 _coverRatioConst = coverRatio;
62 _attackRatioConst = attackRatio;
63 _fleeRatio = fleeRatio;
64 _coverRatio = coverRatio;
65 _attackRatio = attackRatio;
66 _active = true;
67 if (_rangedAttack) {
68 _range = range;
69 } else {
70 _range = 300;
71 }
72 _unstoppable = unstoppable;
73
74 Actor *actor = _vm->_actors[_actorId];
75
76 _actorPosition = actor->getXYZ();
77 _enemyPosition = _vm->_actors[_enemyId]->getXYZ();
78
79 actor->_movementTrack->flush();
80 actor->stopWalking(false);
81
82 if (_enemyId == kActorMcCoy) {
83 actor->setTarget(true);
84 }
85
86 _actorHp = actor->getCurrentHP();
87
88 _coversWaypointCount = 0;
89 for (int i = 0; i < (int)_vm->_gameInfo->getCoverWaypointCount(); ++i) {
90 if (_vm->_combat->_coverWaypoints[i].type == waypointType && _vm->_combat->_coverWaypoints[i].setId == actor->getSetId()) {
91 ++_coversWaypointCount;
92 }
93 }
94 if (_coversWaypointCount == 0) {
95 _coverRatioConst = 0;
96 _coverRatio = 0;
97 }
98
99 _fleeWaypointsCount = 0;
100 for (int i = 0; i < (int)_vm->_gameInfo->getFleeWaypointCount(); ++i) {
101 if (_vm->_combat->_fleeWaypoints[i].type == waypointType && _vm->_combat->_fleeWaypoints[i].setId == actor->getSetId()) {
102 ++_fleeWaypointsCount;
103 }
104 }
105 if (_fleeWaypointsCount == 0) {
106 _fleeRatioConst = 0;
107 _fleeRatio = 0;
108 }
109 }
110
combatOff()111 void ActorCombat::combatOff() {
112 _active = false;
113 reset();
114 }
115
tick()116 void ActorCombat::tick() {
117 static int processingCounter = 0;
118
119 if (!_active || processingCounter > 0) {
120 return;
121 }
122
123 Actor *actor = _vm->_actors[_actorId];
124 Actor *enemy = _vm->_actors[_enemyId];
125
126 if (actor->getSetId() != enemy->getSetId()) {
127 actor->combatModeOff();
128 return;
129 }
130
131 ++processingCounter;
132
133 _actorPosition = actor->getXYZ();
134 _enemyPosition = enemy->getXYZ();
135
136 if (_attackRatioConst >= 0) {
137 _attackRatio = _attackRatioConst;
138 } else {
139 _attackRatio = calculateAttackRatio();
140 }
141
142 if (_vm->_combat->findCoverWaypoint(_waypointType, _actorId, _enemyId) != -1) {
143 if (_coverRatioConst >= 0) {
144 _coverRatio = _coverRatioConst;
145 } else {
146 _coverRatio = calculateCoverRatio();
147 }
148 } else {
149 _coverRatio = 0;
150 }
151
152 if (_fleeRatioConst >= 0) {
153 _fleeRatio = _fleeRatioConst;
154 } else {
155 _fleeRatio = calculateFleeRatio();
156 }
157
158 float dist = actor->distanceFromActor(_enemyId);
159 int oldState = _state;
160
161 if (_attackRatio < _fleeRatio || _attackRatio < _coverRatio) {
162 if (_coverRatio >= _fleeRatio && _coverRatio >= _attackRatio) {
163 _state = kActorCombatStateCover;
164 } else {
165 _state = kActorCombatStateFlee;
166 }
167 } else {
168 if (_rangedAttack) {
169 if (dist > _range) {
170 _state = kActorCombatStateApproachRangedAttack;
171 } else {
172 if (actor->isObstacleBetween(_enemyPosition)) {
173 _state = kActorCombatStateUncover;
174 } else {
175 _state = kActorCombatStateRangedAttack;
176 }
177 }
178 } else {
179 if (dist > 36.0f) {
180 _state = kActorCombatStateApproachCloseAttack;
181 } else {
182 _state = kActorCombatStateCloseAttack;
183 }
184 }
185 }
186
187 if (enemy->isRetired()) {
188 _state = kActorCombatStateIdle;
189 }
190
191 if (actor->getAnimationMode() == kAnimationModeHit || actor->getAnimationMode() == kAnimationModeCombatHit) {
192 _state = kActorCombatStateIdle;
193 } else {
194 if (_state != oldState) {
195 actor->stopWalking(false);
196 }
197 }
198 switch (_state) {
199 case kActorCombatStateCover:
200 cover();
201 break;
202 case kActorCombatStateApproachCloseAttack:
203 approachToCloseAttack();
204 break;
205 case kActorCombatStateUncover:
206 uncover();
207 break;
208 case kActorCombatStateAim:
209 aim();
210 break;
211 case kActorCombatStateRangedAttack:
212 rangedAttack();
213 break;
214 case kActorCombatStateCloseAttack:
215 closeAttack();
216 break;
217 case kActorCombatStateFlee:
218 flee();
219 break;
220 case kActorCombatStateApproachRangedAttack:
221 approachToRangedAttack();
222 break;
223 default:
224 break;
225 }
226 --processingCounter;
227 }
228
hitAttempt()229 void ActorCombat::hitAttempt() {
230 Actor *actor = _vm->_actors[_actorId];
231 Actor *enemy = _vm->_actors[_enemyId];
232
233 if (_enemyId == kActorMcCoy && !_vm->playerHasControl() && !_unstoppable) {
234 return;
235 }
236
237 if (actor->isRetired()) {
238 return;
239 }
240
241 int attackCoefficient = 0;
242 if (_rangedAttack) {
243 attackCoefficient = _rangedAttack ? getCoefficientRangedAttack() : 0;
244 } else {
245 attackCoefficient = getCoefficientCloseAttack();
246 }
247
248 if (attackCoefficient == 0) {
249 return;
250 }
251
252 int random = _vm->_rnd.getRandomNumberRng(1, 100);
253
254 if (random <= attackCoefficient) {
255 if (enemy->isWalking()) {
256 enemy->stopWalking(true);
257 }
258
259 int sentenceId = _vm->_rnd.getRandomNumberRng(0, 1) ? 9000 : 9005;
260 if (enemy->inCombat()) {
261 enemy->changeAnimationMode(kAnimationModeCombatHit, false);
262 } else {
263 enemy->changeAnimationMode(kAnimationModeHit, false);
264 }
265
266 int damage = 0;
267 if (_rangedAttack) {
268 damage = getDamageRangedAttack(random, attackCoefficient);
269 } else {
270 damage = getDamageCloseAttack(random, attackCoefficient);
271 }
272
273 int enemyHp = MAX(enemy->getCurrentHP() - damage, 0);
274 enemy->setCurrentHP(enemyHp);
275
276 if (enemyHp <= 0) {
277 if (!enemy->isRetired()) {
278 #if BLADERUNNER_ORIGINAL_BUGS
279 #else
280 // make sure the dead enemy won't pick a pending movement track and re-spawn
281 enemy->_movementTrack->flush();
282 #endif
283 if (enemy->inCombat()) {
284 enemy->changeAnimationMode(kAnimationModeCombatDie, false);
285 } else {
286 enemy->changeAnimationMode(kAnimationModeDie, false);
287 }
288 sentenceId = 9020;
289 }
290 enemy->retire(true, 6, 3, _actorId);
291 }
292
293 if (_enemyId == kActorMcCoy) {
294 sentenceId += 900;
295 }
296
297 _vm->_audioSpeech->playSpeechLine(_enemyId, sentenceId, 75, enemy->soundPan(), 99);
298 }
299 }
300
save(SaveFileWriteStream & f)301 void ActorCombat::save(SaveFileWriteStream &f) {
302 f.writeInt(_actorId);
303 f.writeBool(_active);
304 f.writeInt(_state);
305 f.writeBool(_rangedAttack);
306 f.writeInt(_enemyId);
307 f.writeInt(_waypointType);
308 f.writeInt(_damage);
309 f.writeInt(_fleeRatio);
310 f.writeInt(_coverRatio);
311 f.writeInt(_attackRatio);
312 f.writeInt(_fleeRatioConst);
313 f.writeInt(_coverRatioConst);
314 f.writeInt(_attackRatioConst);
315 f.writeInt(_range);
316 f.writeInt(_unstoppable);
317 f.writeInt(_actorHp);
318 f.writeInt(_fleeingTowards);
319 f.writeVector3(_actorPosition);
320 f.writeVector3(_enemyPosition);
321 f.writeInt(_coversWaypointCount);
322 f.writeInt(_fleeWaypointsCount);
323 }
324
load(SaveFileReadStream & f)325 void ActorCombat::load(SaveFileReadStream &f) {
326 _actorId = f.readInt();
327 _active = f.readBool();
328 _state = f.readInt();
329 _rangedAttack = f.readBool();
330 _enemyId = f.readInt();
331 _waypointType = f.readInt();
332 _damage = f.readInt();
333 _fleeRatio = f.readInt();
334 _coverRatio = f.readInt();
335 _attackRatio = f.readInt();
336 _fleeRatioConst = f.readInt();
337 _coverRatioConst = f.readInt();
338 _attackRatioConst = f.readInt();
339 _range = f.readInt();
340 _unstoppable = f.readInt();
341 _actorHp = f.readInt();
342 _fleeingTowards = f.readInt();
343 _actorPosition = f.readVector3();
344 _enemyPosition = f.readVector3();
345 _coversWaypointCount = f.readInt();
346 _fleeWaypointsCount = f.readInt();
347 }
348
reset()349 void ActorCombat::reset() {
350 _active = false;
351 _actorId = -1;
352 _state = -1;
353 _rangedAttack = false;
354 _enemyId = -1;
355 _waypointType = -1;
356 _damage = 0;
357 _fleeRatio = -1;
358 _coverRatio = -1;
359 _attackRatio = -1;
360 _fleeRatioConst = -1;
361 _coverRatioConst = -1;
362 _attackRatioConst = -1;
363 _actorHp = 0;
364 _range = 300;
365 _unstoppable = false;
366 _actorPosition = Vector3(0.0f, 0.0f, 0.0f);
367 _enemyPosition = Vector3(0.0f, 0.0f, 0.0f);
368 _coversWaypointCount = 0;
369 _fleeWaypointsCount = 0;
370 _fleeingTowards = -1;
371 }
372
cover()373 void ActorCombat::cover() {
374 Actor *actor = _vm->_actors[_actorId];
375
376 if (actor->isWalking()) {
377 return;
378 }
379
380 if (actor->isObstacleBetween(_enemyPosition)) {
381 faceEnemy();
382 return;
383 }
384
385 int coverWaypointId = _vm->_combat->findCoverWaypoint(_waypointType, _actorId, _enemyId);
386 if (coverWaypointId == -1) {
387 _state = kActorCombatStateIdle;
388 } else {
389 actor->asyncWalkToXYZ(_vm->_combat->_coverWaypoints[coverWaypointId].position, 0, true, 0);
390 }
391 }
392
approachToCloseAttack()393 void ActorCombat::approachToCloseAttack() {
394 Actor *actor = _vm->_actors[_actorId];
395 Actor *enemy = _vm->_actors[_enemyId];
396
397 float dist = actor->distanceFromActor(_enemyId);
398 if (dist > 36.0f) {
399 if (!actor->isWalking() || enemy->isWalking()) {
400 Vector3 target;
401 if (findClosestPositionToEnemy(target)) {
402 actor->asyncWalkToXYZ(target, 0, dist >= 240.0f, 0);
403 } else {
404 _state = kActorCombatStateCover;
405 }
406 }
407 } else {
408 if (actor->isWalking()) {
409 actor->stopWalking(false);
410 }
411 faceEnemy();
412 _state = kActorCombatStateCloseAttack;
413 }
414 }
415
approachToRangedAttack()416 void ActorCombat::approachToRangedAttack() {
417 Actor *actor = _vm->_actors[_actorId];
418 Actor *enemy = _vm->_actors[_enemyId];
419
420 float dist = actor->distanceFromActor(_enemyId);
421 if (dist > _range) {
422 if (!actor->isWalking() || enemy->isWalking()) {
423 Vector3 target;
424 if (findClosestPositionToEnemy(target)) {
425 actor->asyncWalkToXYZ(target, 0, dist >= 240.0f, 0);
426 } else {
427 _state = kActorCombatStateCover;
428 }
429 }
430 } else {
431 if (actor->isWalking()) {
432 actor->stopWalking(false);
433 }
434 faceEnemy();
435 _state = kActorCombatStateRangedAttack;
436 }
437 }
438
uncover()439 void ActorCombat::uncover() {
440 Actor *actor = _vm->_actors[_actorId];
441 Actor *enemy = _vm->_actors[_enemyId];
442
443 if (actor->isObstacleBetween(_enemyPosition)) {
444 actor->asyncWalkToXYZ(enemy->getXYZ(), 16, false, 0);
445 } else {
446 if (actor->isWalking()) {
447 actor->stopWalking(false);
448 }
449 faceEnemy();
450 }
451 }
452
aim()453 void ActorCombat::aim() {
454 Actor *actor = _vm->_actors[_actorId];
455
456 if (actor->isObstacleBetween(_enemyPosition)) {
457 if (actor->getAnimationMode() != kAnimationModeCombatIdle) {
458 actor->changeAnimationMode(kAnimationModeCombatIdle, false);
459 }
460 } else {
461 faceEnemy();
462 if (actor->getAnimationMode() != kAnimationModeCombatAim) {
463 actor->changeAnimationMode(kAnimationModeCombatAim, false);
464 }
465 }
466 }
467
rangedAttack()468 void ActorCombat::rangedAttack() {
469 Actor *actor = _vm->_actors[_actorId];
470
471 if (actor->isObstacleBetween(_enemyPosition) || (actor->distanceFromActor(_enemyId) > _range)) {
472 _state = kActorCombatStateApproachRangedAttack;
473 } else {
474 faceEnemy();
475 if (actor->getAnimationMode() != kAnimationModeCombatAttack) {
476 if (_enemyId != kActorMcCoy || _vm->playerHasControl() || _unstoppable) {
477 actor->changeAnimationMode(kAnimationModeCombatAttack, false);
478 }
479 }
480 }
481 }
482
closeAttack()483 void ActorCombat::closeAttack() {
484 Actor *actor = _vm->_actors[_actorId];
485
486 if (actor->isObstacleBetween(_enemyPosition) || (actor->distanceFromActor(_enemyId) > 36.0f)) {
487 _state = kActorCombatStateApproachCloseAttack;
488 } else {
489 faceEnemy();
490 if (actor->getAnimationMode() != kAnimationModeCombatAttack) {
491 if (_enemyId != kActorMcCoy || _vm->playerHasControl() || _unstoppable) {
492 actor->changeAnimationMode(kAnimationModeCombatAttack, false);
493 }
494 }
495 }
496 }
497
flee()498 void ActorCombat::flee() {
499 Actor *actor = _vm->_actors[_actorId];
500
501 if (_fleeingTowards != -1 && actor->isWalking()) {
502 Vector3 fleeWaypointPosition = _vm->_combat->_fleeWaypoints[_fleeingTowards].position;
503 if (distance(_actorPosition, fleeWaypointPosition) <= 12.0f) {
504 _vm->_aiScripts->fledCombat(_actorId/*, _enemyId*/);
505 actor->setSetId(kSetFreeSlotG);
506 actor->combatModeOff();
507 _fleeingTowards = -1;
508 }
509 } else {
510 int fleeWaypointId = _vm->_combat->findFleeWaypoint(actor->getSetId(), _enemyId, _actorPosition);
511 if (fleeWaypointId == -1) {
512 _state = kActorCombatStateIdle;
513 } else {
514 Vector3 fleeWaypointPosition = _vm->_combat->_fleeWaypoints[fleeWaypointId].position;
515 actor->asyncWalkToXYZ(fleeWaypointPosition, 0, true, 0);
516 _fleeingTowards = fleeWaypointId;
517 }
518 }
519 }
520
faceEnemy()521 void ActorCombat::faceEnemy() {
522 _vm->_actors[_actorId]->setFacing(angle_1024(_actorPosition.x, _actorPosition.z, _enemyPosition.x, _enemyPosition.z), false);
523 }
524
getCoefficientCloseAttack() const525 int ActorCombat::getCoefficientCloseAttack() const{
526 Actor *actor = _vm->_actors[_actorId];
527 Actor *enemy = _vm->_actors[_enemyId];
528
529 float distance = actor->distanceFromActor(_enemyId);
530
531 if (distance > 36.0f) {
532 return 0;
533 }
534
535 int aggressiveness = 0;
536 if (enemy->isRunning()) {
537 aggressiveness = 11;
538 } else if (enemy->isMoving()) {
539 aggressiveness = 22;
540 } else {
541 aggressiveness = 33;
542 }
543
544 aggressiveness += actor->getCombatAggressiveness() / 3;
545
546 int angle = abs(actor->angleTo(_enemyPosition));
547
548 if (angle > 128) {
549 return false;
550 }
551
552 return aggressiveness + (abs(angle - 128) / 3.7f);
553 }
554
getCoefficientRangedAttack() const555 int ActorCombat::getCoefficientRangedAttack() const {
556 Actor *actor = _vm->_actors[_actorId];
557 Actor *enemy = _vm->_actors[_enemyId];
558
559 if (actor->isObstacleBetween(_enemyPosition)) {
560 return 0;
561 }
562
563 int distance = MIN(actor->distanceFromActor(_enemyId), 900.0f);
564
565 int aggressiveness = 0;
566 if (enemy->isRunning()) {
567 aggressiveness = 10;
568 } else if (enemy->isMoving()) {
569 aggressiveness = 20;
570 } else {
571 aggressiveness = 30;
572 }
573
574 aggressiveness += actor->getCombatAggressiveness() / 5;
575 return aggressiveness + abs((distance / 30) - 30) + actor->getIntelligence() / 5;
576 }
577
getDamageCloseAttack(int min,int max) const578 int ActorCombat::getDamageCloseAttack(int min, int max) const {
579 if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyEasy) {
580 return _damage / 2;
581 }
582 if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyHard) {
583 return _damage;
584 }
585 return ((MIN(max - min, 30) * 100.0f / 60.0f) + 50) * _damage / 100;
586 }
587
getDamageRangedAttack(int min,int max) const588 int ActorCombat::getDamageRangedAttack(int min, int max) const {
589 if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyEasy) {
590 return _damage / 2;
591 }
592 if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyHard) {
593 return _damage;
594 }
595 return ((MIN(max - min, 30) * 100.0f / 60.0f) + 50) * _damage / 100;
596 }
597
calculateAttackRatio() const598 int ActorCombat::calculateAttackRatio() const {
599 Actor *actor = _vm->_actors[_actorId];
600 Actor *enemy = _vm->_actors[_enemyId];
601
602 int aggressivenessFactor = actor->getCombatAggressiveness();
603 int actorHpFactor = actor->getCurrentHP();
604 int enemyHpFactor = 100 - enemy->getCurrentHP();
605 int combatFactor = enemy->inCombat() ? 0 : 100;
606 int angleFactor = (100 * abs(enemy->angleTo(_actorPosition))) / 512;
607 int distanceFactor = 2 * (50 - MIN(actor->distanceFromActor(_enemyId) / 12.0f, 50.0f));
608
609 if (_rangedAttack) {
610 return
611 angleFactor * 0.25f +
612 combatFactor * 0.05f +
613 enemyHpFactor * 0.20f +
614 actorHpFactor * 0.10f +
615 aggressivenessFactor * 0.40f;
616 } else {
617 return
618 distanceFactor * 0.20f +
619 angleFactor * 0.10f +
620 combatFactor * 0.10f +
621 enemyHpFactor * 0.15f +
622 actorHpFactor * 0.15f +
623 aggressivenessFactor * 0.30f;
624 }
625 }
626
calculateCoverRatio() const627 int ActorCombat::calculateCoverRatio() const {
628 if (_coversWaypointCount == 0) {
629 return 0;
630 }
631
632 Actor *actor = _vm->_actors[_actorId];
633 Actor *enemy = _vm->_actors[_enemyId];
634
635 int angleFactor = 100 - (100 * abs(enemy->angleTo(_actorPosition))) / 512;
636 int actorHpFactor = 100 - actor->getCurrentHP();
637 int enemyHpFactor = enemy->getCurrentHP();
638 int aggressivenessFactor = 100 - actor->getCombatAggressiveness();
639 int distanceFactor = 2 * MIN(actor->distanceFromActor(_enemyId) / 12.0f, 50.0f);
640
641 if (_rangedAttack) {
642 return
643 angleFactor * 0.40f +
644 enemyHpFactor * 0.05f +
645 actorHpFactor * 0.15f +
646 aggressivenessFactor * 0.50f;
647 } else {
648 return
649 distanceFactor * 0.25f +
650 angleFactor * 0.20f +
651 enemyHpFactor * 0.05f +
652 actorHpFactor * 0.10f +
653 aggressivenessFactor * 0.50f;
654 }
655 }
656
calculateFleeRatio() const657 int ActorCombat::calculateFleeRatio() const {
658 if (_fleeWaypointsCount == 0) {
659 return 0;
660 }
661
662 Actor *actor = _vm->_actors[_actorId];
663 Actor *enemy = _vm->_actors[_enemyId];
664
665 int aggressivenessFactor = 100 - actor->getCombatAggressiveness();
666 int actorHpFactor = 100 - actor->getCurrentHP();
667 int combatFactor = enemy->inCombat() ? 100 : 0;
668
669 return
670 combatFactor * 0.2f +
671 actorHpFactor * 0.4f +
672 aggressivenessFactor * 0.4f;
673 }
674
findClosestPositionToEnemy(Vector3 & output) const675 bool ActorCombat::findClosestPositionToEnemy(Vector3 &output) const {
676 output = Vector3();
677
678 Vector3 offsets[] = {
679 Vector3( 0.0f, 0.0f, -28.0f),
680 Vector3( 28.0f, 0.0f, 0.0f),
681 Vector3( 0.0f, 0.0f, 28.0f),
682 Vector3(-28.0f, 0.0f, 0.0f)
683 };
684
685 float min = -1.0f;
686
687 for (int i = 0; i < 4; ++i) {
688 Vector3 test = _enemyPosition + offsets[i];
689 float dist = distance(_actorPosition, test);
690 if ( min == -1.0f || dist < min) {
691 if (!_vm->_sceneObjects->existsOnXZ(_actorId + kSceneObjectOffsetActors, test.x, test.z, true, true) && _vm->_scene->_set->findWalkbox(test.x, test.z) >= 0) {
692 output = test;
693 min = dist;
694 }
695 }
696 }
697
698 return min >= 0.0f;
699 }
700
701 } // End of namespace BladeRunner
702