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

View file

@ -439,12 +439,12 @@ constexpr auto kSprayDistanceX2 = kSprayDistance * 2;
constexpr auto kMaxChatterRepeatInterval = 99.0f;
constexpr auto kViewFrameUpdate = 1.0f / 25.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 kMaxWeapons = 32;
constexpr auto kNumWeapons = 26;
constexpr auto kMaxCollideMoves = 3;
constexpr auto kMaxCollideMoves = 4;
constexpr auto kGameMaxPlayers = 32;
constexpr auto kGameTeamNum = 2;
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);
// 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
void updateClients ();

View file

@ -53,6 +53,7 @@ struct WeaponInfo {
int id {}; // the weapon id value
StringRef name {}; // name of the weapon when selecting it
StringRef model {}; // model name to separate cs weapons
StringRef alias {}; // alias name for weapon
int price {}; // price when buying
int minPrimaryAmmo {}; // minimum primary ammo
int teamStandard {}; // used by team (number) (standard map)
@ -235,7 +236,6 @@ private:
float m_prevTime {}; // time previously checked movement speed
float m_heavyTimestamp {}; // is it time to execute heavy-weight functions
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_timeHitDoor {}; // specific time after hitting the door
float m_lastChatTime {}; // time bot last chatted
@ -315,7 +315,6 @@ private:
PathWalk m_pathWalk {}; // pointer to current node from path
Dodge m_dodgeStrafeDir {}; // direction to strafe
Dodge m_avoidAction {}; // player avoid action
Fight m_fightStyle {}; // combat style to use
CollisionState m_collisionState {}; // collision State
FindPath m_pathType {}; // which pathfinder to use
@ -351,6 +350,7 @@ private:
Vector m_breakableOrigin {}; // origin of breakable
Vector m_rightRef {}; // right referential vector
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_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_lastVoteKick {}; // last index
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_blindButton {}; // buttons bot press, when blind
int m_radioOrder {}; // actual command
@ -930,6 +930,7 @@ extern ConVar cv_camping_time_min;
extern ConVar cv_camping_time_max;
extern ConVar cv_smoke_grenade_checks;
extern ConVar cv_check_darkness;
extern ConVar cv_use_hitbox_enemy_targeting;
extern ConVar mp_freezetime;
extern ConVar mp_roundtime;

View file

@ -180,6 +180,7 @@ void Bot::checkBreakablesAround () {
|| !cv_destroy_breakables_around
|| usesKnife ()
|| usesSniper ()
|| isOnLadder ()
|| rg.chance (25)
|| !game.hasBreakables ()
|| m_seeEnemyTime + 4.0f > game.time ()
@ -3161,7 +3162,7 @@ void Bot::checkSpawnConditions () {
// switch to knife if time to do this
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);
}
@ -3397,7 +3398,8 @@ void Bot::logic () {
// save the previous speed (for checking if stuck)
m_prevSpeed = cr::abs (m_moveSpeed);
m_prevVelocity = cr::abs (pev->velocity.length2d ());
m_prevVelocity = pev->velocity;
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 ()) {
continue;
}
const auto playerIndex = game.indexOfPlayer (client.ent);
if (needsEnemy && m_team != client.team) {
return humanizedName (game.indexOfPlayer (client.ent));
return humanizedName (playerIndex);
}
else if (!needsEnemy && m_team == client.team) {
return humanizedName (game.indexOfPlayer (client.ent));
return humanizedName (playerIndex);
}
}
return getHighfragPlayer ();
@ -290,6 +291,9 @@ void Bot::prepareChatMessage (StringRef message) {
case 'e':
m_chatBuffer.replace ("%e", getPlayerAlive (true));
break;
case 'g':
m_chatBuffer.replace ("%g", graph.getAuthor ());
};
++replaceCounter;
}

View file

@ -180,7 +180,7 @@ bool Bot::checkBodyParts (edict_t *target) {
}
// 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 checkBodyPartsWithOffsets (target);

View file

