conf: get decal indices during config load

combat: do not init hitbox aiming if disabled
nav: reverted some last changes
nav: fixed check fall on ladders
chat: added %g keyword that is replaced with graph author
This commit is contained in:
jeefo 2025-02-05 21:28:29 +03:00
commit ecb1f20303
No known key found for this signature in database
GPG key ID: D696786B81B667C8
12 changed files with 79 additions and 106 deletions

View file

@ -48,7 +48,7 @@ private:
SmallArray <WeaponInfo> m_weapons {}; SmallArray <WeaponInfo> m_weapons {};
SmallArray <WeaponProp> m_weaponProps {}; SmallArray <WeaponProp> m_weaponProps {};
StringArray m_logos {}; IntArray m_logosIndices {};
StringArray m_avatars {}; StringArray m_avatars {};
HashMap <uint32_t, String, Hash <int32_t>> m_language {}; HashMap <uint32_t, String, Hash <int32_t>> m_language {};
@ -244,13 +244,8 @@ public:
} }
// get's random logo index // get's random logo index
int32_t getRandomLogoIndex () const { int32_t getRandomLogoDecalIndex () const {
return static_cast <int32_t> (m_logos.index (m_logos.random ())); return static_cast <int32_t> (m_logosIndices.random ());
}
// get random name by index
StringRef getLogoName (int index) {
return m_logos[index];
} }
// get custom value // get custom value

View file

@ -439,12 +439,12 @@ constexpr auto kSprayDistanceX2 = kSprayDistance * 2;
constexpr auto kMaxChatterRepeatInterval = 99.0f; constexpr auto kMaxChatterRepeatInterval = 99.0f;
constexpr auto kViewFrameUpdate = 1.0f / 25.0f; constexpr auto kViewFrameUpdate = 1.0f / 25.0f;
constexpr auto kGrenadeDamageRadius = 385.0f; constexpr auto kGrenadeDamageRadius = 385.0f;
constexpr auto kMinMovedDistance = 2.0f; constexpr auto kMinMovedDistance = 3.0f;
constexpr auto kInfiniteDistanceLong = static_cast <int> (kInfiniteDistance); constexpr auto kInfiniteDistanceLong = static_cast <int> (kInfiniteDistance);
constexpr auto kMaxWeapons = 32; constexpr auto kMaxWeapons = 32;
constexpr auto kNumWeapons = 26; constexpr auto kNumWeapons = 26;
constexpr auto kMaxCollideMoves = 3; constexpr auto kMaxCollideMoves = 4;
constexpr auto kGameMaxPlayers = 32; constexpr auto kGameMaxPlayers = 32;
constexpr auto kGameTeamNum = 2; constexpr auto kGameTeamNum = 2;
constexpr auto kInvalidNodeIndex = -1; constexpr auto kInvalidNodeIndex = -1;

View file

@ -62,7 +62,7 @@ public:
bool findNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false, bool needBotWithC4 = false); bool findNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false, bool needBotWithC4 = false);
// tracing decals for bots spraying logos // tracing decals for bots spraying logos
void decalTrace (entvars_t *pev, TraceResult *trace, int logotypeIndex); void decalTrace (TraceResult *trace, int decalIndex);
// update stats on clients // update stats on clients
void updateClients (); void updateClients ();

View file

