nav: allow null-velocity jump links
nav: fix: bots failure to switch weapon back after jump refactor: re-organize some sources
This commit is contained in:
parent
2c9897c8f0
commit
48e157c7b4
10 changed files with 2245 additions and 2208 deletions
|
|
@ -635,7 +635,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasNext () const {
|
bool hasNext () const {
|
||||||
return length () > m_cursor;
|
return length () - m_cursor > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty () const {
|
bool empty () const {
|
||||||
|
|
@ -749,7 +749,6 @@ private:
|
||||||
float m_playServerTime {}; // time bot spent in the game
|
float m_playServerTime {}; // time bot spent in the game
|
||||||
float m_changeViewTime {}; // timestamp to change look at while at freezetime
|
float m_changeViewTime {}; // timestamp to change look at while at freezetime
|
||||||
float m_breakableTime {}; // breakeble acquired time
|
float m_breakableTime {}; // breakeble acquired time
|
||||||
float m_jumpDistance {}; // last jump distance
|
|
||||||
|
|
||||||
bool m_moveToGoal {}; // bot currently moving to goal??
|
bool m_moveToGoal {}; // bot currently moving to goal??
|
||||||
bool m_isStuck {}; // bot is stuck
|
bool m_isStuck {}; // bot is stuck
|
||||||
|
|
@ -770,7 +769,6 @@ private:
|
||||||
bool m_moveToC4 {}; // ct is moving to bomb
|
bool m_moveToC4 {}; // ct is moving to bomb
|
||||||
bool m_grenadeRequested {}; // bot requested change to grenade
|
bool m_grenadeRequested {}; // bot requested change to grenade
|
||||||
bool m_needToSendWelcomeChat {}; // bot needs to greet people on server?
|
bool m_needToSendWelcomeChat {}; // bot needs to greet people on server?
|
||||||
bool m_switchedToKnifeDuringJump {}; // bot needs to revert weapon after jump?
|
|
||||||
bool m_isCreature {}; // bot is not a player, but something else ? zombie ?
|
bool m_isCreature {}; // bot is not a player, but something else ? zombie ?
|
||||||
|
|
||||||
Pickup m_pickupType {}; // type of entity which needs to be used/picked up
|
Pickup m_pickupType {}; // type of entity which needs to be used/picked up
|
||||||
|
|
@ -1302,6 +1300,7 @@ extern ConVar cv_graph_url;
|
||||||
extern ConVar cv_graph_url_upload;
|
extern ConVar cv_graph_url_upload;
|
||||||
extern ConVar cv_graph_auto_save_count;
|
extern ConVar cv_graph_auto_save_count;
|
||||||
extern ConVar cv_graph_analyze_max_jump_height;
|
extern ConVar cv_graph_analyze_max_jump_height;
|
||||||
|
extern ConVar cv_spraypaints;
|
||||||
|
|
||||||
extern ConVar mp_freezetime;
|
extern ConVar mp_freezetime;
|
||||||
extern ConVar mp_roundtime;
|
extern ConVar mp_roundtime;
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,8 @@ sources = files(
|
||||||
'src/sounds.cpp',
|
'src/sounds.cpp',
|
||||||
'src/storage.cpp',
|
'src/storage.cpp',
|
||||||
'src/support.cpp',
|
'src/support.cpp',
|
||||||
|
'src/tasks.cpp',
|
||||||
|
'src/vision.cpp',
|
||||||
'src/vistable.cpp'
|
'src/vistable.cpp'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
2041
src/botlib.cpp
2041
src/botlib.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -2160,3 +2160,71 @@ bool Bot::isEnemyNoticeable (float range) {
|
||||||
|
|
||||||
return rg.get (0.0f, 100.0f) < noticeChance;
|
return rg.get (0.0f, 100.0f) < noticeChance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Bot::getAmmo () {
|
||||||
|
return getAmmo (m_currentWeapon);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bot::getAmmo (int id) {
|
||||||
|
const auto &prop = conf.getWeaponProp (id);
|
||||||
|
|
||||||
|
if (prop.ammo1 == -1 || prop.ammo1 > kMaxWeapons - 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return m_ammo[prop.ammo1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::selectWeaponByIndex (int index) {
|
||||||
|
auto tab = conf.getRawWeapons ();
|
||||||
|
issueCommand (tab[index].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::selectWeaponById (int id) {
|
||||||
|
const auto &prop = conf.getWeaponProp (id);
|
||||||
|
issueCommand (prop.classname.chars ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::checkBurstMode (float distance) {
|
||||||
|
// this function checks burst mode, and switch it depending distance to to enemy.
|
||||||
|
|
||||||
|
if (hasShield ()) {
|
||||||
|
return; // no checking when shield is active
|
||||||
|
}
|
||||||
|
|
||||||
|
// if current weapon is glock, disable burstmode on long distances, enable it else
|
||||||
|
if (m_currentWeapon == Weapon::Glock18 && distance < 300.0f && m_weaponBurstMode == BurstMode::Off) {
|
||||||
|
pev->button |= IN_ATTACK2;
|
||||||
|
}
|
||||||
|
else if (m_currentWeapon == Weapon::Glock18 && distance >= 300.0f && m_weaponBurstMode == BurstMode::On) {
|
||||||
|
pev->button |= IN_ATTACK2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if current weapon is famas, disable burstmode on short distances, enable it else
|
||||||
|
if (m_currentWeapon == Weapon::Famas && distance > 400.0f && m_weaponBurstMode == BurstMode::Off) {
|
||||||
|
pev->button |= IN_ATTACK2;
|
||||||
|
}
|
||||||
|
else if (m_currentWeapon == Weapon::Famas && distance <= 400.0f && m_weaponBurstMode == BurstMode::On) {
|
||||||
|
pev->button |= IN_ATTACK2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::checkSilencer () {
|
||||||
|
if ((m_currentWeapon == Weapon::USP || m_currentWeapon == Weapon::M4A1) && !hasShield () && game.isNullEntity (m_enemy)) {
|
||||||
|
int prob = (m_personality == Personality::Rusher ? 35 : 65);
|
||||||
|
|
||||||
|
// aggressive bots don't like the silencer
|
||||||
|
if (rg.chance (m_currentWeapon == Weapon::USP ? prob / 2 : prob)) {
|
||||||
|
// is the silencer not attached...
|
||||||
|
if (pev->weaponanim > 6) {
|
||||||
|
pev->button |= IN_ATTACK2; // attach the silencer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// is the silencer attached...
|
||||||
|
if (pev->weaponanim <= 6) {
|
||||||
|
pev->button |= IN_ATTACK2; // detach the silencer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1304,7 +1304,6 @@ void Bot::newRound () {
|
||||||
m_isLeader = false;
|
m_isLeader = false;
|
||||||
m_hasProgressBar = false;
|
m_hasProgressBar = false;
|
||||||
m_canChooseAimDirection = true;
|
m_canChooseAimDirection = true;
|
||||||
m_switchedToKnifeDuringJump = false;
|
|
||||||
m_preventFlashing = 0.0f;
|
m_preventFlashing = 0.0f;
|
||||||
|
|
||||||
m_timeTeamOrder = 0.0f;
|
m_timeTeamOrder = 0.0f;
|
||||||
|
|
|
||||||
194
src/navigate.cpp
194
src/navigate.cpp
|
|
@ -840,7 +840,34 @@ bool Bot::updateNavigation () {
|
||||||
// pressing the jump button gives the illusion of the bot actual jumping.
|
// pressing the jump button gives the illusion of the bot actual jumping.
|
||||||
if (isOnFloor () || isOnLadder ()) {
|
if (isOnFloor () || isOnLadder ()) {
|
||||||
if (m_desiredVelocity.length2d () > 0.0f) {
|
if (m_desiredVelocity.length2d () > 0.0f) {
|
||||||
pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.05f;
|
pev->velocity = m_desiredVelocity;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto feet = pev->origin + pev->mins;
|
||||||
|
auto node = Vector { m_pathOrigin.x, m_pathOrigin.y, m_pathOrigin.z - ((m_pathFlags & NodeFlag::Crouch) ? 18.0f : 36.0f) };
|
||||||
|
|
||||||
|
if (feet.z > feet.z) {
|
||||||
|
feet = pev->origin + pev->maxs;
|
||||||
|
}
|
||||||
|
feet = { pev->origin.x, pev->origin.y, feet.z };
|
||||||
|
|
||||||
|
// calculate like we do with grenades
|
||||||
|
auto velocity = calcThrow (feet, node);
|
||||||
|
|
||||||
|
if (velocity.lengthSq () < 100.0f) {
|
||||||
|
velocity = calcToss (feet, node);
|
||||||
|
}
|
||||||
|
velocity = velocity + velocity * 0.45f;
|
||||||
|
|
||||||
|
// set the bot "grenade" velocity
|
||||||
|
if (velocity.length2d () > 0.0f) {
|
||||||
|
pev->velocity = velocity;
|
||||||
|
pev->velocity.z = 0.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pev->velocity = pev->velocity + pev->velocity * m_frameInterval * 2.0f;
|
||||||
|
pev->velocity.z = 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pev->button |= IN_JUMP;
|
pev->button |= IN_JUMP;
|
||||||
|
|
||||||
|
|
@ -849,16 +876,9 @@ bool Bot::updateNavigation () {
|
||||||
m_desiredVelocity = nullptr;
|
m_desiredVelocity = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (!cv_jasonmode.bool_ () && usesKnife () && isOnFloor ()) {
|
||||||
else if (m_jumpDistance > 0.0f && !isKnifeMode () && m_switchedToKnifeDuringJump && isOnFloor ()) {
|
|
||||||
selectBestWeapon ();
|
selectBestWeapon ();
|
||||||
|
|
||||||
// if jump distance was big enough, cooldown a little
|
|
||||||
if (m_jumpDistance > 180.0f) {
|
|
||||||
startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.45f, false);
|
|
||||||
}
|
}
|
||||||
m_jumpDistance = 0.0f;
|
|
||||||
m_switchedToKnifeDuringJump = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pathFlags & NodeFlag::Ladder) {
|
if (m_pathFlags & NodeFlag::Ladder) {
|
||||||
|
|
@ -2194,9 +2214,10 @@ bool Bot::advanceMovement () {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if bot is going to jump
|
// check if bot is going to jump
|
||||||
bool willJump = false;
|
bool willJump = false;
|
||||||
m_jumpDistance = 0.0f;
|
float jumpDistance = 0.0f;
|
||||||
|
|
||||||
Vector src;
|
Vector src;
|
||||||
Vector dst;
|
Vector dst;
|
||||||
|
|
@ -2213,7 +2234,7 @@ bool Bot::advanceMovement () {
|
||||||
src = path.origin;
|
src = path.origin;
|
||||||
dst = next.origin;
|
dst = next.origin;
|
||||||
|
|
||||||
m_jumpDistance = path.origin.distance (next.origin);
|
jumpDistance = src.distance (dst);
|
||||||
willJump = true;
|
willJump = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -2222,11 +2243,8 @@ bool Bot::advanceMovement () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// is there a jump node right ahead and do we need to draw out the light weapon ?
|
// is there a jump node right ahead and do we need to draw out the light weapon ?
|
||||||
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (m_jumpDistance > 175.0f || (dst.z - 32.0f > src.z && m_jumpDistance > 135.0f)) && !(m_states & Sense::SeeingEnemy)) {
|
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (jumpDistance > 145.0f || (dst.z - 32.0f > src.z && jumpDistance > 125.0f)) && !(m_states & Sense::SeeingEnemy)) {
|
||||||
selectWeaponById (Weapon::Knife); // draw out the knife if we needed
|
selectWeaponById (Weapon::Knife); // draw out the knife if we needed
|
||||||
|
|
||||||
// mark as switched
|
|
||||||
m_switchedToKnifeDuringJump = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bot not already on ladder but will be soon?
|
// bot not already on ladder but will be soon?
|
||||||
|
|
@ -2875,152 +2893,6 @@ int Bot::getRandomCampDir () {
|
||||||
return graph.random ();
|
return graph.random ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::updateBodyAngles () {
|
|
||||||
// set the body angles to point the gun correctly
|
|
||||||
pev->angles.x = -pev->v_angle.x * (1.0f / 3.0f);
|
|
||||||
pev->angles.y = pev->v_angle.y;
|
|
||||||
|
|
||||||
pev->angles.clampAngles ();
|
|
||||||
|
|
||||||
// calculate frustum plane data here, since lookangles update functions call this last one
|
|
||||||
calculateFrustum ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bot::updateLookAngles () {
|
|
||||||
|
|
||||||
const float delta = cr::clamp (game.time () - m_lookUpdateTime, cr::kFloatEqualEpsilon, 1.0f / 30.0f);
|
|
||||||
m_lookUpdateTime = game.time ();
|
|
||||||
|
|
||||||
// adjust all body and view angles to face an absolute vector
|
|
||||||
Vector direction = (m_lookAt - getEyesPos ()).angles ();
|
|
||||||
direction.x = -direction.x; // invert for engine
|
|
||||||
|
|
||||||
direction.clampAngles ();
|
|
||||||
|
|
||||||
// lower skilled bot's have lower aiming
|
|
||||||
if (m_difficulty == Difficulty::Noob) {
|
|
||||||
updateLookAnglesNewbie (direction, delta);
|
|
||||||
updateBodyAngles ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float accelerate = 3000.0f;
|
|
||||||
float stiffness = 200.0f;
|
|
||||||
float damping = 25.0f;
|
|
||||||
|
|
||||||
if (((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
|
|
||||||
if (m_difficulty == Difficulty::Expert) {
|
|
||||||
accelerate += 600.0f;
|
|
||||||
}
|
|
||||||
stiffness += 100.0f;
|
|
||||||
damping -= 5.0f;
|
|
||||||
}
|
|
||||||
m_idealAngles = pev->v_angle;
|
|
||||||
|
|
||||||
const float angleDiffPitch = cr::anglesDifference (direction.x, m_idealAngles.x);
|
|
||||||
const float angleDiffYaw = cr::anglesDifference (direction.y, m_idealAngles.y);
|
|
||||||
|
|
||||||
if (angleDiffYaw < 1.0f && angleDiffYaw > -1.0f) {
|
|
||||||
m_lookYawVel = 0.0f;
|
|
||||||
m_idealAngles.y = direction.y;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
float accel = cr::clamp (stiffness * angleDiffYaw - damping * m_lookYawVel, -accelerate, accelerate);
|
|
||||||
|
|
||||||
m_lookYawVel += delta * accel;
|
|
||||||
m_idealAngles.y += delta * m_lookYawVel;
|
|
||||||
}
|
|
||||||
float accel = cr::clamp (2.0f * stiffness * angleDiffPitch - damping * m_lookPitchVel, -accelerate, accelerate);
|
|
||||||
|
|
||||||
m_lookPitchVel += delta * accel;
|
|
||||||
m_idealAngles.x += cr::clamp (delta * m_lookPitchVel, -89.0f, 89.0f);
|
|
||||||
|
|
||||||
pev->v_angle = m_idealAngles;
|
|
||||||
pev->v_angle.clampAngles ();
|
|
||||||
|
|
||||||
updateBodyAngles ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bot::updateLookAnglesNewbie (const Vector &direction, float delta) {
|
|
||||||
Vector spring { 13.0f, 13.0f, 0.0f };
|
|
||||||
Vector damperCoefficient { 0.22f, 0.22f, 0.0f };
|
|
||||||
|
|
||||||
const float offset = cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 25.0f;
|
|
||||||
|
|
||||||
Vector influence = Vector (0.25f, 0.17f, 0.0f) * (100.0f - offset) / 100.f;
|
|
||||||
Vector randomization = Vector (2.0f, 0.18f, 0.0f) * (100.0f - offset) / 100.f;
|
|
||||||
|
|
||||||
const float noTargetRatio = 0.3f;
|
|
||||||
const float offsetDelay = 1.2f;
|
|
||||||
|
|
||||||
Vector stiffness;
|
|
||||||
Vector randomize;
|
|
||||||
|
|
||||||
m_idealAngles = direction.get2d ();
|
|
||||||
m_idealAngles.clampAngles ();
|
|
||||||
|
|
||||||
if (m_aimFlags & (AimFlags::Enemy | AimFlags::Entity)) {
|
|
||||||
m_playerTargetTime = game.time ();
|
|
||||||
m_randomizedIdealAngles = m_idealAngles;
|
|
||||||
|
|
||||||
stiffness = spring * (0.2f + offset / 125.0f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// is it time for bot to randomize the aim direction again (more often where moving) ?
|
|
||||||
if (m_randomizeAnglesTime < game.time () && ((pev->velocity.length () > 1.0f && m_angularDeviation.length () < 5.0f) || m_angularDeviation.length () < 1.0f)) {
|
|
||||||
// is the bot standing still ?
|
|
||||||
if (pev->velocity.length () < 1.0f) {
|
|
||||||
randomize = randomization * 0.2f; // randomize less
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
randomize = randomization;
|
|
||||||
}
|
|
||||||
// randomize targeted location bit (slightly towards the ground)
|
|
||||||
m_randomizedIdealAngles = m_idealAngles + Vector (rg.get (-randomize.x * 0.5f, randomize.x * 1.5f), rg.get (-randomize.y, randomize.y), 0.0f);
|
|
||||||
|
|
||||||
// set next time to do this
|
|
||||||
m_randomizeAnglesTime = game.time () + rg.get (0.4f, offsetDelay);
|
|
||||||
}
|
|
||||||
float stiffnessMultiplier = noTargetRatio;
|
|
||||||
|
|
||||||
// take in account whether the bot was targeting someone in the last N seconds
|
|
||||||
if (game.time () - (m_playerTargetTime + offsetDelay) < noTargetRatio * 10.0f) {
|
|
||||||
stiffnessMultiplier = 1.0f - (game.time () - m_timeLastFired) * 0.1f;
|
|
||||||
|
|
||||||
// don't allow that stiffness multiplier less than zero
|
|
||||||
if (stiffnessMultiplier < 0.0f) {
|
|
||||||
stiffnessMultiplier = 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// also take in account the remaining deviation (slow down the aiming in the last 10°)
|
|
||||||
stiffnessMultiplier *= m_angularDeviation.length () * 0.1f * 0.5f;
|
|
||||||
|
|
||||||
// but don't allow getting below a certain value
|
|
||||||
if (stiffnessMultiplier < 0.35f) {
|
|
||||||
stiffnessMultiplier = 0.35f;
|
|
||||||
}
|
|
||||||
stiffness = spring * stiffnessMultiplier; // increasingly slow aim
|
|
||||||
}
|
|
||||||
// compute randomized angle deviation this time
|
|
||||||
m_angularDeviation = m_randomizedIdealAngles - pev->v_angle;
|
|
||||||
m_angularDeviation.clampAngles ();
|
|
||||||
|
|
||||||
// spring/damper model aiming
|
|
||||||
m_aimSpeed.x = stiffness.x * m_angularDeviation.x - damperCoefficient.x * m_aimSpeed.x;
|
|
||||||
m_aimSpeed.y = stiffness.y * m_angularDeviation.y - damperCoefficient.y * m_aimSpeed.y;
|
|
||||||
|
|
||||||
// influence of y movement on x axis and vice versa (less influence than x on y since it's
|
|
||||||
// easier and more natural for the bot to "move its mouse" horizontally than vertically)
|
|
||||||
m_aimSpeed.x += cr::clamp (m_aimSpeed.y * influence.y, -50.0f, 50.0f);
|
|
||||||
m_aimSpeed.y += cr::clamp (m_aimSpeed.x * influence.x, -200.0f, 200.0f);
|
|
||||||
|
|
||||||
// move the aim cursor
|
|
||||||
pev->v_angle = pev->v_angle + delta * Vector (m_aimSpeed.x, m_aimSpeed.y, 0.0f);
|
|
||||||
pev->v_angle.clampAngles ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) {
|
void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) {
|
||||||
const Vector &los = (moveDir - pev->origin).normalize2d ();
|
const Vector &los = (moveDir - pev->origin).normalize2d ();
|
||||||
float dot = los | pev->angles.forward ().get2d ();
|
float dot = los | pev->angles.forward ().get2d ();
|
||||||
|
|
|
||||||
1680
src/tasks.cpp
Normal file
1680
src/tasks.cpp
Normal file
File diff suppressed because it is too large
Load diff
455
src/vision.cpp
Normal file
455
src/vision.cpp
Normal file
|
|
@ -0,0 +1,455 @@
|
||||||
|
//
|
||||||
|
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||||
|
// Copyright © 2004-2023 YaPB Project <yapb@jeefo.net>.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <yapb.h>
|
||||||
|
|
||||||
|
ConVar cv_max_nodes_for_predict ("yb_max_nodes_for_predict", "20", "Maximum number for path length, to predict the enemy.", true, 15.0f, 256.0f);
|
||||||
|
|
||||||
|
// game console variables
|
||||||
|
ConVar mp_flashlight ("mp_flashlight", nullptr, Var::GameRef);
|
||||||
|
|
||||||
|
float Bot::isInFOV (const Vector &destination) {
|
||||||
|
const float entityAngle = cr::wrapAngle360 (destination.yaw ()); // find yaw angle from source to destination...
|
||||||
|
const float viewAngle = cr::wrapAngle360 (pev->v_angle.y); // get bot's current view angle...
|
||||||
|
|
||||||
|
// return the absolute value of angle to destination entity
|
||||||
|
// zero degrees means straight ahead, 45 degrees to the left or
|
||||||
|
// 45 degrees to the right is the limit of the normal view angle
|
||||||
|
float absoluteAngle = cr::abs (viewAngle - entityAngle);
|
||||||
|
|
||||||
|
if (absoluteAngle > 180.0f) {
|
||||||
|
absoluteAngle = 360.0f - absoluteAngle;
|
||||||
|
}
|
||||||
|
return absoluteAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::isInViewCone (const Vector &origin) {
|
||||||
|
// this function returns true if the spatial vector location origin is located inside
|
||||||
|
// the field of view cone of the bot entity, false otherwise. It is assumed that entities
|
||||||
|
// have a human-like field of view, that is, about 90 degrees.
|
||||||
|
|
||||||
|
return util.isInViewCone (origin, ent ());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::seesItem (const Vector &destination, const char *classname) {
|
||||||
|
TraceResult tr {};
|
||||||
|
|
||||||
|
// trace a line from bot's eyes to destination..
|
||||||
|
game.testLine (getEyesPos (), destination, TraceIgnore::None, ent (), &tr);
|
||||||
|
|
||||||
|
// check if line of sight to object is not blocked (i.e. visible)
|
||||||
|
if (tr.flFraction < 1.0f && tr.pHit && !tr.fStartSolid) {
|
||||||
|
return strcmp (tr.pHit->v.classname.chars (), classname) == 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::seesEntity (const Vector &dest, bool fromBody) {
|
||||||
|
TraceResult tr {};
|
||||||
|
|
||||||
|
// trace a line from bot's eyes to destination...
|
||||||
|
game.testLine (fromBody ? pev->origin : getEyesPos (), dest, TraceIgnore::Everything, ent (), &tr);
|
||||||
|
|
||||||
|
// check if line of sight to object is not blocked (i.e. visible)
|
||||||
|
return tr.flFraction >= 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::updateAimDir () {
|
||||||
|
uint32_t flags = m_aimFlags;
|
||||||
|
|
||||||
|
// don't allow bot to look at danger positions under certain circumstances
|
||||||
|
if (!(flags & (AimFlags::Grenade | AimFlags::Enemy | AimFlags::Entity))) {
|
||||||
|
|
||||||
|
// check if narrow place and we're duck, do not predict enemies in that situation
|
||||||
|
const bool duckedInNarrowPlace = isInNarrowPlace () && ((m_pathFlags & NodeFlag::Crouch) || (pev->button & IN_DUCK));
|
||||||
|
|
||||||
|
if (duckedInNarrowPlace || isOnLadder () || isInWater () || (m_pathFlags & NodeFlag::Ladder) || (m_currentTravelFlags & PathFlag::Jump)) {
|
||||||
|
flags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath);
|
||||||
|
m_canChooseAimDirection = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & AimFlags::Override) {
|
||||||
|
m_lookAt = m_lookAtSafe;
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::Grenade) {
|
||||||
|
m_lookAt = m_throw;
|
||||||
|
|
||||||
|
float throwDistance = m_throw.distance (pev->origin);
|
||||||
|
float coordCorrection = 0.0f;
|
||||||
|
|
||||||
|
if (throwDistance > 100.0f && throwDistance < 800.0f) {
|
||||||
|
coordCorrection = 0.25f * (m_throw.z - pev->origin.z);
|
||||||
|
}
|
||||||
|
else if (throwDistance >= 800.0f) {
|
||||||
|
float angleCorrection = 37.0f * (throwDistance - 800.0f) / 800.0f;
|
||||||
|
|
||||||
|
if (angleCorrection > 45.0f) {
|
||||||
|
angleCorrection = 45.0f;
|
||||||
|
}
|
||||||
|
coordCorrection = throwDistance * cr::tanf (cr::deg2rad (angleCorrection)) + 0.25f * (m_throw.z - pev->origin.z);
|
||||||
|
}
|
||||||
|
m_lookAt.z += coordCorrection * 0.5f;
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::Enemy) {
|
||||||
|
focusEnemy ();
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::Entity) {
|
||||||
|
m_lookAt = m_entity;
|
||||||
|
|
||||||
|
// do not look at hostages legs
|
||||||
|
if (m_pickupType == Pickup::Hostage) {
|
||||||
|
m_lookAt.z += 48.0f;
|
||||||
|
}
|
||||||
|
else if (m_pickupType == Pickup::Weapon) {
|
||||||
|
m_lookAt.z += 72.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::LastEnemy) {
|
||||||
|
m_lookAt = m_lastEnemyOrigin;
|
||||||
|
|
||||||
|
// did bot just see enemy and is quite aggressive?
|
||||||
|
if (m_seeEnemyTime + 2.0f - m_actualReactionTime + m_baseAgressionLevel > game.time ()) {
|
||||||
|
|
||||||
|
// feel free to fire if shootable
|
||||||
|
if (!usesSniper () && lastEnemyShootable ()) {
|
||||||
|
m_wantsToFire = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::PredictPath) {
|
||||||
|
bool changePredictedEnemy = true;
|
||||||
|
bool predictFailed = false;
|
||||||
|
|
||||||
|
if (m_timeNextTracking < game.time () && m_trackingEdict == m_lastEnemy) {
|
||||||
|
changePredictedEnemy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto doFailPredict = [this] () {
|
||||||
|
m_aimFlags &= ~AimFlags::PredictPath;
|
||||||
|
m_trackingEdict = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (changePredictedEnemy) {
|
||||||
|
int pathLength = m_lastPredictLength;
|
||||||
|
int predictNode = m_lastPredictIndex;
|
||||||
|
|
||||||
|
if (predictNode != kInvalidNodeIndex) {
|
||||||
|
TraceResult tr;
|
||||||
|
game.testLine (getEyesPos (), graph[predictNode].origin, TraceIgnore::Everything, ent (), &tr);
|
||||||
|
|
||||||
|
if (tr.flFraction < 0.2f) {
|
||||||
|
pathLength = kInfiniteDistanceLong;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (graph.exists (predictNode) && pathLength < cv_max_nodes_for_predict.int_ ()) {
|
||||||
|
m_lookAt = graph[predictNode].origin;
|
||||||
|
m_lookAtSafe = m_lookAt;
|
||||||
|
|
||||||
|
m_timeNextTracking = game.time () + 0.25f;
|
||||||
|
m_trackingEdict = m_lastEnemy;
|
||||||
|
|
||||||
|
// feel free to fire if shootable
|
||||||
|
if (!usesSniper () && lastEnemyShootable ()) {
|
||||||
|
m_wantsToFire = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
doFailPredict ();
|
||||||
|
predictFailed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predictFailed) {
|
||||||
|
doFailPredict ();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_lookAt = m_lookAtSafe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::Camp) {
|
||||||
|
m_lookAt = m_lookAtSafe;
|
||||||
|
}
|
||||||
|
else if (flags & AimFlags::Nav) {
|
||||||
|
m_lookAt = m_destOrigin;
|
||||||
|
|
||||||
|
if (m_moveToGoal && m_seeEnemyTime + 4.0f < game.time () && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_pathFlags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && pev->origin.distanceSq (m_destOrigin) < cr::sqrf (512.0f)) {
|
||||||
|
auto nextPathIndex = m_pathWalk.next ();
|
||||||
|
|
||||||
|
if (vistab.visible (m_currentNodeIndex, nextPathIndex)) {
|
||||||
|
m_lookAt = graph[nextPathIndex].origin + pev->view_ofs;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_lookAt = m_destOrigin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_seeEnemyTime + 3.0f > game.time () && !m_lastEnemyOrigin.empty ()) {
|
||||||
|
m_lookAt = m_lastEnemyOrigin;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_lookAt = m_destOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_canChooseAimDirection && m_seeEnemyTime + 4.0f < game.time () && m_currentNodeIndex != kInvalidNodeIndex && !(m_pathFlags & NodeFlag::Ladder)) {
|
||||||
|
auto dangerIndex = practice.getIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
|
||||||
|
|
||||||
|
if (graph.exists (dangerIndex) && vistab.visible (m_currentNodeIndex, dangerIndex) && !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
|
||||||
|
if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::sqrf (160.0f)) {
|
||||||
|
m_lookAt = m_destOrigin;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_lookAt = graph[dangerIndex].origin + pev->view_ofs;
|
||||||
|
|
||||||
|
// add danger flags
|
||||||
|
m_aimFlags |= AimFlags::Danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't look at bottom of node, if reached it
|
||||||
|
if (m_lookAt == m_destOrigin) {
|
||||||
|
m_lookAt.z = getEyesPos ().z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_lookAt.empty ()) {
|
||||||
|
m_lookAt = m_destOrigin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::checkDarkness () {
|
||||||
|
|
||||||
|
// do not check for darkness at the start of the round
|
||||||
|
if (m_spawnTime + 5.0f > game.time () || !graph.exists (m_currentNodeIndex)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not check every frame
|
||||||
|
if (m_checkDarkTime + 5.0f > game.time () || cr::fzero (m_path->light)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto skyColor = illum.getSkyColor ();
|
||||||
|
auto flashOn = (pev->effects & EF_DIMLIGHT);
|
||||||
|
|
||||||
|
if (mp_flashlight.bool_ () && !m_hasNVG) {
|
||||||
|
auto task = Task ();
|
||||||
|
|
||||||
|
if (!flashOn && task != Task::Camp && task != Task::Attack && m_heardSoundTime + 3.0f < game.time () && m_flashLevel > 30 && ((skyColor > 50.0f && m_path->light < 10.0f) || (skyColor <= 50.0f && m_path->light < 40.0f))) {
|
||||||
|
pev->impulse = 100;
|
||||||
|
}
|
||||||
|
else if (flashOn && (((m_path->light > 15.0f && skyColor > 50.0f) || (m_path->light > 45.0f && skyColor <= 50.0f)) || task == Task::Camp || task == Task::Attack || m_flashLevel <= 0 || m_heardSoundTime + 3.0f >= game.time ())) {
|
||||||
|
pev->impulse = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_hasNVG) {
|
||||||
|
if (flashOn) {
|
||||||
|
pev->impulse = 100;
|
||||||
|
}
|
||||||
|
else if (!m_usesNVG && ((skyColor > 50.0f && m_path->light < 15.0f) || (skyColor <= 50.0f && m_path->light < 40.0f))) {
|
||||||
|
issueCommand ("nightvision");
|
||||||
|
}
|
||||||
|
else if (m_usesNVG && ((m_path->light > 20.0f && skyColor > 50.0f) || (m_path->light > 45.0f && skyColor <= 50.0f))) {
|
||||||
|
issueCommand ("nightvision");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_checkDarkTime = game.time ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bot::calculateFrustum () {
|
||||||
|
// this function updates bot view frustum
|
||||||
|
|
||||||
|
Vector forward, right, up;
|
||||||
|
pev->v_angle.angleVectors (&forward, &right, &up);
|
||||||
|
|
||||||
|
static Vector fc, nc, fbl, fbr, ftl, ftr, nbl, nbr, ntl, ntr;
|
||||||
|
|
||||||
|
fc = getEyesPos () + forward * frustum.MaxView;
|
||||||
|
nc = getEyesPos () + forward * frustum.MinView;
|
||||||
|
|
||||||
|
fbl = fc + (up * frustum.farHeight * 0.5f) - (right * frustum.farWidth * 0.5f);
|
||||||
|
fbr = fc + (up * frustum.farHeight * 0.5f) + (right * frustum.farWidth * 0.5f);
|
||||||
|
ftl = fc - (up * frustum.farHeight * 0.5f) - (right * frustum.farWidth * 0.5f);
|
||||||
|
ftr = fc - (up * frustum.farHeight * 0.5f) + (right * frustum.farWidth * 0.5f);
|
||||||
|
nbl = nc + (up * frustum.nearHeight * 0.5f) - (right * frustum.nearWidth * 0.5f);
|
||||||
|
nbr = nc + (up * frustum.nearHeight * 0.5f) + (right * frustum.nearWidth * 0.5f);
|
||||||
|
ntl = nc - (up * frustum.nearHeight * 0.5f) - (right * frustum.nearWidth * 0.5f);
|
||||||
|
ntr = nc - (up * frustum.nearHeight * 0.5f) + (right * frustum.nearWidth * 0.5f);
|
||||||
|
|
||||||
|
auto setPlane = [&] (FrustumSide side, const Vector &v1, const Vector &v2, const Vector &v3) {
|
||||||
|
auto &plane = m_frustum[side];
|
||||||
|
|
||||||
|
plane.normal = ((v2 - v1) ^ (v3 - v1)).normalize ();
|
||||||
|
plane.point = v2;
|
||||||
|
|
||||||
|
plane.result = -(plane.normal | plane.point);
|
||||||
|
};
|
||||||
|
|
||||||
|
setPlane (FrustumSide::Top, ftl, ntl, ntr);
|
||||||
|
setPlane (FrustumSide::Bottom, fbr, nbr, nbl);
|
||||||
|
setPlane (FrustumSide::Left, fbl, nbl, ntl);
|
||||||
|
setPlane (FrustumSide::Right, ftr, ntr, nbr);
|
||||||
|
setPlane (FrustumSide::Near, nbr, ntr, ntl);
|
||||||
|
setPlane (FrustumSide::Far, fbl, ftl, ftr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::isEnemyInFrustum (edict_t *enemy) {
|
||||||
|
const Vector &origin = enemy->v.origin - Vector (0.0f, 0.0f, 5.0f);
|
||||||
|
|
||||||
|
for (auto &plane : m_frustum) {
|
||||||
|
if (!util.isObjectInsidePlane (plane, origin, 60.0f, 16.0f)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::updateBodyAngles () {
|
||||||
|
// set the body angles to point the gun correctly
|
||||||
|
pev->angles.x = -pev->v_angle.x * (1.0f / 3.0f);
|
||||||
|
pev->angles.y = pev->v_angle.y;
|
||||||
|
|
||||||
|
pev->angles.clampAngles ();
|
||||||
|
|
||||||
|
// calculate frustum plane data here, since look angles update functions call this last one
|
||||||
|
calculateFrustum ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::updateLookAngles () {
|
||||||
|
const float delta = cr::clamp (game.time () - m_lookUpdateTime, cr::kFloatEqualEpsilon, 1.0f / 30.0f);
|
||||||
|
m_lookUpdateTime = game.time ();
|
||||||
|
|
||||||
|
// adjust all body and view angles to face an absolute vector
|
||||||
|
Vector direction = (m_lookAt - getEyesPos ()).angles ();
|
||||||
|
direction.x = -direction.x; // invert for engine
|
||||||
|
|
||||||
|
direction.clampAngles ();
|
||||||
|
|
||||||
|
// lower skilled bot's have lower aiming
|
||||||
|
if (m_difficulty == Difficulty::Noob) {
|
||||||
|
updateLookAnglesNewbie (direction, delta);
|
||||||
|
updateBodyAngles ();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float accelerate = 3000.0f;
|
||||||
|
float stiffness = 200.0f;
|
||||||
|
float damping = 25.0f;
|
||||||
|
|
||||||
|
if (((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
|
||||||
|
if (m_difficulty == Difficulty::Expert) {
|
||||||
|
accelerate += 600.0f;
|
||||||
|
}
|
||||||
|
stiffness += 100.0f;
|
||||||
|
damping -= 5.0f;
|
||||||
|
}
|
||||||
|
m_idealAngles = pev->v_angle;
|
||||||
|
|
||||||
|
const float angleDiffPitch = cr::anglesDifference (direction.x, m_idealAngles.x);
|
||||||
|
const float angleDiffYaw = cr::anglesDifference (direction.y, m_idealAngles.y);
|
||||||
|
|
||||||
|
if (angleDiffYaw < 1.0f && angleDiffYaw > -1.0f) {
|
||||||
|
m_lookYawVel = 0.0f;
|
||||||
|
m_idealAngles.y = direction.y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float accel = cr::clamp (stiffness * angleDiffYaw - damping * m_lookYawVel, -accelerate, accelerate);
|
||||||
|
|
||||||
|
m_lookYawVel += delta * accel;
|
||||||
|
m_idealAngles.y += delta * m_lookYawVel;
|
||||||
|
}
|
||||||
|
float accel = cr::clamp (2.0f * stiffness * angleDiffPitch - damping * m_lookPitchVel, -accelerate, accelerate);
|
||||||
|
|
||||||
|
m_lookPitchVel += delta * accel;
|
||||||
|
m_idealAngles.x += cr::clamp (delta * m_lookPitchVel, -89.0f, 89.0f);
|
||||||
|
|
||||||
|
pev->v_angle = m_idealAngles;
|
||||||
|
pev->v_angle.clampAngles ();
|
||||||
|
|
||||||
|
updateBodyAngles ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bot::updateLookAnglesNewbie (const Vector &direction, float delta) {
|
||||||
|
Vector spring { 13.0f, 13.0f, 0.0f };
|
||||||
|
Vector damperCoefficient { 0.22f, 0.22f, 0.0f };
|
||||||
|
|
||||||
|
const float offset = cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 25.0f;
|
||||||
|
|
||||||
|
Vector influence = Vector (0.25f, 0.17f, 0.0f) * (100.0f - offset) / 100.f;
|
||||||
|
Vector randomization = Vector (2.0f, 0.18f, 0.0f) * (100.0f - offset) / 100.f;
|
||||||
|
|
||||||
|
const float noTargetRatio = 0.3f;
|
||||||
|
const float offsetDelay = 1.2f;
|
||||||
|
|
||||||
|
Vector stiffness;
|
||||||
|
Vector randomize;
|
||||||
|
|
||||||
|
m_idealAngles = direction.get2d ();
|
||||||
|
m_idealAngles.clampAngles ();
|
||||||
|
|
||||||
|
if (m_aimFlags & (AimFlags::Enemy | AimFlags::Entity)) {
|
||||||
|
m_playerTargetTime = game.time ();
|
||||||
|
m_randomizedIdealAngles = m_idealAngles;
|
||||||
|
|
||||||
|
stiffness = spring * (0.2f + offset / 125.0f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// is it time for bot to randomize the aim direction again (more often where moving) ?
|
||||||
|
if (m_randomizeAnglesTime < game.time () && ((pev->velocity.length () > 1.0f && m_angularDeviation.length () < 5.0f) || m_angularDeviation.length () < 1.0f)) {
|
||||||
|
// is the bot standing still ?
|
||||||
|
if (pev->velocity.length () < 1.0f) {
|
||||||
|
randomize = randomization * 0.2f; // randomize less
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
randomize = randomization;
|
||||||
|
}
|
||||||
|
// randomize targeted location bit (slightly towards the ground)
|
||||||
|
m_randomizedIdealAngles = m_idealAngles + Vector (rg.get (-randomize.x * 0.5f, randomize.x * 1.5f), rg.get (-randomize.y, randomize.y), 0.0f);
|
||||||
|
|
||||||
|
// set next time to do this
|
||||||
|
m_randomizeAnglesTime = game.time () + rg.get (0.4f, offsetDelay);
|
||||||
|
}
|
||||||
|
float stiffnessMultiplier = noTargetRatio;
|
||||||
|
|
||||||
|
// take in account whether the bot was targeting someone in the last N seconds
|
||||||
|
if (game.time () - (m_playerTargetTime + offsetDelay) < noTargetRatio * 10.0f) {
|
||||||
|
stiffnessMultiplier = 1.0f - (game.time () - m_timeLastFired) * 0.1f;
|
||||||
|
|
||||||
|
// don't allow that stiffness multiplier less than zero
|
||||||
|
if (stiffnessMultiplier < 0.0f) {
|
||||||
|
stiffnessMultiplier = 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// also take in account the remaining deviation (slow down the aiming in the last 10°)
|
||||||
|
stiffnessMultiplier *= m_angularDeviation.length () * 0.1f * 0.5f;
|
||||||
|
|
||||||
|
// but don't allow getting below a certain value
|
||||||
|
if (stiffnessMultiplier < 0.35f) {
|
||||||
|
stiffnessMultiplier = 0.35f;
|
||||||
|
}
|
||||||
|
stiffness = spring * stiffnessMultiplier; // increasingly slow aim
|
||||||
|
}
|
||||||
|
// compute randomized angle deviation this time
|
||||||
|
m_angularDeviation = m_randomizedIdealAngles - pev->v_angle;
|
||||||
|
m_angularDeviation.clampAngles ();
|
||||||
|
|
||||||
|
// spring/damper model aiming
|
||||||
|
m_aimSpeed.x = stiffness.x * m_angularDeviation.x - damperCoefficient.x * m_aimSpeed.x;
|
||||||
|
m_aimSpeed.y = stiffness.y * m_angularDeviation.y - damperCoefficient.y * m_aimSpeed.y;
|
||||||
|
|
||||||
|
// influence of y movement on x axis and vice versa (less influence than x on y since it's
|
||||||
|
// easier and more natural for the bot to "move its mouse" horizontally than vertically)
|
||||||
|
m_aimSpeed.x += cr::clamp (m_aimSpeed.y * influence.y, -50.0f, 50.0f);
|
||||||
|
m_aimSpeed.y += cr::clamp (m_aimSpeed.x * influence.x, -200.0f, 200.0f);
|
||||||
|
|
||||||
|
// move the aim cursor
|
||||||
|
pev->v_angle = pev->v_angle + delta * Vector (m_aimSpeed.x, m_aimSpeed.y, 0.0f);
|
||||||
|
pev->v_angle.clampAngles ();
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include <yapb.h>
|
#include <yapb.h>
|
||||||
|
|
||||||
|
|
||||||
void GraphVistable::rebuild () {
|
void GraphVistable::rebuild () {
|
||||||
if (!m_rebuild) {
|
if (!m_rebuild) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@
|
||||||
<ClCompile Include="..\src\sounds.cpp" />
|
<ClCompile Include="..\src\sounds.cpp" />
|
||||||
<ClCompile Include="..\src\storage.cpp" />
|
<ClCompile Include="..\src\storage.cpp" />
|
||||||
<ClCompile Include="..\src\support.cpp" />
|
<ClCompile Include="..\src\support.cpp" />
|
||||||
|
<ClCompile Include="..\src\tasks.cpp" />
|
||||||
|
<ClCompile Include="..\src\vision.cpp" />
|
||||||
<ClCompile Include="..\src\vistable.cpp" />
|
<ClCompile Include="..\src\vistable.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue