adding some states again for following or mimicking movement, also with coordinates to know if coming too far off course
This commit is contained in:
parent
e3cef31246
commit
07d344e730
@ -3,32 +3,78 @@
|
||||
#define DEBUG
|
||||
|
||||
#define PLUGIN_AUTHOR "jenz"
|
||||
#define PLUGIN_VERSION "1.9"
|
||||
#define PLUGIN_VERSION "2.2"
|
||||
#define generic_length 256
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <AFKManager>
|
||||
#include <sdkhooks>
|
||||
#include <PlayerManager>
|
||||
#include <cstrike>
|
||||
#include <socket>
|
||||
#include <UNLOZE.secret>
|
||||
#include <mapchooser_extended>
|
||||
#include <smlib>
|
||||
|
||||
int target_client[5]; //4 autism bots.
|
||||
|
||||
#define BUFFER_SIZE 198 // ~2 seconds at 66 tick
|
||||
// ~5.3s of history at 66 tick. Plenty of headroom for approach + backlog +
|
||||
// resync cycles. At ~48 bytes/frame this is only a few hundred KB total.
|
||||
#define BUFFER_SIZE 350
|
||||
|
||||
enum struct FrameData {
|
||||
int buttons;
|
||||
float vel[3];
|
||||
float angles[3];
|
||||
float origin[3]; // where the real client actually was when this frame was recorded
|
||||
int impulse;
|
||||
int weaponSlot; // 0=none/untracked, 1=primary, 2=secondary, 3=knife, 4=grenade
|
||||
}
|
||||
|
||||
FrameData g_Buffer[MAXPLAYERS+1][BUFFER_SIZE];
|
||||
int g_WriteHead[MAXPLAYERS+1]; // where were writing next
|
||||
int g_FrameCount[MAXPLAYERS+1]; // total frames captured
|
||||
int g_DelayTicks = 128; // delay
|
||||
int g_WriteHead[MAXPLAYERS+1]; // where were writing next, indexed by REAL client
|
||||
int g_FrameCount[MAXPLAYERS+1]; // total frames captured, indexed by REAL client
|
||||
|
||||
// --- movement state machine (bots only) ---
|
||||
#define BOT_STATE_IDLE 0
|
||||
#define BOT_STATE_APPROACH 1
|
||||
#define BOT_STATE_MIMIC 2
|
||||
|
||||
#define ARRIVE_RADIUS 72.0 // horizontal distance to anchor that counts as "arrived"
|
||||
#define ARRIVE_Z_SLACK 128.0 // max height difference to anchor that still counts as "arrived"
|
||||
#define APPROACH_SPEED 250.0 // forward move value while walking to the anchor
|
||||
|
||||
// Must stay comfortably bigger than ARRIVE_RADIUS or the bot can flap
|
||||
// between MIMIC and APPROACH right at the boundary.
|
||||
#define DESYNC_RADIUS 250.0 // if actual pos strays this far from the frame its about to play, resync
|
||||
|
||||
// Anti-loop / anti-stuck tuning
|
||||
#define RESYNC_COOLDOWN 3.0 // min seconds between two resyncs; drift during cooldown just keeps mimicking
|
||||
#define MAX_RESYNC_STRIKES 3 // this many resyncs on the same target -> give up, pick a new target
|
||||
#define STUCK_CHECK_INTERVAL 4.0 // seconds between stuck checks while approaching
|
||||
#define STUCK_MOVE_EPSILON 50.0 // if the bot moved less than this between checks, it counts as stuck
|
||||
#define MAX_STUCK_STRIKES 2 // first strike re-anchors + jumps, second strike drops the target
|
||||
|
||||
int g_BotState[MAXPLAYERS+1]; // indexed by BOT client
|
||||
float g_AnchorPos[MAXPLAYERS+1][3]; // indexed by BOT client - the single fixed point it walks to
|
||||
int g_ReadHead[MAXPLAYERS+1]; // indexed by BOT client - read index into g_Buffer[target][]
|
||||
int g_LastWeaponSlot[MAXPLAYERS+1]; // indexed by BOT client - last weaponSlot category we told it to switch to
|
||||
|
||||
// Anti-loop / anti-stuck state, all indexed by BOT client
|
||||
float g_LastResyncTime[MAXPLAYERS+1]; // GetGameTime() of the last drift-triggered resync
|
||||
int g_ResyncStrikes[MAXPLAYERS+1]; // consecutive resyncs against the current target
|
||||
float g_StuckCheckTime[MAXPLAYERS+1]; // GetGameTime() of the last stuck check
|
||||
float g_StuckCheckPos[MAXPLAYERS+1][3]; // where the bot was at the last stuck check
|
||||
int g_StuckStrikes[MAXPLAYERS+1]; // consecutive stuck detections while approaching
|
||||
bool g_WantJump[MAXPLAYERS+1]; // one-shot: press jump on the next approach tick (unstick aid)
|
||||
|
||||
// Weapon category is cheap to get slightly stale, so we only actually
|
||||
// re-check it every WEAPON_CHECK_INTERVAL seconds per real client instead of
|
||||
// every tick, and just keep writing the cached value into each frame.
|
||||
#define WEAPON_CHECK_INTERVAL 5.0
|
||||
int g_CachedWeaponSlot[MAXPLAYERS+1]; // indexed by REAL client - last computed category
|
||||
float g_NextWeaponCheck[MAXPLAYERS+1]; // indexed by REAL client - GetGameTime() of next allowed check
|
||||
|
||||
int ports[7] = {48470, 48471, 48472, 48473, 48474, 48479, 48480}; //last three indexes are ports its sending udp from in plugin
|
||||
int server_ports[2] = {27015, 27035}; //server ports: ze, ze2
|
||||
@ -51,7 +97,7 @@ public Plugin myinfo =
|
||||
{
|
||||
name = "autism bot simulate player movement",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "just copies player movement to the autism bots",
|
||||
description = "just copies player movement to the autism bots. now also AI slopped.",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
@ -61,6 +107,7 @@ public void OnPluginStart()
|
||||
//talking
|
||||
RegConsoleCmd("sm_autism", cmd_talk, "talking to the bot through java application");
|
||||
RegConsoleCmd("sm_botrtv", cmd_botrtv, "making bots rtv");
|
||||
RegAdminCmd("sm_botstate", cmd_botstate, ADMFLAG_GENERIC, "debug: show state of each autism bot");
|
||||
|
||||
HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
|
||||
|
||||
@ -131,6 +178,29 @@ public void OnPluginEnd()
|
||||
}
|
||||
}
|
||||
|
||||
// Classifies the clients currently held weapon into a slot category. This
|
||||
// servers loadout is fixed: raw inventory slot 0=primary, 1=secondary,
|
||||
// 2=knife, 3=HE grenade (no smoke/flash, no slot 4 ever exists), so we can
|
||||
// just compare the active weapon against each fixed slot directly.
|
||||
stock int GetActiveWeaponSlotCategory(int client)
|
||||
{
|
||||
int activeWeapon = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon", 0);
|
||||
if (activeWeapon <= -1 || !IsValidEntity(activeWeapon))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int rawSlot = 0; rawSlot <= 3; rawSlot++)
|
||||
{
|
||||
if (activeWeapon == GetPlayerWeaponSlot(client, rawSlot))
|
||||
{
|
||||
return rawSlot + 1; // 1=primary, 2=secondary, 3=knife, 4=grenade
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2])
|
||||
{
|
||||
if (!IsValidClient(client) || IsFakeClient(client))
|
||||
@ -142,9 +212,129 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float
|
||||
g_Buffer[client][head].impulse = impulse;
|
||||
g_Buffer[client][head].vel = vel;
|
||||
g_Buffer[client][head].angles = angles;
|
||||
GetClientAbsOrigin(client, g_Buffer[client][head].origin);
|
||||
|
||||
float now = GetGameTime();
|
||||
if (now >= g_NextWeaponCheck[client])
|
||||
{
|
||||
g_CachedWeaponSlot[client] = GetActiveWeaponSlotCategory(client);
|
||||
g_NextWeaponCheck[client] = now + WEAPON_CHECK_INTERVAL;
|
||||
}
|
||||
g_Buffer[client][head].weaponSlot = g_CachedWeaponSlot[client];
|
||||
|
||||
g_WriteHead[client] = (head + 1) % BUFFER_SIZE;
|
||||
g_FrameCount[client]++;
|
||||
|
||||
// If any bot currently mimicking/approaching this client is about to have
|
||||
// its unread backlog overwritten by this write, nudge its read head
|
||||
// forward so it never reads stale/overwritten frames. With 4 bots max
|
||||
// this loop is cheap.
|
||||
for (int slot = 1; slot <= 4; slot++)
|
||||
{
|
||||
if (target_client[slot] != client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int bot = GetBotClientForSlot(slot);
|
||||
if (bot == -1 || g_BotState[bot] == BOT_STATE_IDLE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (g_ReadHead[bot] == g_WriteHead[client])
|
||||
{
|
||||
g_ReadHead[bot] = (g_ReadHead[bot] + 1) % BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal (XY) distance between two points. The old 3D check made bots
|
||||
// standing directly under/over an anchor on another floor "never arrive",
|
||||
// which also produced the crazy spinning (near-vertical direction vectors
|
||||
// have unstable yaw).
|
||||
stock float GetHorizontalDistance(const float a[3], const float b[3])
|
||||
{
|
||||
float dx = a[0] - b[0];
|
||||
float dy = a[1] - b[1];
|
||||
return SquareRoot(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
// Faces the bot toward `anchor` (yaw from horizontal components only, so a
|
||||
// mostly-vertical offset cant destabilize it) and pushes it forward. Shared
|
||||
// by the normal APPROACH state and by the mid-mimic resync path below.
|
||||
void ApplyApproachMovement(int bot, const float anchor[3], int &buttons, int &impulse, float vel[3], float angles[3])
|
||||
{
|
||||
float botPos[3];
|
||||
GetClientAbsOrigin(bot, botPos);
|
||||
|
||||
float dir[3];
|
||||
SubtractVectors(anchor, botPos, dir);
|
||||
dir[2] = 0.0; // yaw only - never derive facing from the vertical component
|
||||
|
||||
impulse = 0;
|
||||
vel[1] = 0.0;
|
||||
vel[2] = 0.0;
|
||||
angles[0] = 0.0;
|
||||
angles[2] = 0.0;
|
||||
|
||||
if (GetVectorLength(dir) < 1.0)
|
||||
{
|
||||
// Horizontally on top of the anchor but not "arrived" (must be a
|
||||
// height mismatch). Running forward would just grind into geometry
|
||||
// and the yaw would be garbage - hold position, keep current yaw,
|
||||
// and let stuck detection deal with it.
|
||||
buttons = 0;
|
||||
vel[0] = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
float faceAngles[3];
|
||||
GetVectorAngles(dir, faceAngles);
|
||||
angles[1] = faceAngles[1];
|
||||
|
||||
buttons = IN_FORWARD;
|
||||
vel[0] = APPROACH_SPEED;
|
||||
|
||||
if (g_WantJump[bot])
|
||||
{
|
||||
buttons |= IN_JUMP;
|
||||
g_WantJump[bot] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Directly sets the bots active weapon to match the category the target is
|
||||
// holding. Fixed slot layout: category 1-4 maps straight to raw inventory
|
||||
// slot 0-3 (primary, secondary, knife, HE grenade). Uses smlibs
|
||||
// Client_SetActiveWeapon, which manipulates the weapon state via SDK calls
|
||||
// rather than a queued client command - this is why it can be called
|
||||
// synchronously from inside OnPlayerRunCmd without the reentrancy problems
|
||||
// a FakeClientCommandEx("slotN") had.
|
||||
void SyncWeaponSlot(int bot, int wantSlot)
|
||||
{
|
||||
if (wantSlot == 0 || wantSlot == g_LastWeaponSlot[bot])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int weapon = GetPlayerWeaponSlot(bot, wantSlot - 1);
|
||||
if (weapon != -1 && Weapon_IsValid(weapon))
|
||||
{
|
||||
Client_SetActiveWeapon(bot, weapon);
|
||||
}
|
||||
|
||||
g_LastWeaponSlot[bot] = wantSlot;
|
||||
}
|
||||
|
||||
// Drops the bots current target entirely and goes idle. check_team runs
|
||||
// every 2 seconds and will hand it a fresh target (possibly the same player,
|
||||
// but with a brand-new anchor and clean strike counters).
|
||||
void GiveUpOnTarget(int bot)
|
||||
{
|
||||
int slot = specific_bot_player[bot];
|
||||
if (slot != -1)
|
||||
{
|
||||
target_client[slot] = -1;
|
||||
}
|
||||
g_BotState[bot] = BOT_STATE_IDLE;
|
||||
}
|
||||
|
||||
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2])
|
||||
@ -154,44 +344,228 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
||||
return Plugin_Continue;
|
||||
}
|
||||
int target = target_client[specific_bot_player[client]];
|
||||
// Read from the slot that is DelayTicks behind current write head
|
||||
int readSlot = (g_WriteHead[target] - g_DelayTicks + BUFFER_SIZE) % BUFFER_SIZE;
|
||||
float now = GetGameTime();
|
||||
|
||||
buttons = g_Buffer[target][readSlot].buttons;
|
||||
impulse = g_Buffer[target][readSlot].impulse;
|
||||
vel = g_Buffer[target][readSlot].vel;
|
||||
angles = g_Buffer[target][readSlot].angles;
|
||||
if (g_BotState[client] == BOT_STATE_APPROACH)
|
||||
{
|
||||
float botPos[3];
|
||||
GetClientAbsOrigin(client, botPos);
|
||||
|
||||
// Anti-stuck: if the bot has barely moved since the last check, its
|
||||
// wedged on geometry. First strike: re-anchor to the targets CURRENT
|
||||
// position (theyve moved on; the fresh anchor may have a clear path)
|
||||
// and hop to clear small ledges. Second strike: give up on this
|
||||
// target and let check_team assign a new one.
|
||||
if (now - g_StuckCheckTime[client] >= STUCK_CHECK_INTERVAL)
|
||||
{
|
||||
if (GetVectorDistance(botPos, g_StuckCheckPos[client]) < STUCK_MOVE_EPSILON)
|
||||
{
|
||||
g_StuckStrikes[client]++;
|
||||
if (g_StuckStrikes[client] >= MAX_STUCK_STRIKES)
|
||||
{
|
||||
GiveUpOnTarget(client);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
GetClientAbsOrigin(target, g_AnchorPos[client]);
|
||||
g_ReadHead[client] = g_WriteHead[target];
|
||||
g_WantJump[client] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_StuckStrikes[client] = 0;
|
||||
}
|
||||
g_StuckCheckPos[client] = botPos;
|
||||
g_StuckCheckTime[client] = now;
|
||||
}
|
||||
|
||||
// Arrival is judged horizontally with generous height slack. A pure
|
||||
// 3D radius made anchors on other floors unreachable forever.
|
||||
if (GetHorizontalDistance(botPos, g_AnchorPos[client]) <= ARRIVE_RADIUS
|
||||
&& FloatAbs(botPos[2] - g_AnchorPos[client][2]) <= ARRIVE_Z_SLACK)
|
||||
{
|
||||
// arrived - start draining whatever the target did while we were walking here
|
||||
g_BotState[client] = BOT_STATE_MIMIC;
|
||||
g_StuckStrikes[client] = 0;
|
||||
// fall through into mimic logic below on this same tick
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyApproachMovement(client, g_AnchorPos[client], buttons, impulse, vel, angles);
|
||||
return Plugin_Changed;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_BotState[client] != BOT_STATE_MIMIC)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
// Drift check: compare where the bot actually is against where the
|
||||
// target actually was for the frame were about to play. If its too
|
||||
// far off (missed a knockback, got stuck on geometry mid-mimic, etc.),
|
||||
// drop back into APPROACH toward the targets current live position
|
||||
// instead of continuing to mimic from a wrong spot.
|
||||
//
|
||||
// Two guards against the approach<->mimic ping-pong loop:
|
||||
// - a cooldown, so drift during the cooldown window just keeps
|
||||
// mimicking instead of instantly re-approaching
|
||||
// - a strike counter, so a target whose replay keeps desyncing on the
|
||||
// same geometry gets dropped entirely instead of looping forever
|
||||
if (g_ReadHead[client] != g_WriteHead[target])
|
||||
{
|
||||
int nextSlot = g_ReadHead[client];
|
||||
float botPos[3];
|
||||
GetClientAbsOrigin(client, botPos);
|
||||
|
||||
if (GetVectorDistance(botPos, g_Buffer[target][nextSlot].origin) > DESYNC_RADIUS
|
||||
&& now - g_LastResyncTime[client] >= RESYNC_COOLDOWN)
|
||||
{
|
||||
g_ResyncStrikes[client]++;
|
||||
if (g_ResyncStrikes[client] >= MAX_RESYNC_STRIKES)
|
||||
{
|
||||
GiveUpOnTarget(client);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
g_LastResyncTime[client] = now;
|
||||
GetClientAbsOrigin(target, g_AnchorPos[client]);
|
||||
g_BotState[client] = BOT_STATE_APPROACH;
|
||||
g_ReadHead[client] = g_WriteHead[target]; // drop the stale backlog, start fresh once we arrive
|
||||
|
||||
// reset the stuck tracker for the fresh approach leg
|
||||
g_StuckStrikes[client] = 0;
|
||||
g_StuckCheckTime[client] = now;
|
||||
GetClientAbsOrigin(client, g_StuckCheckPos[client]);
|
||||
|
||||
ApplyApproachMovement(client, g_AnchorPos[client], buttons, impulse, vel, angles);
|
||||
return Plugin_Changed;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ReadHead[client] == g_WriteHead[target])
|
||||
{
|
||||
// fully drained - nothing new recorded yet, hold still until more frames arrive
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
int slot = g_ReadHead[client];
|
||||
buttons = g_Buffer[target][slot].buttons;
|
||||
impulse = g_Buffer[target][slot].impulse;
|
||||
vel = g_Buffer[target][slot].vel;
|
||||
angles = g_Buffer[target][slot].angles;
|
||||
|
||||
SyncWeaponSlot(client, g_Buffer[target][slot].weaponSlot);
|
||||
|
||||
// "cleans them out after processing" - advancing the read head past this
|
||||
// slot means it will never be handed out again, which is the queue
|
||||
// equivalent of clearing it.
|
||||
g_ReadHead[client] = (slot + 1) % BUFFER_SIZE;
|
||||
|
||||
return Plugin_Changed;
|
||||
}
|
||||
|
||||
// Looks up which bot client (if any) currently occupies a given bot slot (1-4).
|
||||
int GetBotClientForSlot(int slot)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && specific_bot_player[i] == slot)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Assigns `target` as the client that `bot` should mimic. Captures a single,
|
||||
// fixed anchor point (the targets position right now) for the bot to walk
|
||||
// to, and marks the point in the targets buffer where recording of the
|
||||
// "walk-over" backlog begins. No teleport happens here.
|
||||
void SetBotTarget(int bot, int target)
|
||||
{
|
||||
int slot = specific_bot_player[bot];
|
||||
if (slot == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target_client[slot] = target;
|
||||
g_LastWeaponSlot[bot] = 0; // force weapon re-evaluation once mimicking starts
|
||||
|
||||
// clean slate for the anti-loop/anti-stuck trackers
|
||||
g_ResyncStrikes[bot] = 0;
|
||||
g_StuckStrikes[bot] = 0;
|
||||
g_LastResyncTime[bot] = 0.0;
|
||||
g_StuckCheckTime[bot] = GetGameTime();
|
||||
g_WantJump[bot] = false;
|
||||
if (IsValidClient(bot))
|
||||
{
|
||||
GetClientAbsOrigin(bot, g_StuckCheckPos[bot]);
|
||||
}
|
||||
|
||||
if (!IsValidClient(target))
|
||||
{
|
||||
g_BotState[bot] = BOT_STATE_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
GetClientAbsOrigin(target, g_AnchorPos[bot]);
|
||||
g_BotState[bot] = BOT_STATE_APPROACH;
|
||||
g_ReadHead[bot] = g_WriteHead[target]; // everything the target does from now on gets replayed once we arrive
|
||||
}
|
||||
|
||||
// Debug command: prints each bots state machine status so loops/stuck
|
||||
// situations can be diagnosed live instead of guessed at.
|
||||
public Action cmd_botstate(int client, int args)
|
||||
{
|
||||
static const char stateNames[][] = { "IDLE", "APPROACH", "MIMIC" };
|
||||
bool found = false;
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsValidClient(i) || specific_bot_player[i] == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
int slot = specific_bot_player[i];
|
||||
int target = target_client[slot];
|
||||
int backlog = 0;
|
||||
char targetName[64];
|
||||
strcopy(targetName, sizeof(targetName), "<none>");
|
||||
if (IsValidClient(target))
|
||||
{
|
||||
GetClientName(target, targetName, sizeof(targetName));
|
||||
backlog = (g_WriteHead[target] - g_ReadHead[i] + BUFFER_SIZE) % BUFFER_SIZE;
|
||||
}
|
||||
ReplyToCommand(client,
|
||||
"[bot %d] %N | state=%s | target=%s | backlog=%d frames | resync_strikes=%d | stuck_strikes=%d",
|
||||
slot, i, stateNames[g_BotState[i]], targetName, backlog,
|
||||
g_ResyncStrikes[i], g_StuckStrikes[i]);
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
ReplyToCommand(client, "no autism bots currently connected");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
target_client[1] = -1;
|
||||
target_client[2] = -1;
|
||||
target_client[3] = -1;
|
||||
target_client[4] = -1;
|
||||
|
||||
|
||||
chat_cooldown = false;
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && IsPlayerAlive(i) && specific_bot_player[i] != -1)
|
||||
{
|
||||
for (int j = 1; j <= MaxClients; j++)
|
||||
{
|
||||
if (IsValidClient(j) && GetClientTeam(j) == GetClientTeam(i) && IsPlayerAlive(j) && GetClientIdleTime(j) < 20)
|
||||
{
|
||||
if (target_client[1] == j || target_client[2] == j || target_client[3] == j || target_client[4])
|
||||
{
|
||||
continue; //one of the autism bots already got this dude as their target.
|
||||
}
|
||||
float vec[3];
|
||||
GetClientAbsOrigin(j, vec);
|
||||
TeleportEntity(i, vec, NULL_VECTOR, NULL_VECTOR);
|
||||
target_client[specific_bot_player[i]] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_BotState[i] = BOT_STATE_IDLE;
|
||||
g_LastWeaponSlot[i] = 0;
|
||||
g_ResyncStrikes[i] = 0;
|
||||
g_StuckStrikes[i] = 0;
|
||||
g_WantJump[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +919,15 @@ public Action check_team(Handle timer, any data)
|
||||
{
|
||||
if (IsPlayerAlive(client))
|
||||
{
|
||||
target_client[specific_bot_player[client]] = GetClosestClient_option1(client);
|
||||
int newTarget = GetClosestClient_option1(client);
|
||||
if (newTarget != target_client[specific_bot_player[client]])
|
||||
{
|
||||
// only reset the walk-over/mimic state when the target
|
||||
// actually changes - otherwise a bot re-targeting the
|
||||
// same player every 2 seconds would keep getting yanked
|
||||
// back into APPROACH state
|
||||
SetBotTarget(client, newTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -603,7 +985,7 @@ public int GetClosestClient_option1(int autism_client)
|
||||
{
|
||||
if (IsValidClient(i) && !IsFakeClient(i) && IsPlayerAlive(i) && i != autism_client)
|
||||
{
|
||||
if (GetClientIdleTime(i) > 20 || GetClientTeam(autism_client) != GetClientTeam(i))
|
||||
if (GetClientIdleTime(i) > 3 || GetClientTeam(autism_client) != GetClientTeam(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -636,11 +1018,19 @@ public float get_power_distance(int target_player, float pos[3])
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
specific_bot_player[client] = -1;
|
||||
g_BotState[client] = BOT_STATE_IDLE;
|
||||
g_LastWeaponSlot[client] = 0;
|
||||
g_NextWeaponCheck[client] = 0.0; // force an immediate recheck rather than waiting out a stale timer
|
||||
g_ResyncStrikes[client] = 0;
|
||||
g_StuckStrikes[client] = 0;
|
||||
g_WantJump[client] = false;
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
specific_bot_player[client] = -1;
|
||||
g_BotState[client] = BOT_STATE_IDLE;
|
||||
g_LastWeaponSlot[client] = 0;
|
||||
is_autism_bot1(client);
|
||||
is_autism_bot2(client);
|
||||
is_autism_bot3(client);
|
||||
@ -693,6 +1083,24 @@ stock void connect_socket()
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
specific_bot_player[client] = -1;
|
||||
g_BotState[client] = BOT_STATE_IDLE;
|
||||
g_LastWeaponSlot[client] = 0;
|
||||
|
||||
// if this client was somebodys target, put that bot back to idle so it
|
||||
// doesnt keep trying to read/approach a client thats gone
|
||||
for (int slot = 1; slot <= 4; slot++)
|
||||
{
|
||||
if (target_client[slot] == client)
|
||||
{
|
||||
target_client[slot] = -1;
|
||||
int bot = GetBotClientForSlot(slot);
|
||||
if (bot != -1)
|
||||
{
|
||||
g_BotState[bot] = BOT_STATE_IDLE;
|
||||
g_LastWeaponSlot[bot] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Socket callback
|
||||
@ -717,3 +1125,4 @@ public Action TimerConnect(Handle timer, any arg)
|
||||
connect_socket();
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user