Update GW

This commit is contained in:
Jackz 2022-07-01 11:16:12 -05:00
parent bf3058751f
commit 910a560eb9
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
2 changed files with 150 additions and 72 deletions

Binary file not shown.

View file

@ -2,16 +2,26 @@
#pragma newdecls required #pragma newdecls required
#define DEBUG #define DEBUG
#define DEBUG_SHOW_POINTS
#define DEBUG_BOT_MOVE
// #define DEBUG_SEEKER_PATH_CREATION 1 // #define DEBUG_SEEKER_PATH_CREATION 1
#define PLUGIN_VERSION "1.0" #define PLUGIN_VERSION "1.0"
#define BOT_MOVE_RANDOM_MIN_TIME 7.0 // The minimum random time for Timer_BotMove to activate (set per bot, per round) #define BOT_MOVE_RANDOM_MIN_TIME 6.0 // The minimum random time for Timer_BotMove to activate (set per bot, per round)
#define BOT_MOVE_RANDOM_MAX_TIME 9.6 // The maximum random time for Timer_BotMove to activate (set per bot, per round) #define BOT_MOVE_RANDOM_MAX_TIME 8.6 // The maximum random time for Timer_BotMove to activate (set per bot, per round)
#define BOT_MOVE_CHANCE 0.90 // The chance the bot will move each Timer_BotMove #define BOT_MOVE_CHANCE 0.96 // The chance the bot will move each Timer_BotMove
#define BOT_MOVE_AVOID_FLOW_DIST 15.0 // The flow range of flow distance that triggers avoid #define BOT_MOVE_AVOID_FLOW_DIST 12.0 // The flow range of flow distance that triggers avoid
#define BOT_MOVE_AVOID_SEEKER_CHANCE 0.60 // The chance that if the bot gets too close to the seeker, it runs away #define BOT_MOVE_AVOID_SEEKER_CHANCE 0.50 // The chance that if the bot gets too close to the seeker, it runs away
#define BOT_MOVE_AVOID_MIN_DISTANCE 200.0 // The minimum distance for a far away point. (NOT flow)
#define BOT_MOVE_USE_CHANCE 0.001 // Chance the bots will use +USE (used for opening doors, buttons, etc)
#define BOT_MOVE_JUMP_CHANCE 0.001
#define BOT_MOVE_SHOVE_CHANCE 0.0015
#define BOT_MOVE_RUN_CHANCE 0.15
#define DOOR_TOGGLE_INTERVAL 5.0 // Interval that loops throuh all doors to randomly toggle
#define DOOR_TOGGLE_CHANCE 0.01 // Chance that every Timer_DoorToggles triggers a door to toggle state
#define HIDER_SWAP_COOLDOWN 30.0 // Amount of seconds until they can swap #define HIDER_SWAP_COOLDOWN 30.0 // Amount of seconds until they can swap
#define FLOW_BOUND_BUFFER 200.0 // Amount to add to calculated bounds (Make it very generous)
#if defined DEBUG #if defined DEBUG
#define SEED_TIME 1.0 #define SEED_TIME 1.0
@ -24,8 +34,11 @@
// #define SMOKE_PARTICLE_PATH "materials/particle/splashsprites/largewatersplash.vmt" // #define SMOKE_PARTICLE_PATH "materials/particle/splashsprites/largewatersplash.vmt"
// #define SMOKE_PARTICLE_PATH "materials/particle/fire_explosion_1/fire_explosion_1.vmt" // #define SMOKE_PARTICLE_PATH "materials/particle/fire_explosion_1/fire_explosion_1.vmt"
#define SOUND_MODEL_SWAP "ui/pickup_secret01.wav" #define SOUND_MODEL_SWAP "ui/pickup_secret01.wav"
#define MAX_VALID_LOCATIONS 1000 // The maximum amount of locations to hold, once this limit is reached only MAX_VALID_LOCATIONS_KEEP_PERCENT entries will be kept at random #define MAX_VALID_LOCATIONS 2000 // The maximum amount of locations to hold, once this limit is reached only MAX_VALID_LOCATIONS_KEEP_PERCENT entries will be kept at random
#define MAX_VALID_LOCATIONS_KEEP_PERCENT 0.20 // The % of locations to be kept when dumping validLocations #define MAX_VALID_LOCATIONS_KEEP_PERCENT 0.30 // The % of locations to be kept when dumping validLocations
float DEBUG_POINT_VIEW_MIN[3] = { -5.0, -5.0, 0.0 };
float DEBUG_POINT_VIEW_MAX[3] = { 5.0, 5.0, 2.0 };
#include <sourcemod> #include <sourcemod>
#include <sdktools> #include <sdktools>
@ -72,6 +85,8 @@ int hiderSwapTime[MAXPLAYERS+1];
// Temp Ent Materials & Timers // Temp Ent Materials & Timers
Handle spawningTimer; Handle spawningTimer;
Handle recordTimer;
Handle acquireLocationsTimer;
Handle moveTimers[MAXPLAYERS+1]; Handle moveTimers[MAXPLAYERS+1];
UserMsg g_FadeUserMsgId; UserMsg g_FadeUserMsgId;
int g_iSmokeParticle; int g_iSmokeParticle;
@ -87,6 +102,8 @@ float flowMin, flowMax;
static float seekerPos[3]; static float seekerPos[3];
float seekerFlow = 0.0; float seekerFlow = 0.0;
float vecLastLocation[MAXPLAYERS+1][3];
#include <guesswho/gwcore> #include <guesswho/gwcore>
public Plugin myinfo = { public Plugin myinfo = {
@ -108,6 +125,10 @@ public void OnPluginStart() {
SetFailState("This plugin is for L4D2 only."); SetFailState("This plugin is for L4D2 only.");
} }
g_iTeamNum = FindSendPropInfo("CTerrorPlayerResource", "m_iTeam");
if (g_iTeamNum == -1)
SetFailState("CTerrorPlayerResource \"m_iTeam\" offset is invalid");
validMaps = new ArrayList(ByteCountToCells(64)); validMaps = new ArrayList(ByteCountToCells(64));
validSets = new ArrayList(ByteCountToCells(16)); validSets = new ArrayList(ByteCountToCells(16));
mapConfigs = new StringMap(); mapConfigs = new StringMap();
@ -124,9 +145,12 @@ public void OnPluginStart() {
cvar_seekerFailDamageAmount = CreateConVar("guesswho_seeker_damage", "20.0", "The amount of damage the seeker takes when they attack a bot.", FCVAR_NONE, true, 1.0); cvar_seekerFailDamageAmount = CreateConVar("guesswho_seeker_damage", "20.0", "The amount of damage the seeker takes when they attack a bot.", FCVAR_NONE, true, 1.0);
RegAdminCmd("sm_guesswho", Command_GuessWho, ADMFLAG_KICK); RegAdminCmd("sm_guesswho", Command_GuessWho, ADMFLAG_KICK);
RegAdminCmd("sm_gw", Command_GuessWho, ADMFLAG_KICK);
RegConsoleCmd("sm_joingame", Command_Join); RegConsoleCmd("sm_joingame", Command_Join);
} }
public void OnPluginEnd() { public void OnPluginEnd() {
Cleanup(); Cleanup();
} }
@ -137,16 +161,12 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[
if(isEnabled == shouldEnable) return; if(isEnabled == shouldEnable) return;
if(spawningTimer != null) delete spawningTimer; if(spawningTimer != null) delete spawningTimer;
if(shouldEnable) { if(shouldEnable) {
SetCvars(); SetCvars(true);
PrintToChatAll("[GuessWho] Gamemode is starting"); PrintToChatAll("[GuessWho] Gamemode is starting");
HookEvent("round_start", Event_RoundStart); HookEvent("round_start", Event_RoundStart);
HookEvent("player_death", Event_PlayerDeath); HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_bot_replace", Event_PlayerToBot); HookEvent("player_bot_replace", Event_PlayerToBot);
for(int i = 1; i <= MaxClients; i++) { AddCommandListener(OnGoAwayFromKeyboard, "go_away_from_keyboard");
if(IsClientConnected(i) && IsClientInGame(i)) {
//ForcePlayerSuicide(i);
}
}
} else if(!lateLoaded) { } else if(!lateLoaded) {
UnsetCvars(); UnsetCvars();
UnhookEvent("round_start", Event_RoundStart); UnhookEvent("round_start", Event_RoundStart);
@ -154,19 +174,24 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[
UnhookEvent("player_bot_replace", Event_PlayerToBot); UnhookEvent("player_bot_replace", Event_PlayerToBot);
Cleanup(); Cleanup();
PrintToChatAll("[GuessWho] Gamemode unloaded but cvars have not been reset."); PrintToChatAll("[GuessWho] Gamemode unloaded but cvars have not been reset.");
RemoveCommandListener(OnGoAwayFromKeyboard, "go_away_from_keyboard");
} }
isEnabled = shouldEnable; isEnabled = shouldEnable;
} }
public Action OnGoAwayFromKeyboard(int client, const char[] command, int argc) {
return Plugin_Handled;
}
void Event_PlayerToBot(Event event, const char[] name, bool dontBroadcast) { void Event_PlayerToBot(Event event, const char[] name, bool dontBroadcast) {
int player = GetClientOfUserId(GetEventInt(event, "player")); int player = GetClientOfUserId(event.GetInt("player"));
int bot = GetClientOfUserId(GetEventInt(event, "bot")); int bot = GetClientOfUserId(event.GetInt("bot"));
// Do not kick bots being spawned in // Do not kick bots being spawned in
if(spawningTimer == null) { if(spawningTimer == null) {
PrintToServer("[GuessWho/debug] Kicking possible idle bot: %d (player: %d)", bot, player); PrintToServer("[GuessWho/debug] possible idle bot: %d (player: %d)", bot, player);
ChangeClientTeam(player, 0); // ChangeClientTeam(player, 0);
L4D_SetHumanSpec(bot, player); // L4D_SetHumanSpec(bot, player);
L4D_TakeOverBot(player); L4D_TakeOverBot(player);
// KickClient(bot); // KickClient(bot);
} }
@ -256,9 +281,6 @@ public void OnMapStart() {
isStarting = false; isStarting = false;
if(!isEnabled) return; if(!isEnabled) return;
g_iTeamNum = FindSendPropInfo("CTerrorPlayerResource", "m_iTeam");
if (g_iTeamNum == -1)
LogError("CTerrorPlayerResource \"m_iTeam\" offset is invalid");
SDKHook(FindEntityByClassname(0, "terror_player_manager"), SDKHook_ThinkPost, ThinkPost); SDKHook(FindEntityByClassname(0, "terror_player_manager"), SDKHook_ThinkPost, ThinkPost);
char map[128]; char map[128];
@ -274,6 +296,7 @@ public void OnMapStart() {
strcopy(currentMap, sizeof(currentMap), map); strcopy(currentMap, sizeof(currentMap), map);
LoadMapData(map, currentSet); LoadMapData(map, currentSet);
} }
g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt"); g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt");
g_iSmokeParticle = GetParticleIndex(SMOKE_PARTICLE_PATH); g_iSmokeParticle = GetParticleIndex(SMOKE_PARTICLE_PATH);
if(g_iSmokeParticle == INVALID_STRING_INDEX) { if(g_iSmokeParticle == INVALID_STRING_INDEX) {
@ -281,9 +304,7 @@ public void OnMapStart() {
} }
// g_iSmokeParticle = PrecacheModel(SMOKE_PARTICLE_PATH); // g_iSmokeParticle = PrecacheModel(SMOKE_PARTICLE_PATH);
PrecacheSound(SOUND_MODEL_SWAP); PrecacheSound(SOUND_MODEL_SWAP);
if(isEnabled) { SetCvars(false);
SetCvars();
}
if(lateLoaded) { if(lateLoaded) {
int seeker = GetSeeker(); int seeker = GetSeeker();
@ -334,9 +355,7 @@ public void OnClientPutInServer(int client) {
ChangeClientTeam(client, 1); ChangeClientTeam(client, 1);
isPendingPlay[client] = true; isPendingPlay[client] = true;
PrintToChatAll("%N will play next round", client); PrintToChatAll("%N will play next round", client);
float pos[3]; TeleportToSpawn(client);
FindSpawnPosition(pos);
TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR);
if(!IsPendingPlayers()) { if(!IsPendingPlayers()) {
StartGame(); StartGame();
} }
@ -344,32 +363,47 @@ public void OnClientPutInServer(int client) {
} }
public void OnClientDisconnect(int client) { public void OnClientDisconnect(int client) {
if(!isEnabled) return;
if(client == currentSeeker) { if(client == currentSeeker) {
if(acquireLocationsTimer != null) delete acquireLocationsTimer;
PrintToChatAll("The seeker has disconnected"); PrintToChatAll("The seeker has disconnected");
CreateTimer(1.0, Timer_ResetAll); CreateTimer(1.0, Timer_ResetAll);
currentSeeker = 0; currentSeeker = 0;
} else if(!IsFakeClient(client) && GetState() == State_Active) {
PrintToChatAll("A hider has left (%N)", client);
if(GetPlayersLeftAlive() == 0 && GetState() == State_Active) {
PrintToChatAll("Game Over. %N wins!", currentSeeker);
CreateTimer(5.0, Timer_ResetAll);
SetState(State_SeekerWon);
}
} }
} }
public bool SetCvarString(ConVar cvar, const char[] value) { public bool SetCvarString(ConVar cvar, const char[] value, bool record) {
if(cvar == null) return false; if(cvar == null) return false;
char name[32], prevValue[32]; char prevValue[32];
if(record) {
char name[32];
cvar.GetName(name, sizeof(name)); cvar.GetName(name, sizeof(name));
cvar.GetString(prevValue, sizeof(prevValue)); cvar.GetString(prevValue, sizeof(prevValue));
previousCvarValues.SetString(name, prevValue); previousCvarValues.SetString(name, prevValue);
}
cvar.SetString(value); cvar.SetString(value);
return true; return true;
} }
public bool SetCvarValue(ConVar cvar, any value) { public bool SetCvarValue(ConVar cvar, any value, bool record) {
if(cvar == null) return false; if(cvar == null) return false;
if(record) {
char name[32]; char name[32];
cvar.GetName(name, sizeof(name)); cvar.GetName(name, sizeof(name));
previousCvarValues.SetValue(name, float(value)); previousCvarValues.SetValue(name, float(value));
}
cvar.FloatValue = value; cvar.FloatValue = value;
return true; return true;
} }
void SetCvars() { void SetCvars(bool record = false) {
PrintToServer("[GuessWho] Setting convars"); PrintToServer("[GuessWho] Setting convars");
cvar_survivorLimit = FindConVar("survivor_limit"); cvar_survivorLimit = FindConVar("survivor_limit");
ConVar cvar_separationMinRange = FindConVar("sb_separation_danger_min_range"); ConVar cvar_separationMinRange = FindConVar("sb_separation_danger_min_range");
@ -379,14 +413,16 @@ void SetCvars() {
ConVar cvar_sbPushScale = FindConVar("sb_pushscale"); ConVar cvar_sbPushScale = FindConVar("sb_pushscale");
if(cvar_survivorLimit != null) { if(cvar_survivorLimit != null) {
cvar_survivorLimit.SetBounds(ConVarBound_Upper, true, 64.0); cvar_survivorLimit.SetBounds(ConVarBound_Upper, true, 64.0);
SetCvarValue(cvar_survivorLimit, MaxClients); SetCvarValue(cvar_survivorLimit, MaxClients, record);
cvar_survivorLimit.IntValue = MaxClients; cvar_survivorLimit.IntValue = MaxClients;
} }
SetCvarValue(cvar_separationMinRange, 1000); SetCvarValue(cvar_separationMinRange, 1000, record);
SetCvarValue(cvar_separationMaxRange, 1200); SetCvarValue(cvar_separationMaxRange, 1200, record);
SetCvarValue(cvar_abmAutoHard, 0); SetCvarValue(cvar_abmAutoHard, 0, record);
SetCvarValue(cvar_sbFixEnabled, 0); SetCvarValue(cvar_sbFixEnabled, 0, record);
SetCvarValue(cvar_sbPushScale, 0); SetCvarValue(cvar_sbPushScale, 0, record);
SetCvarValue(FindConVar("sb_battlestation_give_up_range_from_human"), 5000.0, record);
SetCvarValue(FindConVar("sb_max_battlestation_range_from_human"), 5000.0, record);
} }
void UnsetCvars() { void UnsetCvars() {
@ -473,6 +509,7 @@ Action Timer_SpawnBots(Handle h, int max) {
} }
Action Timer_SpawnPost(Handle h) { Action Timer_SpawnPost(Handle h) {
spawningTimer = null;
PrintToChatAll("Timer_SpawnPost(): activating"); PrintToChatAll("Timer_SpawnPost(): activating");
bool isL4D1 = L4D2_GetSurvivorSetMap() == 1; bool isL4D1 = L4D2_GetSurvivorSetMap() == 1;
int remainingSeekers; int remainingSeekers;
@ -510,13 +547,14 @@ Action Timer_SpawnPost(Handle h) {
} }
Action Timer_WaitForStart(Handle h) { Action Timer_WaitForStart(Handle h) {
if(L4D_HasAnySurvivorLeftSafeArea()) { if(mapConfig.hasSpawnpoint || L4D_HasAnySurvivorLeftSafeArea()) {
int targetPlayer = L4D_GetHighestFlowSurvivor(); int targetPlayer = L4D_GetHighestFlowSurvivor();
if(targetPlayer > 0) { if(targetPlayer > 0) {
GetClientAbsOrigin(targetPlayer, seekerPos); GetClientAbsOrigin(targetPlayer, seekerPos);
} }
seekerFlow = L4D2Direct_GetFlowDistance(currentSeeker); seekerFlow = L4D2Direct_GetFlowDistance(currentSeeker);
CreateTimer(0.5, Timer_AcquireLocations, _, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); acquireLocationsTimer = CreateTimer(0.5, Timer_AcquireLocations, _, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT);
CreateTimer(DOOR_TOGGLE_INTERVAL, Timer_DoorToggles, _, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT);
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if(i != currentSeeker && IsClientConnected(i) && IsClientInGame(i)) { if(i != currentSeeker && IsClientConnected(i) && IsClientInGame(i)) {
TeleportEntity(i, seekerPos, NULL_VECTOR, NULL_VECTOR); TeleportEntity(i, seekerPos, NULL_VECTOR, NULL_VECTOR);
@ -557,7 +595,7 @@ Action OnTakeDamageAlive(int victim, int& attacker, int& inflictor, float& damag
ClearInventory(victim); ClearInventory(victim);
if(IsFakeClient(victim)) { if(IsFakeClient(victim)) {
PrintToChat(attacker, "That was a bot! -%.0f health", cvar_seekerFailDamageAmount.FloatValue); PrintToChat(attacker, "That was a bot! -%.0f health", cvar_seekerFailDamageAmount.FloatValue);
SDKHooks_TakeDamage(attacker, 0, 0, cvar_seekerFailDamageAmount.FloatValue); SDKHooks_TakeDamage(attacker, 0, 0, cvar_seekerFailDamageAmount.FloatValue, DMG_BURN);
} }
return Plugin_Changed; return Plugin_Changed;
} else if(attacker > 0 && attacker <= MaxClients) { } else if(attacker > 0 && attacker <= MaxClients) {
@ -568,9 +606,14 @@ Action OnTakeDamageAlive(int victim, int& attacker, int& inflictor, float& damag
} }
} }
static float vecLastLocation[MAXPLAYERS+1][3];
static float DEBUG_BOT_MOVER_MIN[3] = { -5.0, -5.0, 0.0 }; Action Timer_DoorToggles(Handle h) {
static float DEBUG_BOT_MOVER_MAX[3] = { 5.0, 5.0, 2.0 }; int entity = INVALID_ENT_REFERENCE;
while ((entity = FindEntityByClassname(entity, "prop_door_rotating")) != INVALID_ENT_REFERENCE) {
if(GetURandomFloat() < DOOR_TOGGLE_CHANCE)
AcceptEntityInput(entity, "Toggle");
}
}
Action Timer_AcquireLocations(Handle h) { Action Timer_AcquireLocations(Handle h) {
bool ignoreSeeker = true; bool ignoreSeeker = true;
@ -580,7 +623,7 @@ Action Timer_AcquireLocations(Handle h) {
seekerFlow = L4D2Direct_GetFlowDistance(currentSeeker); seekerFlow = L4D2Direct_GetFlowDistance(currentSeeker);
GetClientAbsOrigin(currentSeeker, seekerPos); GetClientAbsOrigin(currentSeeker, seekerPos);
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if((!ignoreSeeker || i != currentSeeker) && IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && GetEntityFlags(i) & FL_ONGROUND ) { if((!ignoreSeeker || i != currentSeeker) && IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2 && GetEntityFlags(i) & FL_ONGROUND ) {
LocationMeta meta; LocationMeta meta;
GetClientAbsOrigin(i, meta.pos); GetClientAbsOrigin(i, meta.pos);
GetClientEyeAngles(i, meta.ang); GetClientEyeAngles(i, meta.ang);
@ -591,7 +634,9 @@ Action Timer_AcquireLocations(Handle h) {
validLocations.Sort(Sort_Random, Sort_Float); validLocations.Sort(Sort_Random, Sort_Float);
validLocations.Erase(RoundFloat(MAX_VALID_LOCATIONS * MAX_VALID_LOCATIONS_KEEP_PERCENT)); validLocations.Erase(RoundFloat(MAX_VALID_LOCATIONS * MAX_VALID_LOCATIONS_KEEP_PERCENT));
} }
Effect_DrawBeamBoxRotatableToClient(i, meta.pos, DEBUG_BOT_MOVER_MIN, DEBUG_BOT_MOVER_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 0, 255}, 0); #if defined DEBUG_SHOW_POINTS
Effect_DrawBeamBoxRotatableToClient(i, meta.pos, DEBUG_POINT_VIEW_MIN, DEBUG_POINT_VIEW_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {0, 0, 255, 64}, 0);
#endif
vecLastLocation[i] = meta.pos; vecLastLocation[i] = meta.pos;
} }
} }
@ -599,12 +644,19 @@ Action Timer_AcquireLocations(Handle h) {
return Plugin_Continue; return Plugin_Continue;
} }
// float botMovePos[MAXPLAYERS+1][3];
Action Timer_BotMove(Handle h, int userid) { Action Timer_BotMove(Handle h, int userid) {
int i = GetClientOfUserId(userid); int i = GetClientOfUserId(userid);
if(i == 0) return Plugin_Stop; if(i == 0) return Plugin_Stop;
if(GetURandomFloat() > BOT_MOVE_CHANCE) return Plugin_Continue; if(GetURandomFloat() > BOT_MOVE_CHANCE) {
L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})",
GetClientUserId(i),
activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2]
);
#if defined DEBUG_SHOW_POINTS
Effect_DrawBeamBoxRotatableToAll(activeBotLocations[i].pos, DEBUG_POINT_VIEW_MIN, DEBUG_POINT_VIEW_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {157, 0, 255, 255}, 0);
#endif
return Plugin_Continue;
}
float botFlow = L4D2Direct_GetFlowDistance(i); float botFlow = L4D2Direct_GetFlowDistance(i);
if(botFlow < flowMin || botFlow > flowMax) { if(botFlow < flowMin || botFlow > flowMax) {
@ -612,23 +664,40 @@ Action Timer_BotMove(Handle h, int userid) {
TE_SetupBeamLaser(i, currentSeeker, g_iLaserIndex, 0, 0, 0, 8.0, 0.5, 0.1, 0, 1.0, {255, 255, 0, 125}, 1); TE_SetupBeamLaser(i, currentSeeker, g_iLaserIndex, 0, 0, 0, 8.0, 0.5, 0.1, 0, 1.0, {255, 255, 0, 125}, 1);
TE_SendToAll(); TE_SendToAll();
L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", GetClientUserId(i), seekerPos[0], seekerPos[1], seekerPos[2]); L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", GetClientUserId(i), seekerPos[0], seekerPos[1], seekerPos[2]);
#if defined DEBUG_BOT_MOVE
PrintToConsoleAll("[gw/debug] BOT %N TOO FAR (%f) BOUNDS (%f, %f)-> Moving to seeker (%f %f %f)", i, botFlow, flowMin, flowMax, seekerPos[0], seekerPos[1], seekerPos[2]); PrintToConsoleAll("[gw/debug] BOT %N TOO FAR (%f) BOUNDS (%f, %f)-> Moving to seeker (%f %f %f)", i, botFlow, flowMin, flowMax, seekerPos[0], seekerPos[1], seekerPos[2]);
#endif
} else if(validLocations.Length > 0) { } else if(validLocations.Length > 0) {
if(mapConfig.hasSpawnpoint && FloatAbs(botFlow - seekerFlow) < BOT_MOVE_AVOID_FLOW_DIST && GetURandomFloat() < BOT_MOVE_AVOID_SEEKER_CHANCE) { if(mapConfig.hasSpawnpoint && FloatAbs(botFlow - seekerFlow) < BOT_MOVE_AVOID_FLOW_DIST && GetURandomFloat() < BOT_MOVE_AVOID_SEEKER_CHANCE) {
if(!FindPointAway(seekerPos, activeBotLocations[i].pos)) { if(!FindPointAway(seekerPos, activeBotLocations[i].pos, BOT_MOVE_AVOID_MIN_DISTANCE)) {
#if defined DEBUG_BOT_MOVE
PrintToConsoleAll("[gw/debug] BOT %N TOO CLOSE -> Failed to find far point, falling back to spawn", i); PrintToConsoleAll("[gw/debug] BOT %N TOO CLOSE -> Failed to find far point, falling back to spawn", i);
#endif
activeBotLocations[i].pos = mapConfig.spawnpoint; activeBotLocations[i].pos = mapConfig.spawnpoint;
} else { } else {
PrintToConsoleAll("[gw/debug] BOT %N TOO CLOSE -> Moving to far point (%f %f %f)", i, activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2]); #if defined DEBUG_BOT_MOVE
PrintToConsoleAll("[gw/debug] BOT %N TOO CLOSE -> Moving to far point (%f %f %f) (%f units away)", i, activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2], GetVectorDistance(seekerPos, activeBotLocations[i].pos));
#endif
} }
activeBotLocations[i].runto = GetURandomFloat() < 0.75; activeBotLocations[i].runto = GetURandomFloat() < 0.75;
Effect_DrawBeamBoxRotatableToAll(activeBotLocations[i].pos, DEBUG_BOT_MOVER_MIN, DEBUG_BOT_MOVER_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.2, 0.1, 0, 0.0, {255, 255, 255, 255}, 0); #if defined DEBUG_SHOW_POINTS
Effect_DrawBeamBoxRotatableToAll(activeBotLocations[i].pos, DEBUG_POINT_VIEW_MIN, DEBUG_POINT_VIEW_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.2, 0.1, 0, 0.0, {255, 255, 255, 255}, 0);
#endif
} else { } else {
activeBotLocations[i].runto = GetURandomFloat() > 0.90; activeBotLocations[i].runto = GetURandomFloat() < BOT_MOVE_RUN_CHANCE;
validLocations.GetArray(GetURandomInt() % validLocations.Length, activeBotLocations[i]); validLocations.GetArray(GetURandomInt() % validLocations.Length, activeBotLocations[i]);
Effect_DrawBeamBoxRotatableToAll(activeBotLocations[i].pos, DEBUG_BOT_MOVER_MIN, DEBUG_BOT_MOVER_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 255, 255}, 0); #if defined DEBUG_SHOW_POINTS
Effect_DrawBeamBoxRotatableToAll(activeBotLocations[i].pos, DEBUG_POINT_VIEW_MIN, DEBUG_POINT_VIEW_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 255, 255}, 0);
#endif
} }
// botMovePos[i] = activeBotLocations[i].pos; #if defined DEBUG_BOT_MOVE
if(!L4D2_IsReachable(i, activeBotLocations[i].pos)) {
PrintToChatAll("[gw/debug] POINT UNREACHABLE (Bot:%d) (%f %f %f)", i, activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2]);
PrintToServer("[gw/debug] POINT UNREACHABLE (Bot:%d) (%f %f %f)", i, activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2]);
Effect_DrawBeamBoxRotatableToAll(activeBotLocations[i].pos, DEBUG_POINT_VIEW_MIN, DEBUG_POINT_VIEW_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 0, 255}, 0);
}
#endif
L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})",
GetClientUserId(i), GetClientUserId(i),
activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2] activeBotLocations[i].pos[0], activeBotLocations[i].pos[1], activeBotLocations[i].pos[2]
@ -640,15 +709,18 @@ Action Timer_BotMove(Handle h, int userid) {
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]) { 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]) {
if(!isEnabled) return Plugin_Continue; if(!isEnabled) return Plugin_Continue;
if(IsFakeClient(client)) { if(IsFakeClient(client)) {
float random = GetURandomFloat();
buttons |= (activeBotLocations[client].runto ? IN_WALK : IN_SPEED); buttons |= (activeBotLocations[client].runto ? IN_WALK : IN_SPEED);
if(random < 0.001) { if(GetURandomFloat() < BOT_MOVE_USE_CHANCE) {
buttons |= IN_USE;
}
float random = GetURandomFloat();
if(random < BOT_MOVE_JUMP_CHANCE) {
buttons |= IN_JUMP; buttons |= IN_JUMP;
} else if(random < 0.0015) { } else if(random < BOT_MOVE_SHOVE_CHANCE) {
buttons |= IN_ATTACK2; buttons |= IN_ATTACK2;
} }
return Plugin_Changed; return Plugin_Changed;
} else if(buttons & IN_RELOAD) { } else if(client != currentSeeker && buttons & IN_RELOAD) {
int target = GetClientAimTarget(client, true); int target = GetClientAimTarget(client, true);
if(target > 0) { if(target > 0) {
int time = GetTime(); int time = GetTime();
@ -731,7 +803,7 @@ stock void L4D2_RunScript(const char[] sCode, any ...) {
} }
public void OnSceneStageChanged(int scene, SceneStages stage) { public void OnSceneStageChanged(int scene, SceneStages stage) {
if(stage == SceneStage_Started) { if(isEnabled && stage == SceneStage_Started) {
int activator = GetSceneInitiator(scene); int activator = GetSceneInitiator(scene);
if(activator == 0) { if(activator == 0) {
CancelScene(scene); CancelScene(scene);
@ -743,8 +815,8 @@ bool SaveMapData(const char[] map, const char[] set = "default") {
char buffer[256]; char buffer[256];
// guesswho folder should be created by ReloadMapDB // guesswho folder should be created by ReloadMapDB
BuildPath(Path_SM, buffer, sizeof(buffer), "data/guesswho/%s", map); BuildPath(Path_SM, buffer, sizeof(buffer), "data/guesswho/%s", map);
CreateDirectory(buffer, 1402 /* 770 */); CreateDirectory(buffer, FOLDER_PERMS);
Format(buffer, sizeof(buffer), "%s/%s.txt", buffer, set); BuildPath(Path_SM, buffer, sizeof(buffer), "data/guesswho/%s/%s.txt", map, set);
File file = OpenFile(buffer, "w+"); File file = OpenFile(buffer, "w+");
if(file != null) { if(file != null) {
file.WriteLine("px\tpy\tpz\tax\tay\taz"); file.WriteLine("px\tpy\tpz\tax\tay\taz");
@ -758,6 +830,7 @@ bool SaveMapData(const char[] map, const char[] set = "default") {
delete file; delete file;
return true; return true;
} }
PrintToServer("[GuessWho] OpenFile(w+) returned null for %s", buffer);
return false; return false;
} }
@ -786,10 +859,13 @@ bool LoadMapData(const char[] map, const char[] set = "default") {
// Calculate the flow bounds // Calculate the flow bounds
Address nav = L4D2Direct_GetTerrorNavArea(meta.pos); Address nav = L4D2Direct_GetTerrorNavArea(meta.pos);
if(nav == Address_Null) {
nav = L4D_GetNearestNavArea(meta.pos);
if(nav == Address_Null) { if(nav == Address_Null) {
PrintToServer("[GuessWho] WARN: POINT AT (%f,%f,%f) IS INVALID (NO NAV AREA); skipping", meta.pos[0], meta.pos[1], meta.pos[2]); PrintToServer("[GuessWho] WARN: POINT AT (%f,%f,%f) IS INVALID (NO NAV AREA); skipping", meta.pos[0], meta.pos[1], meta.pos[2]);
continue; continue;
} }
}
float flow = L4D2Direct_GetTerrorNavAreaFlow(nav); float flow = L4D2Direct_GetTerrorNavAreaFlow(nav);
if(flow < flowMin) flowMin = flow; if(flow < flowMin) flowMin = flow;
else if(flow > flowMax) flowMax = flow; else if(flow > flowMax) flowMax = flow;
@ -797,11 +873,13 @@ bool LoadMapData(const char[] map, const char[] set = "default") {
validLocations.PushArray(meta); validLocations.PushArray(meta);
} }
// Give some buffer space, to not trigger TOO FAR // Give some buffer space, to not trigger TOO FAR
flowMin -= 5.0; flowMin -= FLOW_BOUND_BUFFER;
flowMax += 5.0; flowMax += FLOW_BOUND_BUFFER;
PrintToServer("[GuessWho] Loaded %d locations for %s/%s", validLocations.Length, map, set);
PrintToServer("[GuessWho] Loaded %d locations (bounds (%.0f, %.0f)) for %s/%s", validLocations.Length, flowMin, flowMax, map, set);
delete file; delete file;
return true; return true;
} }
PrintToServer("[GuessWho] OpenFile(r+) returned null for %s", buffer);
return false; return false;
} }