@ -53,6 +53,7 @@ struct WeaponInfo {
int id {}; // the weapon id value int id {}; // the weapon id value
StringRef name {}; // name of the weapon when selecting it StringRef name {}; // name of the weapon when selecting it
StringRef model {}; // model name to separate cs weapons StringRef model {}; // model name to separate cs weapons
StringRef alias {}; // alias name for weapon
int price {}; // price when buying int price {}; // price when buying
int minPrimaryAmmo {}; // minimum primary ammo int minPrimaryAmmo {}; // minimum primary ammo
int teamStandard {}; // used by team (number) (standard map) int teamStandard {}; // used by team (number) (standard map)
@ -235,7 +236,6 @@ private:
float m_prevTime {}; // time previously checked movement speed float m_prevTime {}; // time previously checked movement speed
float m_heavyTimestamp {}; // is it time to execute heavy-weight functions float m_heavyTimestamp {}; // is it time to execute heavy-weight functions
float m_prevSpeed {}; // speed some frames before float m_prevSpeed {}; // speed some frames before
float m_prevVelocity {}; // velocity some frames before
float m_timeDoorOpen {}; // time to next door open check float m_timeDoorOpen {}; // time to next door open check
float m_timeHitDoor {}; // specific time after hitting the door float m_timeHitDoor {}; // specific time after hitting the door
float m_lastChatTime {}; // time bot last chatted float m_lastChatTime {}; // time bot last chatted
@ -315,7 +315,6 @@ private:
PathWalk m_pathWalk {}; // pointer to current node from path PathWalk m_pathWalk {}; // pointer to current node from path
Dodge m_dodgeStrafeDir {}; // direction to strafe Dodge m_dodgeStrafeDir {}; // direction to strafe
Dodge m_avoidAction {}; // player avoid action
Fight m_fightStyle {}; // combat style to use Fight m_fightStyle {}; // combat style to use
CollisionState m_collisionState {}; // collision State CollisionState m_collisionState {}; // collision State
FindPath m_pathType {}; // which pathfinder to use FindPath m_pathType {}; // which pathfinder to use
@ -351,6 +350,7 @@ private:
Vector m_breakableOrigin {}; // origin of breakable Vector m_breakableOrigin {}; // origin of breakable
Vector m_rightRef {}; // right referential vector Vector m_rightRef {}; // right referential vector
Vector m_checkFallPoint[2] {}; // check fall point Vector m_checkFallPoint[2] {}; // check fall point
Vector m_prevVelocity {}; // velocity some frames before
Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
Array <edict_t *> m_ignoredItems {}; // list of pointers to entity to ignore for pickup Array <edict_t *> m_ignoredItems {}; // list of pointers to entity to ignore for pickup
@ -651,7 +651,7 @@ public:
int m_voteKickIndex {}; // index of player to vote against int m_voteKickIndex {}; // index of player to vote against
int m_lastVoteKick {}; // last index int m_lastVoteKick {}; // last index
int m_voteMap {}; // number of map to vote for int m_voteMap {}; // number of map to vote for
int m_logotypeIndex {}; // index for logotype int m_logoDecalIndex {}; // index for logotype
int m_buyState {}; // current count in buying int m_buyState {}; // current count in buying
int m_blindButton {}; // buttons bot press, when blind int m_blindButton {}; // buttons bot press, when blind
int m_radioOrder {}; // actual command int m_radioOrder {}; // actual command
@ -930,6 +930,7 @@ extern ConVar cv_camping_time_min;
extern ConVar cv_camping_time_max; extern ConVar cv_camping_time_max;
extern ConVar cv_smoke_grenade_checks; extern ConVar cv_smoke_grenade_checks;
extern ConVar cv_check_darkness; extern ConVar cv_check_darkness;
extern ConVar cv_use_hitbox_enemy_targeting;
extern ConVar mp_freezetime; extern ConVar mp_freezetime;
extern ConVar mp_roundtime; extern ConVar mp_roundtime;

View file

@ -180,6 +180,7 @@ void Bot::checkBreakablesAround () {
|| !cv_destroy_breakables_around || !cv_destroy_breakables_around
|| usesKnife () || usesKnife ()
|| usesSniper () || usesSniper ()
|| isOnLadder ()
|| rg.chance (25) || rg.chance (25)
|| !game.hasBreakables () || !game.hasBreakables ()
|| m_seeEnemyTime + 4.0f > game.time () || m_seeEnemyTime + 4.0f > game.time ()
@ -3161,7 +3162,7 @@ void Bot::checkSpawnConditions () {
// switch to knife if time to do this // switch to knife if time to do this
if (m_checkKnifeSwitch && m_buyingFinished && m_spawnTime + rg (5.0f, 7.5f) < game.time ()) { if (m_checkKnifeSwitch && m_buyingFinished && m_spawnTime + rg (5.0f, 7.5f) < game.time ()) {
if (!game.is (GameFlags::Xash3D) && rg (1, 100) < 2 && cv_spraypaints) { if (rg (1, 100) < 30 && cv_spraypaints) {
startTask (Task::Spraypaint, TaskPri::Spraypaint, kInvalidNodeIndex, game.time () + 1.0f, false); startTask (Task::Spraypaint, TaskPri::Spraypaint, kInvalidNodeIndex, game.time () + 1.0f, false);
} }
@ -3397,7 +3398,8 @@ void Bot::logic () {
// save the previous speed (for checking if stuck) // save the previous speed (for checking if stuck)
m_prevSpeed = cr::abs (m_moveSpeed); m_prevSpeed = cr::abs (m_moveSpeed);
m_prevVelocity = cr::abs (pev->velocity.length2d ()); m_prevVelocity = pev->velocity;
m_lastDamageType = -1; // reset damage m_lastDamageType = -1; // reset damage
} }

View file

@ -235,12 +235,13 @@ void Bot::prepareChatMessage (StringRef message) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent ()) {
continue; continue;
} }
const auto playerIndex = game.indexOfPlayer (client.ent);
if (needsEnemy && m_team != client.team) { if (needsEnemy && m_team != client.team) {
return humanizedName (game.indexOfPlayer (client.ent)); return humanizedName (playerIndex);
} }
else if (!needsEnemy && m_team == client.team) { else if (!needsEnemy && m_team == client.team) {
return humanizedName (game.indexOfPlayer (client.ent)); return humanizedName (playerIndex);
} }
} }
return getHighfragPlayer (); return getHighfragPlayer ();
@ -290,6 +291,9 @@ void Bot::prepareChatMessage (StringRef message) {
case 'e': case 'e':
m_chatBuffer.replace ("%e", getPlayerAlive (true)); m_chatBuffer.replace ("%e", getPlayerAlive (true));
break; break;
case 'g':
m_chatBuffer.replace ("%g", graph.getAuthor ());
}; };
++replaceCounter; ++replaceCounter;
} }