@ -735,19 +735,32 @@ void BotConfig::loadLogosConfig () {
String line {};
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
if (openConfig ("logos", "Logos config file not found. Loading defaults.", &file)) {
m_logos.clear ();
while (file.getLine (line)) {
if (isCommentLine (line)) {
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_retryJoin = 0;
m_moneyAmount = 0;
m_logotypeIndex = conf.getRandomLogoIndex ();
m_logoDecalIndex = conf.getRandomLogoDecalIndex ();
if (cv_rotate_bots) {
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_minSpeed = 260.0f;
m_prevSpeed = 0.0f;
m_prevVelocity = 0.0f;
m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance);
m_prevTime = game.time ();
m_lookUpdateTime = game.time ();
@ -1630,7 +1629,6 @@ void Bot::newRound () {
m_zoomCheckTime = 0.0f;
m_strafeSetTime = 0.0f;
m_dodgeStrafeDir = Dodge::None;
m_avoidAction = Dodge::None;
m_fightStyle = Fight::None;
m_lastFightStyleCheck = 0.0f;

View file

@ -530,23 +530,8 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
// found somebody?
if (game.isNullEntity (m_hindrance)) {
m_avoidAction = Dodge::None;
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);
// 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 ();
// 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
&& m_path->radius > 16.0f
&& !isInNarrowPlace ())
|| m_moveSpeed < 0.0f) {
if ((dir | right.normalize2d_apx ()) > 0.0f) {
m_avoidAction = Dodge::Left;
// 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 ((dir | forward.normalize2d_apx ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed;
}
}
ignoreCollision ();
}
}
@ -600,10 +574,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
TraceResult tr {};
m_isStuck = false;
const auto tid = getCurrentTaskId ();
// 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?
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_isStuck = true;
@ -881,6 +857,14 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
}
void Bot::checkFall () {
if (isPreviousLadder ()) {
return;
}
else if (graph.exists (m_currentNodeIndex)) {
if (graph[m_currentNodeIndex].flags & NodeFlag::Ladder) {
return;
}
}
if (!m_checkFall) {
if (isOnFloor ()) {

View file

@ -103,24 +103,13 @@ bool BotSupport::isVisible (const Vector &origin, edict_t *ent) {
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.
if (cr::fequal (trace->flFraction, 1.0f)) {
if (cr::fequal (trace->flFraction, 1.0f) || decalIndex <= 0) {
return;
}
auto logo = conf.getLogoName (logotypeIndex);
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 (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) {
@ -148,18 +137,6 @@ void BotSupport::decalTrace (entvars_t *pev, TraceResult *trace, int logotypeInd
decalIndex -= 256;
}
}
if (logo.startsWith ("{")) {
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY)
.writeByte (TE_PLAYERDECAL)
.writeByte (game.indexOfEntity (pev->pContainingEntity))
.writeCoord (trace->vecEndPos.x)
.writeCoord (trace->vecEndPos.y)
.writeCoord (trace->vecEndPos.z)
.writeShort (static_cast <short> (game.indexOfEntity (trace->pHit)))
.writeByte (decalIndex);
}
else {
MessageWriter msg {};
msg.start (MSG_BROADCAST, SVC_TEMPENTITY)
@ -173,7 +150,6 @@ void BotSupport::decalTrace (entvars_t *pev, TraceResult *trace, int logotypeInd
msg.writeShort (entityIndex);
}
msg.end ();
}
}
bool BotSupport::isPlayer (edict_t *ent) {

View file

@ -99,11 +99,10 @@ void Bot::normal_ () {
// spray logo sometimes if allowed to do so
if (!(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))
&& m_seeEnemyTime + 5.0f < game.time ()
&& !m_reloadState && m_timeLogoSpray < game.time ()
&& !game.is (GameFlags::Xash3D)
&& m_reloadState == Reload::None
&& m_timeLogoSpray < game.time ()
&& cv_spraypaints
&& rg.chance (50)
&& m_moveSpeed > getShiftSpeed ()
&& m_moveSpeed >= getShiftSpeed ()
&& game.isNullEntity (m_pickupItem)) {
if (!(game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_team == Team::CT)) {
@ -316,7 +315,7 @@ void Bot::spraypaint_ () {
m_aimFlags |= AimFlags::Entity;
// 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 ();
Vector sprayOrigin = getEyesPos () + forward * 128.0f;
@ -331,11 +330,12 @@ void Bot::spraypaint_ () {
if (getTask ()->time - 0.5f < game.time ()) {
// 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);
// 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);
}
}