mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 14:50:17 +00:00
701 lines
18 KiB
C++
701 lines
18 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "bladerunner/actor_combat.h"
|
|
|
|
#include "bladerunner/actor.h"
|
|
#include "bladerunner/audio_speech.h"
|
|
#include "bladerunner/bladerunner.h"
|
|
#include "bladerunner/combat.h"
|
|
#include "bladerunner/game_constants.h"
|
|
#include "bladerunner/game_info.h"
|
|
#include "bladerunner/movement_track.h"
|
|
#include "bladerunner/savefile.h"
|
|
#include "bladerunner/scene.h"
|
|
#include "bladerunner/scene_objects.h"
|
|
#include "bladerunner/script/ai_script.h"
|
|
#include "bladerunner/set.h"
|
|
#include "bladerunner/settings.h"
|
|
|
|
namespace BladeRunner {
|
|
|
|
ActorCombat::ActorCombat(BladeRunnerEngine *vm) {
|
|
_vm = vm;
|
|
reset();
|
|
}
|
|
|
|
ActorCombat::~ActorCombat() {
|
|
}
|
|
|
|
void ActorCombat::setup() {
|
|
reset();
|
|
}
|
|
|
|
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) {
|
|
_actorId = actorId;
|
|
_state = initialState;
|
|
_rangedAttack = rangedAttackFlag;
|
|
_enemyId = enemyId;
|
|
_waypointType = waypointType;
|
|
_damage = damage;
|
|
_fleeRatioConst = fleeRatio;
|
|
_coverRatioConst = coverRatio;
|
|
_attackRatioConst = attackRatio;
|
|
_fleeRatio = fleeRatio;
|
|
_coverRatio = coverRatio;
|
|
_attackRatio = attackRatio;
|
|
_active = true;
|
|
if (_rangedAttack) {
|
|
_range = range;
|
|
} else {
|
|
_range = 300;
|
|
}
|
|
_unstoppable = unstoppable;
|
|
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
|
|
_actorPosition = actor->getXYZ();
|
|
_enemyPosition = _vm->_actors[_enemyId]->getXYZ();
|
|
|
|
actor->_movementTrack->flush();
|
|
actor->stopWalking(false);
|
|
|
|
if (_enemyId == kActorMcCoy) {
|
|
actor->setTarget(true);
|
|
}
|
|
|
|
_actorHp = actor->getCurrentHP();
|
|
|
|
_coversWaypointCount = 0;
|
|
for (int i = 0; i < (int)_vm->_gameInfo->getCoverWaypointCount(); ++i) {
|
|
if (_vm->_combat->_coverWaypoints[i].type == waypointType && _vm->_combat->_coverWaypoints[i].setId == actor->getSetId()) {
|
|
++_coversWaypointCount;
|
|
}
|
|
}
|
|
if (_coversWaypointCount == 0) {
|
|
_coverRatioConst = 0;
|
|
_coverRatio = 0;
|
|
}
|
|
|
|
_fleeWaypointsCount = 0;
|
|
for (int i = 0; i < (int)_vm->_gameInfo->getFleeWaypointCount(); ++i) {
|
|
if (_vm->_combat->_fleeWaypoints[i].type == waypointType && _vm->_combat->_fleeWaypoints[i].setId == actor->getSetId()) {
|
|
++_fleeWaypointsCount;
|
|
}
|
|
}
|
|
if (_fleeWaypointsCount == 0) {
|
|
_fleeRatioConst = 0;
|
|
_fleeRatio = 0;
|
|
}
|
|
}
|
|
|
|
void ActorCombat::combatOff() {
|
|
_active = false;
|
|
reset();
|
|
}
|
|
|
|
void ActorCombat::tick() {
|
|
static int processingCounter = 0;
|
|
|
|
if (!_active || processingCounter > 0) {
|
|
return;
|
|
}
|
|
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
if (actor->getSetId() != enemy->getSetId()) {
|
|
actor->combatModeOff();
|
|
return;
|
|
}
|
|
|
|
++processingCounter;
|
|
|
|
_actorPosition = actor->getXYZ();
|
|
_enemyPosition = enemy->getXYZ();
|
|
|
|
if (_attackRatioConst >= 0) {
|
|
_attackRatio = _attackRatioConst;
|
|
} else {
|
|
_attackRatio = calculateAttackRatio();
|
|
}
|
|
|
|
if (_vm->_combat->findCoverWaypoint(_waypointType, _actorId, _enemyId) != -1) {
|
|
if (_coverRatioConst >= 0) {
|
|
_coverRatio = _coverRatioConst;
|
|
} else {
|
|
_coverRatio = calculateCoverRatio();
|
|
}
|
|
} else {
|
|
_coverRatio = 0;
|
|
}
|
|
|
|
if (_fleeRatioConst >= 0) {
|
|
_fleeRatio = _fleeRatioConst;
|
|
} else {
|
|
_fleeRatio = calculateFleeRatio();
|
|
}
|
|
|
|
float dist = actor->distanceFromActor(_enemyId);
|
|
int oldState = _state;
|
|
|
|
if (_attackRatio < _fleeRatio || _attackRatio < _coverRatio) {
|
|
if (_coverRatio >= _fleeRatio && _coverRatio >= _attackRatio) {
|
|
_state = kActorCombatStateCover;
|
|
} else {
|
|
_state = kActorCombatStateFlee;
|
|
}
|
|
} else {
|
|
if (_rangedAttack) {
|
|
if (dist > _range) {
|
|
_state = kActorCombatStateApproachRangedAttack;
|
|
} else {
|
|
if (actor->isObstacleBetween(_enemyPosition)) {
|
|
_state = kActorCombatStateUncover;
|
|
} else {
|
|
_state = kActorCombatStateRangedAttack;
|
|
}
|
|
}
|
|
} else {
|
|
if (dist > 36.0f) {
|
|
_state = kActorCombatStateApproachCloseAttack;
|
|
} else {
|
|
_state = kActorCombatStateCloseAttack;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enemy->isRetired()) {
|
|
_state = kActorCombatStateIdle;
|
|
}
|
|
|
|
if (actor->getAnimationMode() == kAnimationModeHit || actor->getAnimationMode() == kAnimationModeCombatHit) {
|
|
_state = kActorCombatStateIdle;
|
|
} else {
|
|
if (_state != oldState) {
|
|
actor->stopWalking(false);
|
|
}
|
|
}
|
|
switch (_state) {
|
|
case kActorCombatStateCover:
|
|
cover();
|
|
break;
|
|
case kActorCombatStateApproachCloseAttack:
|
|
approachToCloseAttack();
|
|
break;
|
|
case kActorCombatStateUncover:
|
|
uncover();
|
|
break;
|
|
case kActorCombatStateAim:
|
|
aim();
|
|
break;
|
|
case kActorCombatStateRangedAttack:
|
|
rangedAttack();
|
|
break;
|
|
case kActorCombatStateCloseAttack:
|
|
closeAttack();
|
|
break;
|
|
case kActorCombatStateFlee:
|
|
flee();
|
|
break;
|
|
case kActorCombatStateApproachRangedAttack:
|
|
approachToRangedAttack();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
--processingCounter;
|
|
}
|
|
|
|
void ActorCombat::hitAttempt() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
if (_enemyId == kActorMcCoy && !_vm->playerHasControl() && !_unstoppable) {
|
|
return;
|
|
}
|
|
|
|
if (actor->isRetired()) {
|
|
return;
|
|
}
|
|
|
|
int attackCoefficient = 0;
|
|
if (_rangedAttack) {
|
|
attackCoefficient = _rangedAttack ? getCoefficientRangedAttack() : 0;
|
|
} else {
|
|
attackCoefficient = getCoefficientCloseAttack();
|
|
}
|
|
|
|
if (attackCoefficient == 0) {
|
|
return;
|
|
}
|
|
|
|
int random = _vm->_rnd.getRandomNumberRng(1, 100);
|
|
|
|
if (random <= attackCoefficient) {
|
|
if (enemy->isWalking()) {
|
|
enemy->stopWalking(true);
|
|
}
|
|
|
|
int sentenceId = _vm->_rnd.getRandomNumberRng(0, 1) ? 9000 : 9005;
|
|
if (enemy->inCombat()) {
|
|
enemy->changeAnimationMode(kAnimationModeCombatHit, false);
|
|
} else {
|
|
enemy->changeAnimationMode(kAnimationModeHit, false);
|
|
}
|
|
|
|
int damage = 0;
|
|
if (_rangedAttack) {
|
|
damage = getDamageRangedAttack(random, attackCoefficient);
|
|
} else {
|
|
damage = getDamageCloseAttack(random, attackCoefficient);
|
|
}
|
|
|
|
int enemyHp = MAX(enemy->getCurrentHP() - damage, 0);
|
|
enemy->setCurrentHP(enemyHp);
|
|
|
|
if (enemyHp <= 0) {
|
|
if (!enemy->isRetired()) {
|
|
#if BLADERUNNER_ORIGINAL_BUGS
|
|
#else
|
|
// make sure the dead enemy won't pick a pending movement track and re-spawn
|
|
enemy->_movementTrack->flush();
|
|
#endif
|
|
if (enemy->inCombat()) {
|
|
enemy->changeAnimationMode(kAnimationModeCombatDie, false);
|
|
} else {
|
|
enemy->changeAnimationMode(kAnimationModeDie, false);
|
|
}
|
|
sentenceId = 9020;
|
|
}
|
|
enemy->retire(true, 6, 3, _actorId);
|
|
}
|
|
|
|
if (_enemyId == kActorMcCoy) {
|
|
sentenceId += 900;
|
|
}
|
|
|
|
_vm->_audioSpeech->playSpeechLine(_enemyId, sentenceId, 75, enemy->soundPan(), 99);
|
|
}
|
|
}
|
|
|
|
void ActorCombat::save(SaveFileWriteStream &f) {
|
|
f.writeInt(_actorId);
|
|
f.writeBool(_active);
|
|
f.writeInt(_state);
|
|
f.writeBool(_rangedAttack);
|
|
f.writeInt(_enemyId);
|
|
f.writeInt(_waypointType);
|
|
f.writeInt(_damage);
|
|
f.writeInt(_fleeRatio);
|
|
f.writeInt(_coverRatio);
|
|
f.writeInt(_attackRatio);
|
|
f.writeInt(_fleeRatioConst);
|
|
f.writeInt(_coverRatioConst);
|
|
f.writeInt(_attackRatioConst);
|
|
f.writeInt(_range);
|
|
f.writeInt(_unstoppable);
|
|
f.writeInt(_actorHp);
|
|
f.writeInt(_fleeingTowards);
|
|
f.writeVector3(_actorPosition);
|
|
f.writeVector3(_enemyPosition);
|
|
f.writeInt(_coversWaypointCount);
|
|
f.writeInt(_fleeWaypointsCount);
|
|
}
|
|
|
|
void ActorCombat::load(SaveFileReadStream &f) {
|
|
_actorId = f.readInt();
|
|
_active = f.readBool();
|
|
_state = f.readInt();
|
|
_rangedAttack = f.readBool();
|
|
_enemyId = f.readInt();
|
|
_waypointType = f.readInt();
|
|
_damage = f.readInt();
|
|
_fleeRatio = f.readInt();
|
|
_coverRatio = f.readInt();
|
|
_attackRatio = f.readInt();
|
|
_fleeRatioConst = f.readInt();
|
|
_coverRatioConst = f.readInt();
|
|
_attackRatioConst = f.readInt();
|
|
_range = f.readInt();
|
|
_unstoppable = f.readInt();
|
|
_actorHp = f.readInt();
|
|
_fleeingTowards = f.readInt();
|
|
_actorPosition = f.readVector3();
|
|
_enemyPosition = f.readVector3();
|
|
_coversWaypointCount = f.readInt();
|
|
_fleeWaypointsCount = f.readInt();
|
|
}
|
|
|
|
void ActorCombat::reset() {
|
|
_active = false;
|
|
_actorId = -1;
|
|
_state = -1;
|
|
_rangedAttack = false;
|
|
_enemyId = -1;
|
|
_waypointType = -1;
|
|
_damage = 0;
|
|
_fleeRatio = -1;
|
|
_coverRatio = -1;
|
|
_attackRatio = -1;
|
|
_fleeRatioConst = -1;
|
|
_coverRatioConst = -1;
|
|
_attackRatioConst = -1;
|
|
_actorHp = 0;
|
|
_range = 300;
|
|
_unstoppable = false;
|
|
_actorPosition = Vector3(0.0f, 0.0f, 0.0f);
|
|
_enemyPosition = Vector3(0.0f, 0.0f, 0.0f);
|
|
_coversWaypointCount = 0;
|
|
_fleeWaypointsCount = 0;
|
|
_fleeingTowards = -1;
|
|
}
|
|
|
|
void ActorCombat::cover() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
|
|
if (actor->isWalking()) {
|
|
return;
|
|
}
|
|
|
|
if (actor->isObstacleBetween(_enemyPosition)) {
|
|
faceEnemy();
|
|
return;
|
|
}
|
|
|
|
int coverWaypointId = _vm->_combat->findCoverWaypoint(_waypointType, _actorId, _enemyId);
|
|
if (coverWaypointId == -1) {
|
|
_state = kActorCombatStateIdle;
|
|
} else {
|
|
actor->asyncWalkToXYZ(_vm->_combat->_coverWaypoints[coverWaypointId].position, 0, true, 0);
|
|
}
|
|
}
|
|
|
|
void ActorCombat::approachToCloseAttack() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
float dist = actor->distanceFromActor(_enemyId);
|
|
if (dist > 36.0f) {
|
|
if (!actor->isWalking() || enemy->isWalking()) {
|
|
Vector3 target;
|
|
if (findClosestPositionToEnemy(target)) {
|
|
actor->asyncWalkToXYZ(target, 0, dist >= 240.0f, 0);
|
|
} else {
|
|
_state = kActorCombatStateCover;
|
|
}
|
|
}
|
|
} else {
|
|
if (actor->isWalking()) {
|
|
actor->stopWalking(false);
|
|
}
|
|
faceEnemy();
|
|
_state = kActorCombatStateCloseAttack;
|
|
}
|
|
}
|
|
|
|
void ActorCombat::approachToRangedAttack() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
float dist = actor->distanceFromActor(_enemyId);
|
|
if (dist > _range) {
|
|
if (!actor->isWalking() || enemy->isWalking()) {
|
|
Vector3 target;
|
|
if (findClosestPositionToEnemy(target)) {
|
|
actor->asyncWalkToXYZ(target, 0, dist >= 240.0f, 0);
|
|
} else {
|
|
_state = kActorCombatStateCover;
|
|
}
|
|
}
|
|
} else {
|
|
if (actor->isWalking()) {
|
|
actor->stopWalking(false);
|
|
}
|
|
faceEnemy();
|
|
_state = kActorCombatStateRangedAttack;
|
|
}
|
|
}
|
|
|
|
void ActorCombat::uncover() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
if (actor->isObstacleBetween(_enemyPosition)) {
|
|
actor->asyncWalkToXYZ(enemy->getXYZ(), 16, false, 0);
|
|
} else {
|
|
if (actor->isWalking()) {
|
|
actor->stopWalking(false);
|
|
}
|
|
faceEnemy();
|
|
}
|
|
}
|
|
|
|
void ActorCombat::aim() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
|
|
if (actor->isObstacleBetween(_enemyPosition)) {
|
|
if (actor->getAnimationMode() != kAnimationModeCombatIdle) {
|
|
actor->changeAnimationMode(kAnimationModeCombatIdle, false);
|
|
}
|
|
} else {
|
|
faceEnemy();
|
|
if (actor->getAnimationMode() != kAnimationModeCombatAim) {
|
|
actor->changeAnimationMode(kAnimationModeCombatAim, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActorCombat::rangedAttack() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
|
|
if (actor->isObstacleBetween(_enemyPosition) || (actor->distanceFromActor(_enemyId) > _range)) {
|
|
_state = kActorCombatStateApproachRangedAttack;
|
|
} else {
|
|
faceEnemy();
|
|
if (actor->getAnimationMode() != kAnimationModeCombatAttack) {
|
|
if (_enemyId != kActorMcCoy || _vm->playerHasControl() || _unstoppable) {
|
|
actor->changeAnimationMode(kAnimationModeCombatAttack, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActorCombat::closeAttack() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
|
|
if (actor->isObstacleBetween(_enemyPosition) || (actor->distanceFromActor(_enemyId) > 36.0f)) {
|
|
_state = kActorCombatStateApproachCloseAttack;
|
|
} else {
|
|
faceEnemy();
|
|
if (actor->getAnimationMode() != kAnimationModeCombatAttack) {
|
|
if (_enemyId != kActorMcCoy || _vm->playerHasControl() || _unstoppable) {
|
|
actor->changeAnimationMode(kAnimationModeCombatAttack, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActorCombat::flee() {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
|
|
if (_fleeingTowards != -1 && actor->isWalking()) {
|
|
Vector3 fleeWaypointPosition = _vm->_combat->_fleeWaypoints[_fleeingTowards].position;
|
|
if (distance(_actorPosition, fleeWaypointPosition) <= 12.0f) {
|
|
_vm->_aiScripts->fledCombat(_actorId/*, _enemyId*/);
|
|
actor->setSetId(kSetFreeSlotG);
|
|
actor->combatModeOff();
|
|
_fleeingTowards = -1;
|
|
}
|
|
} else {
|
|
int fleeWaypointId = _vm->_combat->findFleeWaypoint(actor->getSetId(), _enemyId, _actorPosition);
|
|
if (fleeWaypointId == -1) {
|
|
_state = kActorCombatStateIdle;
|
|
} else {
|
|
Vector3 fleeWaypointPosition = _vm->_combat->_fleeWaypoints[fleeWaypointId].position;
|
|
actor->asyncWalkToXYZ(fleeWaypointPosition, 0, true, 0);
|
|
_fleeingTowards = fleeWaypointId;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActorCombat::faceEnemy() {
|
|
_vm->_actors[_actorId]->setFacing(angle_1024(_actorPosition.x, _actorPosition.z, _enemyPosition.x, _enemyPosition.z), false);
|
|
}
|
|
|
|
int ActorCombat::getCoefficientCloseAttack() const{
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
float distance = actor->distanceFromActor(_enemyId);
|
|
|
|
if (distance > 36.0f) {
|
|
return 0;
|
|
}
|
|
|
|
int aggressiveness = 0;
|
|
if (enemy->isRunning()) {
|
|
aggressiveness = 11;
|
|
} else if (enemy->isMoving()) {
|
|
aggressiveness = 22;
|
|
} else {
|
|
aggressiveness = 33;
|
|
}
|
|
|
|
aggressiveness += actor->getCombatAggressiveness() / 3;
|
|
|
|
int angle = abs(actor->angleTo(_enemyPosition));
|
|
|
|
if (angle > 128) {
|
|
return false;
|
|
}
|
|
|
|
return aggressiveness + (abs(angle - 128) / 3.7f);
|
|
}
|
|
|
|
int ActorCombat::getCoefficientRangedAttack() const {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
if (actor->isObstacleBetween(_enemyPosition)) {
|
|
return 0;
|
|
}
|
|
|
|
int distance = MIN(actor->distanceFromActor(_enemyId), 900.0f);
|
|
|
|
int aggressiveness = 0;
|
|
if (enemy->isRunning()) {
|
|
aggressiveness = 10;
|
|
} else if (enemy->isMoving()) {
|
|
aggressiveness = 20;
|
|
} else {
|
|
aggressiveness = 30;
|
|
}
|
|
|
|
aggressiveness += actor->getCombatAggressiveness() / 5;
|
|
return aggressiveness + abs((distance / 30) - 30) + actor->getIntelligence() / 5;
|
|
}
|
|
|
|
int ActorCombat::getDamageCloseAttack(int min, int max) const {
|
|
if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyEasy) {
|
|
return _damage / 2;
|
|
}
|
|
if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyHard) {
|
|
return _damage;
|
|
}
|
|
return ((MIN(max - min, 30) * 100.0f / 60.0f) + 50) * _damage / 100;
|
|
}
|
|
|
|
int ActorCombat::getDamageRangedAttack(int min, int max) const {
|
|
if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyEasy) {
|
|
return _damage / 2;
|
|
}
|
|
if (_enemyId == kActorMcCoy && _vm->_settings->getDifficulty() == kGameDifficultyHard) {
|
|
return _damage;
|
|
}
|
|
return ((MIN(max - min, 30) * 100.0f / 60.0f) + 50) * _damage / 100;
|
|
}
|
|
|
|
int ActorCombat::calculateAttackRatio() const {
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
int aggressivenessFactor = actor->getCombatAggressiveness();
|
|
int actorHpFactor = actor->getCurrentHP();
|
|
int enemyHpFactor = 100 - enemy->getCurrentHP();
|
|
int combatFactor = enemy->inCombat() ? 0 : 100;
|
|
int angleFactor = (100 * abs(enemy->angleTo(_actorPosition))) / 512;
|
|
int distanceFactor = 2 * (50 - MIN(actor->distanceFromActor(_enemyId) / 12.0f, 50.0f));
|
|
|
|
if (_rangedAttack) {
|
|
return
|
|
angleFactor * 0.25f +
|
|
combatFactor * 0.05f +
|
|
enemyHpFactor * 0.20f +
|
|
actorHpFactor * 0.10f +
|
|
aggressivenessFactor * 0.40f;
|
|
} else {
|
|
return
|
|
distanceFactor * 0.20f +
|
|
angleFactor * 0.10f +
|
|
combatFactor * 0.10f +
|
|
enemyHpFactor * 0.15f +
|
|
actorHpFactor * 0.15f +
|
|
aggressivenessFactor * 0.30f;
|
|
}
|
|
}
|
|
|
|
int ActorCombat::calculateCoverRatio() const {
|
|
if (_coversWaypointCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
int angleFactor = 100 - (100 * abs(enemy->angleTo(_actorPosition))) / 512;
|
|
int actorHpFactor = 100 - actor->getCurrentHP();
|
|
int enemyHpFactor = enemy->getCurrentHP();
|
|
int aggressivenessFactor = 100 - actor->getCombatAggressiveness();
|
|
int distanceFactor = 2 * MIN(actor->distanceFromActor(_enemyId) / 12.0f, 50.0f);
|
|
|
|
if (_rangedAttack) {
|
|
return
|
|
angleFactor * 0.40f +
|
|
enemyHpFactor * 0.05f +
|
|
actorHpFactor * 0.15f +
|
|
aggressivenessFactor * 0.50f;
|
|
} else {
|
|
return
|
|
distanceFactor * 0.25f +
|
|
angleFactor * 0.20f +
|
|
enemyHpFactor * 0.05f +
|
|
actorHpFactor * 0.10f +
|
|
aggressivenessFactor * 0.50f;
|
|
}
|
|
}
|
|
|
|
int ActorCombat::calculateFleeRatio() const {
|
|
if (_fleeWaypointsCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
Actor *actor = _vm->_actors[_actorId];
|
|
Actor *enemy = _vm->_actors[_enemyId];
|
|
|
|
int aggressivenessFactor = 100 - actor->getCombatAggressiveness();
|
|
int actorHpFactor = 100 - actor->getCurrentHP();
|
|
int combatFactor = enemy->inCombat() ? 100 : 0;
|
|
|
|
return
|
|
combatFactor * 0.2f +
|
|
actorHpFactor * 0.4f +
|
|
aggressivenessFactor * 0.4f;
|
|
}
|
|
|
|
bool ActorCombat::findClosestPositionToEnemy(Vector3 &output) const {
|
|
output = Vector3();
|
|
|
|
Vector3 offsets[] = {
|
|
Vector3( 0.0f, 0.0f, -28.0f),
|
|
Vector3( 28.0f, 0.0f, 0.0f),
|
|
Vector3( 0.0f, 0.0f, 28.0f),
|
|
Vector3(-28.0f, 0.0f, 0.0f)
|
|
};
|
|
|
|
float min = -1.0f;
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
Vector3 test = _enemyPosition + offsets[i];
|
|
float dist = distance(_actorPosition, test);
|
|
if ( min == -1.0f || dist < min) {
|
|
if (!_vm->_sceneObjects->existsOnXZ(_actorId + kSceneObjectOffsetActors, test.x, test.z, true, true) && _vm->_scene->_set->findWalkbox(test.x, test.z) >= 0) {
|
|
output = test;
|
|
min = dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
return min >= 0.0f;
|
|
}
|
|
|
|
} // End of namespace BladeRunner
|