View file

@ -180,7 +180,7 @@ bool Bot::checkBodyParts (edict_t *target) {
} }
// hitboxes requested ? // hitboxes requested ?
if (game.is (GameFlags::HasStudioModels) && cv_use_hitbox_enemy_targeting) { if (game.is (GameFlags::HasStudioModels) && cv_use_hitbox_enemy_targeting && m_hitboxEnumerator) {
return checkBodyPartsWithHitboxes (target); return checkBodyPartsWithHitboxes (target);
} }
return checkBodyPartsWithOffsets (target); return checkBodyPartsWithOffsets (target);

View file

@ -735,19 +735,32 @@ void BotConfig::loadLogosConfig () {
String line {}; String line {};
MemFile file {}; MemFile file {};
auto addLogoIndex = [&] (StringRef logo) {
const auto index = engfuncs.pfnDecalIndex (logo.chars ());
if (index > 0) {
m_logosIndices.push (index);
}
};
m_logosIndices.clear ();
// logos initialization // logos initialization
if (openConfig ("logos", "Logos config file not found. Loading defaults.", &file)) { if (openConfig ("logos", "Logos config file not found. Loading defaults.", &file)) {
m_logos.clear ();
while (file.getLine (line)) { while (file.getLine (line)) {
if (isCommentLine (line)) { if (isCommentLine (line)) {
continue; continue;
} }
m_logos.push (cr::move (line.trim ())); addLogoIndex (line);
} }
} }
else {
m_logos = cr::move (String { "{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r" }.split (";")); // use defaults
if (m_logosIndices.empty ()) {
auto defaults = String { "{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r" }.split (";");
for (const auto &logo : defaults) {
addLogoIndex (logo);
}
} }
} }

View file

@ -1157,7 +1157,7 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) {
m_startAction = BotMsg::None; m_startAction = BotMsg::None;
m_retryJoin = 0; m_retryJoin = 0;
m_moneyAmount = 0; m_moneyAmount = 0;
m_logotypeIndex = conf.getRandomLogoIndex (); m_logoDecalIndex = conf.getRandomLogoDecalIndex ();
if (cv_rotate_bots) { if (cv_rotate_bots) {
m_stayTime = game.time () + rg (cv_rotate_stay_min.as <float> (), cv_rotate_stay_max.as <float> ()); m_stayTime = game.time () + rg (cv_rotate_stay_min.as <float> (), cv_rotate_stay_max.as <float> ());
@ -1496,7 +1496,6 @@ void Bot::newRound () {
m_askCheckTime = rg (30.0f, 90.0f); m_askCheckTime = rg (30.0f, 90.0f);
m_minSpeed = 260.0f; m_minSpeed = 260.0f;
m_prevSpeed = 0.0f; m_prevSpeed = 0.0f;
m_prevVelocity = 0.0f;
m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance); m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance);
m_prevTime = game.time (); m_prevTime = game.time ();
m_lookUpdateTime = game.time (); m_lookUpdateTime = game.time ();
@ -1630,7 +1629,6 @@ void Bot::newRound () {
m_zoomCheckTime = 0.0f; m_zoomCheckTime = 0.0f;
m_strafeSetTime = 0.0f; m_strafeSetTime = 0.0f;
m_dodgeStrafeDir = Dodge::None; m_dodgeStrafeDir = Dodge::None;
m_avoidAction = Dodge::None;
m_fightStyle = Fight::None; m_fightStyle = Fight::None;
m_lastFightStyleCheck = 0.0f; m_lastFightStyleCheck = 0.0f;

View file

@ -530,23 +530,8 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
// found somebody? // found somebody?
if (game.isNullEntity (m_hindrance)) { if (game.isNullEntity (m_hindrance)) {
m_avoidAction = Dodge::None;
return; return;
} }
else {
if (util.getConeDeviation (ent (), m_hindrance->v.origin) < 0.8f) {
return;
}
if (m_avoidAction != Dodge::None) {
if (m_avoidAction == Dodge::Left) {
setStrafeSpeed (normal, pev->maxspeed);
}
else if (m_avoidAction == Dodge::Right) {
setStrafeSpeed (normal, -pev->maxspeed);
}
}
}
const float interval = m_frameInterval * (!isDucking () && pev->velocity.lengthSq2d () > 0.0f ? 6.0f : 2.0f); const float interval = m_frameInterval * (!isDucking () && pev->velocity.lengthSq2d () > 0.0f ? 6.0f : 2.0f);
// use our movement angles, try to predict where we should be next frame // use our movement angles, try to predict where we should be next frame
@ -566,31 +551,20 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
const auto &dir = (pev->origin - m_hindrance->v.origin).normalize2d_apx (); const auto &dir = (pev->origin - m_hindrance->v.origin).normalize2d_apx ();
// to start strafing, we have to first figure out if the target is on the left side or right side // to start strafing, we have to first figure out if the target is on the left side or right side
if ((m_avoidAction == Dodge::None if ((dir | right.normalize2d_apx ()) > 0.0f) {
&& m_path->radius > 16.0f // start strafing
&& !isInNarrowPlace ()) setStrafeSpeed (normal, pev->maxspeed);
|| m_moveSpeed < 0.0f) { }
if ((dir | right.normalize2d_apx ()) > 0.0f) { else {
m_avoidAction = Dodge::Left; // start strafing
setStrafeSpeed (normal, -pev->maxspeed);
// start strafing
setStrafeSpeed (normal, pev->maxspeed);
}
else {
m_avoidAction = Dodge::Right;
// start strafing
setStrafeSpeed (normal, -pev->maxspeed);
}
} }
m_navTimeset = game.time ();
if (distanceSq < cr::sqrf (96.0f)) { if (distanceSq < cr::sqrf (96.0f)) {
if ((dir | forward.normalize2d_apx ()) < 0.0f) { if ((dir | forward.normalize2d_apx ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed; m_moveSpeed = -pev->maxspeed;
} }
} }
ignoreCollision ();
} }
} }
@ -600,10 +574,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
TraceResult tr {}; TraceResult tr {};
m_isStuck = false; m_isStuck = false;
const auto tid = getCurrentTaskId ();
// standing still, no need to check? // standing still, no need to check?
if (m_lastCollTime < game.time () && getCurrentTaskId () != Task::Attack) { if (m_lastCollTime < game.time () && tid != Task::Attack && tid != Task::Camp) {
// didn't we move enough previously? // didn't we move enough previously?
if (movedDistance < kMinMovedDistance && (m_prevSpeed > 20.0f || m_prevVelocity < m_moveSpeed / 2)) { if (movedDistance < kMinMovedDistance && m_prevSpeed > 20.0f) {
m_prevTime = game.time (); // then consider being stuck m_prevTime = game.time (); // then consider being stuck
m_isStuck = true; m_isStuck = true;
@ -881,6 +857,14 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
} }
void Bot::checkFall () { void Bot::checkFall () {
if (isPreviousLadder ()) {
return;
}
else if (graph.exists (m_currentNodeIndex)) {
if (graph[m_currentNodeIndex].flags & NodeFlag::Ladder) {
return;
}
}
if (!m_checkFall) { if (!m_checkFall) {
if (isOnFloor ()) { if (isOnFloor ()) {

View file

@ -103,24 +103,13 @@ bool BotSupport::isVisible (const Vector &origin, edict_t *ent) {
return true; return true;
} }
void BotSupport::decalTrace (entvars_t *pev, TraceResult *trace, int logotypeIndex) { void BotSupport::decalTrace (TraceResult *trace, int decalIndex) {
// this function draw spraypaint depending on the tracing results. // this function draw spraypaint depending on the tracing results.
if (cr::fequal (trace->flFraction, 1.0f)) { if (cr::fequal (trace->flFraction, 1.0f) || decalIndex <= 0) {
return; return;
} }
auto logo = conf.getLogoName (logotypeIndex);
int entityIndex = -1, message = TE_DECAL; int entityIndex = -1, message = TE_DECAL;
int decalIndex = engfuncs.pfnDecalIndex (logo.chars ());
if (decalIndex <= 0) {
decalIndex = engfuncs.pfnDecalIndex ("{lambda06");
}
if (decalIndex <= 0) {
return;
}
if (!game.isNullEntity (trace->pHit)) { if (!game.isNullEntity (trace->pHit)) {
if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) { if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) {
@ -148,32 +137,19 @@ void BotSupport::decalTrace (entvars_t *pev, TraceResult *trace, int logotypeInd
decalIndex -= 256; decalIndex -= 256;
} }
} }
MessageWriter msg {};
if (logo.startsWith ("{")) { msg.start (MSG_BROADCAST, SVC_TEMPENTITY)
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY) .writeByte (message)
.writeByte (TE_PLAYERDECAL) .writeCoord (trace->vecEndPos.x)
.writeByte (game.indexOfEntity (pev->pContainingEntity)) .writeCoord (trace->vecEndPos.y)
.writeCoord (trace->vecEndPos.x) .writeCoord (trace->vecEndPos.z)
.writeCoord (trace->vecEndPos.y) .writeByte (decalIndex);
.writeCoord (trace->vecEndPos.z)
.writeShort (static_cast <short> (game.indexOfEntity (trace->pHit))) if (entityIndex) {
.writeByte (decalIndex); msg.writeShort (entityIndex);
}
else {
MessageWriter msg {};
msg.start (MSG_BROADCAST, SVC_TEMPENTITY)
.writeByte (message)
.writeCoord (trace->vecEndPos.x)
.writeCoord (trace->vecEndPos.y)
.writeCoord (trace->vecEndPos.z)
.writeByte (decalIndex);
if (entityIndex) {
msg.writeShort (entityIndex);
}
msg.end ();
} }
msg.end ();
} }
bool BotSupport::isPlayer (edict_t *ent) { bool BotSupport::isPlayer (edict_t *ent) {

View file

@ -99,11 +99,10 @@ void Bot::normal_ () {
// spray logo sometimes if allowed to do so // spray logo sometimes if allowed to do so
if (!(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) if (!(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))
&& m_seeEnemyTime + 5.0f < game.time () && m_seeEnemyTime + 5.0f < game.time ()
&& !m_reloadState && m_timeLogoSpray < game.time () && m_reloadState == Reload::None
&& !game.is (GameFlags::Xash3D) && m_timeLogoSpray < game.time ()
&& cv_spraypaints && cv_spraypaints
&& rg.chance (50) && m_moveSpeed >= getShiftSpeed ()
&& m_moveSpeed > getShiftSpeed ()
&& game.isNullEntity (m_pickupItem)) { && game.isNullEntity (m_pickupItem)) {
if (!(game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_team == Team::CT)) { if (!(game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_team == Team::CT)) {
@ -316,7 +315,7 @@ void Bot::spraypaint_ () {
m_aimFlags |= AimFlags::Entity; m_aimFlags |= AimFlags::Entity;
// bot didn't spray this round? // bot didn't spray this round?
if (m_timeLogoSpray < game.time () && getTask ()->time > game.time ()) { if (m_timeLogoSpray <= game.time () && getTask ()->time > game.time ()) {
const auto &forward = pev->v_angle.forward (); const auto &forward = pev->v_angle.forward ();
Vector sprayOrigin = getEyesPos () + forward * 128.0f; Vector sprayOrigin = getEyesPos () + forward * 128.0f;
@ -331,11 +330,12 @@ void Bot::spraypaint_ () {
if (getTask ()->time - 0.5f < game.time ()) { if (getTask ()->time - 0.5f < game.time ()) {
// emit spray can sound // emit spray can sound
engfuncs.pfnEmitSound (ent (), CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100); engfuncs.pfnEmitSound (pev->pContainingEntity, CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100);
game.testLine (getEyesPos (), getEyesPos () + forward * 128.0f, TraceIgnore::Monsters, ent (), &tr); game.testLine (getEyesPos (), getEyesPos () + forward * 128.0f, TraceIgnore::Monsters, ent (), &tr);
// paint the actual logo decal // paint the actual logo decal
util.decalTrace (pev, &tr, m_logotypeIndex); util.decalTrace (&tr, m_logoDecalIndex);
m_timeLogoSpray = game.time () + rg (60.0f, 90.0f); m_timeLogoSpray = game.time () + rg (60.0f, 90.0f);
} }
} }