mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 16:33:21 +00:00
Changes
This commit is contained in:
parent
f3ff80c5ab
commit
9a5aa5dd5f
24 changed files with 478 additions and 101 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
plugins/l4d2_mimic.smx
Normal file
BIN
plugins/l4d2_mimic.smx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -21,6 +21,7 @@ char PRECACHE_SOUNDS[PRECACHE_SOUNDS_COUNT][] = {
|
|||
#tryinclude <sceneprocessor>
|
||||
#include <multicolors>
|
||||
#include "l4d_survivor_identity_fix.inc"
|
||||
#include <anymap>
|
||||
|
||||
char MODELS[8][] = {
|
||||
"models/survivors/survivor_gambler.mdl",
|
||||
|
@ -51,6 +52,7 @@ static bool isHighPingIdle[MAXPLAYERS+1], isL4D1Survivors;
|
|||
static Handle hGoAwayFromKeyboard;
|
||||
static StringMap SteamIDs;
|
||||
static char lastSound[MAXPLAYERS+1][64], gamemode[32];
|
||||
AnyMap disabledItems;
|
||||
|
||||
static float OUT_OF_BOUNDS[3] = {0.0, -1000.0, 0.0};
|
||||
|
||||
|
@ -108,6 +110,7 @@ public void OnPluginStart() {
|
|||
}
|
||||
|
||||
LasersUsed = new ArrayList(1, 0);
|
||||
disabledItems = new AnyMap();
|
||||
SteamIDs = new StringMap();
|
||||
|
||||
hGamemode = FindConVar("mp_gamemode");
|
||||
|
@ -462,7 +465,7 @@ void SetCharacter(int target, int survivorIndex, L4DModelId modelIndex, bool kee
|
|||
if (IsFakeClient(target)) {
|
||||
char name[32];
|
||||
GetSurvivorName(target, name, sizeof(name));
|
||||
// SetClientInfo(target, "name", name);
|
||||
SetClientInfo(target, "name", name);
|
||||
}
|
||||
UpdatePlayerIdentity(target, view_as<Character>(survivorIndex), keepModel);
|
||||
|
||||
|
@ -595,7 +598,6 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
|
|||
}
|
||||
}
|
||||
|
||||
int disabledItem[2048];
|
||||
//Can also probably prevent kit drop to pick them up
|
||||
public void Event_WeaponDrop(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
|
@ -604,16 +606,18 @@ public void Event_WeaponDrop(Event event, const char[] name, bool dontBroadcast)
|
|||
GetEntityClassname(client, newWpn, sizeof(newWpn));
|
||||
if(StrEqual(newWpn, "weapon_ammo_pack")) {
|
||||
// prevent weapon from being picked up?
|
||||
disabledItem[weapon] = client;
|
||||
disabledItems.SetValue(weapon, GetClientUserId(client));
|
||||
CreateTimer(10.0, Timer_AllowKitPickup, weapon);
|
||||
}
|
||||
}
|
||||
public Action Event_OnWeaponEquip(int client, int weapon) {
|
||||
if(disabledItem[weapon] > 0 && disabledItem[weapon] != client) return Plugin_Handled;
|
||||
return Plugin_Continue;
|
||||
int userid;
|
||||
if(disabledItems.GetValue(weapon, userid) && GetClientUserId(client) == userid)
|
||||
return Plugin_Handled;
|
||||
else return Plugin_Continue;
|
||||
}
|
||||
public Action Timer_AllowKitPickup(Handle h, int entity) {
|
||||
disabledItem[entity] = 0;
|
||||
disabledItems.Remove(entity);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
public void OnMapStart() {
|
||||
|
@ -629,6 +633,9 @@ public void OnMapStart() {
|
|||
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
}
|
||||
public void OnMapEnd() {
|
||||
disabledItems.Clear();
|
||||
}
|
||||
public void OnConfigsExecuted() {
|
||||
isL4D1Survivors = L4D2_GetSurvivorSetMap() == 1;
|
||||
if(hSVMaxPlayers != null && hPlayerLimit.IntValue > 0) {
|
||||
|
@ -638,14 +645,19 @@ public void OnConfigsExecuted() {
|
|||
|
||||
#if defined _sceneprocessor_included
|
||||
public void OnSceneStageChanged(int scene, SceneStages stage) {
|
||||
if(stage == SceneStage_Started) {
|
||||
if(stage == SceneStage_SpawnedPost) {
|
||||
int activator = GetSceneInitiator(scene);
|
||||
// int actor = GetActorFromScene(scene);
|
||||
|
||||
// PrintToServer("activator=%N actor=%N %s", activator, actor, sceneFile);
|
||||
if(activator == 0) {
|
||||
static char sceneFile[64];
|
||||
GetSceneFile(scene, sceneFile, sizeof(sceneFile));
|
||||
if(StrContains(sceneFile, "scenes/mechanic/dlc1_c6m1_initialmeeting") > -1 || StrEqual(sceneFile, "scenes/teengirl/dlc1_c6m1_initialmeeting07.vcd")) {
|
||||
CancelScene(scene);
|
||||
}else if(StrEqual(sceneFile, "scenes/teengirl/dlc1_c6m1_initialmeeting13.vcd") && activator == 0) {
|
||||
} else if(StrEqual(sceneFile, "scenes/teengirl/dlc1_c6m1_initialmeeting13.vcd")) {
|
||||
CancelScene(scene);
|
||||
} else if(StrEqual(sceneFile, "scenes/coach/worldc1m3b04.vcd")) {
|
||||
CancelScene(scene);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ int playerJoinTime[MAXPLAYERS+1];
|
|||
Handle updateHealthTimer[MAXPLAYERS+1];
|
||||
Handle updateItemTimer[MAXPLAYERS+1];
|
||||
Handle receiveTimeoutTimer = null;
|
||||
int pendingTries = 3;
|
||||
|
||||
bool lateLoaded;
|
||||
|
||||
|
@ -72,16 +73,12 @@ GameState g_gameState;
|
|||
Buffer sendBuffer;
|
||||
Buffer receiveBuffer; // Unfortunately there's no easy way to have this not be the same as BUFFER_SIZE
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
||||
lateLoaded = late;
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
|
||||
// TODO: periodic reconnect
|
||||
public void OnPluginStart() {
|
||||
g_socket = new Socket(SOCKET_TCP, OnSocketError);
|
||||
g_socket.SetOption(SocketKeepAlive, 1);
|
||||
g_socket.SetOption(SocketReuseAddr, 1);
|
||||
|
@ -118,17 +115,20 @@ public void OnPluginStart()
|
|||
HookEvent("pills_used", Event_ItemUsed);
|
||||
HookEvent("adrenaline_used", Event_ItemUsed);
|
||||
HookEvent("weapon_drop", Event_WeaponDrop);
|
||||
HookEvent("player_first_spawn", Event_PlayerFirstSpawn);
|
||||
HookEvent("player_spawn", Event_PlayerSpawn);
|
||||
HookEvent("map_transition", Event_MapTransition);
|
||||
HookEvent("player_death", Event_PlayerDeath);
|
||||
HookEvent("player_bot_replace", Event_PlayerToBot);
|
||||
HookEvent("bot_player_replace", Event_BotToPlayer);
|
||||
|
||||
campaignStartTime = GetTime();
|
||||
char auth[32];
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
playerJoinTime[i] = GetTime();
|
||||
OnClientPutInServer(i);
|
||||
if(IsClientInGame(i)) {
|
||||
if(GetClientAuthId(i, AuthId_Steam2, auth, sizeof(auth))) {
|
||||
OnClientAuthorized(i, auth);
|
||||
OnClientPutInServer(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,15 @@ public void OnPluginStart()
|
|||
RegAdminCmd("sm_panel_request_stop", Command_RequestStop, ADMFLAG_GENERIC);
|
||||
|
||||
CommandArgRegex = new Regex("(?:[^\\s\"]+|\"[^\"]*\")+", 0);
|
||||
|
||||
CreateTimer(300.0, Timer_FullSync, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
Action Timer_FullSync(Handle h) {
|
||||
if(CanSendPayload(true)) {
|
||||
SendFullSync();
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
void TriggerHealthUpdate(int client, bool instant = false) {
|
||||
|
@ -187,6 +196,7 @@ void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int
|
|||
if(authState == Auth_Pending) {
|
||||
if(response == Live_OK) {
|
||||
authState = Auth_Success;
|
||||
pendingTries = 0;
|
||||
PrintToServer("[AdminPanel] Authenticated with server successfully.");
|
||||
SendFullSync();
|
||||
} else if(response == Live_Error) {
|
||||
|
@ -203,7 +213,7 @@ void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int
|
|||
|
||||
lastReceiveTime = GetTime();
|
||||
switch(response) {
|
||||
case Live_RunComand: {
|
||||
case Live_RunCommand: {
|
||||
char command[128];
|
||||
char cmdNamespace[32];
|
||||
int id = receiveBuffer.ReadByte();
|
||||
|
@ -223,8 +233,18 @@ void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int
|
|||
case Live_Reconnect:
|
||||
CreateTimer(5.0, Timer_Reconnect);
|
||||
case Live_Refresh: {
|
||||
PrintToServer("[AdminPanel] Sync requested, performing");
|
||||
// SendFullSync();
|
||||
int userid = receiveBuffer.ReadByte();
|
||||
if(userid > 0) {
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(client > 0 && StartPayload(true)) {
|
||||
PrintToServer("[AdminPanel] Sync requested for #%d, performing", userid);
|
||||
AddPlayerRecord(client);
|
||||
SendPayload();
|
||||
}
|
||||
} else {
|
||||
PrintToServer("[AdminPanel] Sync requested, performing");
|
||||
SendFullSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(receiveTimeoutTimer != null) {
|
||||
|
@ -481,6 +501,7 @@ void ConnectSocket() {
|
|||
if(authToken[0] == '\0') return;
|
||||
// Do not try to reconnect on auth failure, until token has changed
|
||||
if(authState == Auth_Fail) return;
|
||||
authState = Auth_Pending;
|
||||
g_socket.Connect(OnSocketConnect, OnSocketReceive, OnSocketDisconnect, serverIp, serverPort);
|
||||
}
|
||||
|
||||
|
@ -499,6 +520,11 @@ Action Command_PanelDebug(int client, int args) {
|
|||
ReplyToCommand(client, "Target Host: %s:%d", serverIp, serverPort);
|
||||
ReplyToCommand(client, "Buffer Size: %d", BUFFER_SIZE);
|
||||
ReplyToCommand(client, "Can Send: %b\tCan Force-Send: %b", CanSendPayload(), CanSendPayload(true));
|
||||
} else if(StrEqual(arg, "cansend")) {
|
||||
if(!g_socket.Connected) ReplyToCommand(client, "Socket Not Connected");
|
||||
else if(authState != Auth_Success) ReplyToCommand(client, "Socket Not Authenticated (State=%d)", authState);
|
||||
else if(numberOfViewers == 0 || numberOfPlayers == 0) ReplyToCommand(client, "Can send forefully, but no players(%d)/viewers(%d)", numberOfPlayers, numberOfViewers);
|
||||
ReplyToCommand(client, "Can Send!");
|
||||
} else if(StrEqual(arg, "builtin")) {
|
||||
if(args < 2) {
|
||||
ReplyToCommand(client, "Usage: builtin <command>");
|
||||
|
@ -561,6 +587,7 @@ void Event_GameStart(Event event, const char[] name, bool dontBroadcast) {
|
|||
}
|
||||
void Event_GameEnd(Event event, const char[] name, bool dontBroadcast) {
|
||||
campaignStartTime = 0;
|
||||
CreateTimer(10.0, Timer_FullSync);
|
||||
}
|
||||
|
||||
void Event_MapTransition(Event event, const char[] name, bool dontBroadcast) {
|
||||
|
@ -571,6 +598,12 @@ void Event_MapTransition(Event event, const char[] name, bool dontBroadcast) {
|
|||
}
|
||||
}
|
||||
|
||||
public void L4D_OnFirstSurvivorLeftSafeArea_Post(int client) {
|
||||
if(CanSendPayload()) {
|
||||
SendPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client > 0) {
|
||||
|
@ -590,6 +623,8 @@ public void Event_BotToPlayer(Handle event, char[] name, bool dontBroadcast) {
|
|||
int player = GetClientOfUserId(GetEventInt(event, "player"));
|
||||
int bot = GetClientOfUserId(GetEventInt(event, "bot"));
|
||||
if(player > 0 && !IsFakeClient(player) && StartPayload(true)) {
|
||||
// Bot is going away, remove it: (prob unnecessary OnClientDisconnect happens)
|
||||
AddPlayerRecord(bot, false);
|
||||
AddPlayerRecord(player);
|
||||
SendPayload();
|
||||
}
|
||||
|
@ -616,10 +651,13 @@ void Event_HealInterrupted(Event event, const char[] name, bool dontBroadcast) {
|
|||
g_icBeingHealed[subject] = false;
|
||||
}
|
||||
|
||||
void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
playerJoinTime[client] = GetTime();
|
||||
void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
RecalculatePlayerCount();
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client > 0 && StartPayload()) {
|
||||
AddPlayerRecord(client);
|
||||
SendPayload();
|
||||
}
|
||||
}
|
||||
|
||||
void RecalculatePlayerCount() {
|
||||
|
@ -662,6 +700,21 @@ public void OnMapStart() {
|
|||
public void OnConfigsExecuted() {
|
||||
isL4D1Survivors = L4D2_GetSurvivorSetMap() == 1;
|
||||
}
|
||||
public void OnClientAuthorized(int client, const char[] auth) {
|
||||
if(!IsFakeClient(client)) {
|
||||
strcopy(steamidCache[client], 32, auth);
|
||||
numberOfPlayers++;
|
||||
} else {
|
||||
// Check if they are not a real survivor bot, such as ABMBot or EPIBot, etc
|
||||
if(StrContains(nameCache[client], "bot", false) > -1) {
|
||||
return;
|
||||
}
|
||||
strcopy(steamidCache[client], 32, "BOT");
|
||||
}
|
||||
GetClientName(client, nameCache[client], MAX_NAME_LENGTH);
|
||||
playerJoinTime[client] = GetTime();
|
||||
RequestFrame(SendNewClient, client);
|
||||
}
|
||||
// Player counts
|
||||
public void OnClientPutInServer(int client) {
|
||||
if(g_gameState == State_Transitioning) {
|
||||
|
@ -671,21 +724,9 @@ public void OnClientPutInServer(int client) {
|
|||
SendPayload();
|
||||
}
|
||||
}
|
||||
GetClientName(client, nameCache[client], MAX_NAME_LENGTH);
|
||||
if(!IsFakeClient(client)) {
|
||||
GetClientAuthId(client, AuthId_SteamID64, steamidCache[client], 32);
|
||||
numberOfPlayers++;
|
||||
} else {
|
||||
// Check if they are not a bot, such as ABMBot or EPIBot, etc
|
||||
if(StrContains(nameCache[client], "bot", false) > -1) {
|
||||
return;
|
||||
}
|
||||
strcopy(steamidCache[client], 32, "BOT");
|
||||
}
|
||||
SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponPickUp);
|
||||
SDKHook(client, SDKHook_OnTakeDamageAlivePost, OnTakeDamagePost);
|
||||
// We wait a frame because Event_PlayerFirstSpawn sets their join time
|
||||
RequestFrame(SendNewClient, client);
|
||||
}
|
||||
|
||||
void OnWeaponPickUp(int client, int weapon) {
|
||||
|
@ -780,6 +821,7 @@ public void OnClientDisconnect(int client) {
|
|||
// Incase somehow we lost track
|
||||
if(numberOfPlayers < 0) {
|
||||
numberOfPlayers = 0;
|
||||
CreateTimer(1.0, Timer_FullSync);
|
||||
}
|
||||
}
|
||||
if(updateHealthTimer[client] != null) {
|
||||
|
@ -806,7 +848,6 @@ void OnCvarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
|||
if(serverPort == 0) serverPort = DEFAULT_SERVER_PORT;
|
||||
}
|
||||
PrintToServer("[AdminPanel] Sending data to %s:%d", serverIp, serverPort);
|
||||
authState = Auth_Pending;
|
||||
ConnectSocket();
|
||||
}
|
||||
} else if(cvar_gamemode == convar) {
|
||||
|
@ -823,7 +864,6 @@ void OnCvarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
|||
}
|
||||
} else if(cvar_authToken == convar) {
|
||||
strcopy(authToken, sizeof(authToken), newValue);
|
||||
authState = Auth_Pending;
|
||||
// Token changed, re-try authentication
|
||||
ConnectSocket();
|
||||
}
|
||||
|
@ -931,15 +971,15 @@ enum CommandResultType {
|
|||
}
|
||||
|
||||
enum LiveRecordType {
|
||||
Live_Game,
|
||||
Live_Player,
|
||||
Live_Survivor,
|
||||
Live_Infected,
|
||||
Live_Finale,
|
||||
Live_SurvivorItems,
|
||||
Live_CommandResponse,
|
||||
Live_Auth,
|
||||
Live_Meta
|
||||
Live_Game = 0,
|
||||
Live_Player = 1,
|
||||
Live_Survivor = 2,
|
||||
Live_Infected = 3,
|
||||
Live_Finale = 4,
|
||||
Live_SurvivorItems = 5,
|
||||
Live_CommandResponse = 6,
|
||||
Live_Auth = 7,
|
||||
Live_Meta = 8
|
||||
}
|
||||
char LIVE_RECORD_NAMES[view_as<int>(Live_Meta)+1][] = {
|
||||
"Game",
|
||||
|
@ -958,13 +998,21 @@ enum LiveRecordResponse {
|
|||
Live_Reconnect,
|
||||
Live_Error,
|
||||
Live_Refresh,
|
||||
Live_RunComand
|
||||
Live_RunCommand
|
||||
}
|
||||
|
||||
char pendingRecords[64];
|
||||
|
||||
bool CanSendPayload(bool ignorePause = false) {
|
||||
if(!g_socket.Connected || authState != Auth_Success) return false;
|
||||
if(!g_socket.Connected) return false;
|
||||
if(authState != Auth_Success) {
|
||||
if(authState == Auth_Pending && pendingTries > 0) {
|
||||
pendingTries--;
|
||||
PrintToServer("[AdminPanel] Auth state is pending. Too early?");
|
||||
ConnectSocket();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if(!ignorePause && (numberOfViewers == 0 || numberOfPlayers == 0)) return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -978,34 +1026,45 @@ bool StartPayload(bool ignorePause = false) {
|
|||
/// Starts payload, ignoring if the payload can even be sent
|
||||
bool hasRecord;
|
||||
int recordStart;
|
||||
bool pendingRecord;
|
||||
|
||||
void StartPayloadEx() {
|
||||
if(pendingRecord) {
|
||||
LogError("StartPayloadEx called before EndRecord()");
|
||||
}
|
||||
sendBuffer.Reset();
|
||||
hasRecord = false;
|
||||
pendingRecords[0] = '\0';
|
||||
recordStart = 0;
|
||||
pendingRecord = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void StartRecord(LiveRecordType type) {
|
||||
if(pendingRecord) {
|
||||
LogError("StartRecord called before EndRecord()");
|
||||
}
|
||||
if(hasRecord) {
|
||||
sendBuffer.WriteChar('\x1e');
|
||||
}
|
||||
if(cvar_debug.BoolValue)
|
||||
Format(pendingRecords, sizeof(pendingRecords), "%s%s ", pendingRecords, LIVE_RECORD_NAMES[view_as<int>(type)]);
|
||||
recordStart = sendBuffer.offset;
|
||||
sendBuffer.WriteByte(0); // write temp NULL to be replaced
|
||||
sendBuffer.WriteShort(-1); // write temp value to be replaced when record ends
|
||||
sendBuffer.WriteByte(view_as<int>(type));
|
||||
hasRecord = true;
|
||||
pendingRecord = true;
|
||||
}
|
||||
|
||||
void EndRecord() {
|
||||
int length = sendBuffer.offset - recordStart - 1; // subtract 1, as don't count length inside
|
||||
sendBuffer.WriteByteAt(length, recordStart);
|
||||
int length = sendBuffer.offset - recordStart - 2; // subtract 1, as don't count length inside
|
||||
sendBuffer.WriteShortAt(length, recordStart);
|
||||
// if(cvar_debug.BoolValue) {
|
||||
// int type = sendBuffer.ReadByteAt(recordStart + 1);
|
||||
// int type = sendBuffer.ReadByteAt(recordStart + 2);
|
||||
// PrintToServer("End record %s(%d) (start: %d, end: %d) length: %d", LIVE_RECORD_NAMES[view_as<int>(type)], type, recordStart, sendBuffer.offset, length);
|
||||
// }
|
||||
hasRecord = true;
|
||||
pendingRecord = false;
|
||||
}
|
||||
|
||||
void AddGameRecord() {
|
||||
|
@ -1020,13 +1079,6 @@ void AddGameRecord() {
|
|||
EndRecord();
|
||||
}
|
||||
|
||||
|
||||
int GetMaxPlayers() {
|
||||
if(cvar_visibleMaxPlayers != null && cvar_visibleMaxPlayers.IntValue > 0) return cvar_visibleMaxPlayers.IntValue;
|
||||
if(cvar_maxplayers != null) return cvar_maxplayers.IntValue;
|
||||
return L4D_IsVersusMode() ? 8 : 4;
|
||||
}
|
||||
|
||||
void AddFinaleRecord(int stage) {
|
||||
StartRecord(Live_Finale);
|
||||
sendBuffer.WriteByte(stage); // finale stage
|
||||
|
@ -1127,6 +1179,9 @@ void AddCommandResponseRecord(int id, CommandResultType resultType = Result_None
|
|||
}
|
||||
|
||||
void AddAuthRecord() {
|
||||
if(authToken[0] == '\0') {
|
||||
LogError("AddAuthRecord called with missing auth token");
|
||||
}
|
||||
StartRecord(Live_Auth);
|
||||
sendBuffer.WriteByte(LIVESTATUS_VERSION);
|
||||
sendBuffer.WriteString(authToken);
|
||||
|
@ -1141,10 +1196,11 @@ void AddMetaRecord(bool state) {
|
|||
|
||||
void SendPayload() {
|
||||
if(sendBuffer.offset == 0) return;
|
||||
int len = sendBuffer.Finish();
|
||||
if(cvar_debug.BoolValue) {
|
||||
PrintToServer("[AdminPanel] Sending %d bytes of data (records = %s)", sendBuffer.offset, pendingRecords);
|
||||
PrintToServer("[AdminPanel] Sending %d bytes of data (records = %s)", len, pendingRecords);
|
||||
}
|
||||
g_socket.Send(sendBuffer.buffer, sendBuffer.offset);
|
||||
g_socket.Send(sendBuffer.buffer, len);
|
||||
}
|
||||
|
||||
enum struct Buffer {
|
||||
|
@ -1194,19 +1250,30 @@ enum struct Buffer {
|
|||
this.buffer[this.offset++] = (value >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
void WriteInt(int value, int bytes = 4) {
|
||||
this.buffer[this.offset++] = value & 0xFF;
|
||||
this.buffer[this.offset++] = (value >> 8) & 0xFF;
|
||||
this.buffer[this.offset++] = (value >> 16) & 0xFF;
|
||||
this.buffer[this.offset++] = (value >> 24) & 0xFF;
|
||||
void WriteShortAt(int value, int offset) {
|
||||
this.buffer[offset] = value & 0xFF;
|
||||
this.buffer[offset+1] = (value >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
void WriteInt(int value) {
|
||||
this.WriteIntAt(value, this.offset);
|
||||
this.offset += 4;
|
||||
}
|
||||
|
||||
void WriteIntAt(int value, int offset) {
|
||||
this.buffer[offset] = value & 0xFF;
|
||||
this.buffer[offset+1] = (value >> 8) & 0xFF;
|
||||
this.buffer[offset+2] = (value >> 16) & 0xFF;
|
||||
this.buffer[offset+3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
void WriteFloat(float value) {
|
||||
this.WriteInt(view_as<int>(value));
|
||||
}
|
||||
|
||||
// Writes a null-terminated length string, strlen > size is truncated.
|
||||
void WriteString(const char[] string) {
|
||||
this.buffer[this.offset] = '\0';
|
||||
int written = strcopy(this.buffer[this.offset], BUFFER_SIZE, string);
|
||||
this.offset += written + 1;
|
||||
}
|
||||
|
@ -1250,4 +1317,15 @@ enum struct Buffer {
|
|||
bool EOF() {
|
||||
return this.offset >= BUFFER_SIZE;
|
||||
}
|
||||
|
||||
int Finish() {
|
||||
this.buffer[this.offset++] = '\x0A';
|
||||
return this.offset;
|
||||
}
|
||||
}
|
||||
|
||||
int GetMaxPlayers() {
|
||||
if(cvar_visibleMaxPlayers != null && cvar_visibleMaxPlayers.IntValue > 0) return cvar_visibleMaxPlayers.IntValue;
|
||||
if(cvar_maxplayers != null) return cvar_maxplayers.IntValue;
|
||||
return L4D_IsVersusMode() ? 8 : 4;
|
||||
}
|
|
@ -169,8 +169,7 @@ void OnTankBotSpawn(int client) {
|
|||
SetEntProp(client, Prop_Send, "m_iHealth", health);
|
||||
g_finaleStage = Stage_FirstTankSpawned;
|
||||
return;
|
||||
} else if(g_realSurvivorCount < 6 && g_finaleStage == Stage_FirstTankSpawned) {
|
||||
// 2nd tank spawned
|
||||
} else if(g_realSurvivorCount >= 6 && g_finaleStage == Stage_FirstTankSpawned) {
|
||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: [FINALE] 2nd tank spawned");
|
||||
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
|
||||
// Pass it 0, which doesnt make it a split tank, has default health
|
||||
|
@ -215,6 +214,7 @@ int CalculateExtraTankHealth(int client) {
|
|||
int health = GetEntProp(client, Prop_Send, "m_iHealth");
|
||||
float additionalHealth = float(g_survivorCount - 4) * cvEPITankHealth.FloatValue;
|
||||
health += RoundFloat(additionalHealth);
|
||||
if(health <= 0) PrintToServer("CalculateExtraTankHealth: returning 0?");
|
||||
return health;
|
||||
}
|
||||
|
||||
|
|
|
@ -707,15 +707,15 @@ Action Command_SetReverseFF(int client, int args) {
|
|||
} else if(StrEqual(arg, "1")) {
|
||||
flag = 1;
|
||||
} else {
|
||||
ReplyToCommand(client, "Unsupported amount. Possible values: 0, 2, 0.5, 3, 1");
|
||||
ReplyToCommand(client, "Unsupported amount. Possible values: 0.5, 1, 2, 3");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
// args are 1-indexed so <=
|
||||
for(int i = 3; i <= args; i++) {
|
||||
GetCmdArg(i, arg, sizeof(arg));
|
||||
if(arg[0] == 'f') {
|
||||
if(arg[0] == 'f') { // [f]ire
|
||||
flag |= 32;
|
||||
} else if(arg[0] == 'e') {
|
||||
} else if(arg[0] == 'e' || arg[0] == 'b') { //[]blast or [e]xplode
|
||||
flag |= 64;
|
||||
} else {
|
||||
ReplyToCommand(client, "Unknown arg: %s", arg);
|
||||
|
|
|
@ -86,9 +86,14 @@ public Plugin myinfo =
|
|||
};
|
||||
|
||||
ConVar hExtraItemBasePercentage, hExtraSpawnBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime, hSplitTankChance, cvFFDecreaseRate, cvZDifficulty, cvEPIHudFlags, cvEPISpecialSpawning, cvEPIGamemodes, hGamemode, cvEPITankHealth, cvEPIEnabledMode;
|
||||
ConVar cvEPICommonCountScale, cvEPICommonCountScaleMax;
|
||||
ConVar g_ffFactorCvar, hExtraTankThreshold;
|
||||
|
||||
|
||||
ConVar cvZCommonLimit; int zCommonLimitPrevValue;
|
||||
|
||||
int g_extraKitsAmount, g_extraKitsStart, g_saferoomDoorEnt, g_prevPlayerCount;
|
||||
bool g_forcedSurvivorCount;
|
||||
bool g_forcedSurvivorCount, g_extraKitsSpawnedFinale;
|
||||
static int g_currentChapter;
|
||||
bool g_isCheckpointReached, g_isLateLoaded, g_startCampaignGiven, g_isFailureRound, g_areItemsPopulated;
|
||||
static ArrayList g_ammoPacks;
|
||||
|
@ -317,6 +322,10 @@ public void OnPluginStart() {
|
|||
cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
|
||||
cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
|
||||
cvEPIEnabledMode = CreateConVar("epi_enabled", "1", "Is EPI enabled?\n0=OFF\n1=Auto (Official Maps Only)(5+)\n2=Auto (Any map) (5+)\n3=Forced on", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
cvEPICommonCountScale = CreateConVar("epi_commons_scale_multiplier", "0", "This value is multiplied by the number of extra players playing. It's then added to z_common_limit. 5 players with value 5 would be z_common_limit + ", FCVAR_NONE, true, 0.0);
|
||||
cvEPICommonCountScaleMax = CreateConVar("epi_commons_scale_max", "60", "The maximum amount that z_common_limit can be scaled to.", FCVAR_NONE, true, 0.0);
|
||||
cvZCommonLimit = FindConVar("z_common_limit");
|
||||
|
||||
// TODO: hook flags, reset name index / ping mode
|
||||
cvEPIHudFlags.AddChangeHook(Cvar_HudStateChange);
|
||||
cvEPISpecialSpawning.AddChangeHook(Cvar_SpecialSpawningChange);
|
||||
|
@ -1001,6 +1010,19 @@ Action Event_Pickup(int client, int weapon) {
|
|||
void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client > 0 && GetClientTeam(client) == 2 && !IsFakeClient(client)) {
|
||||
if(!g_extraKitsSpawnedFinale && L4D_IsMissionFinalMap(true)) {
|
||||
float pos[3];
|
||||
GetAbsOrigin(client, pos);
|
||||
Address address = L4D_GetNearestNavArea(pos);
|
||||
if(address != Address_Null) {
|
||||
int attributes = L4D_GetNavArea_SpawnAttributes(address);
|
||||
if(attributes & NAV_SPAWN_FINALE) {
|
||||
IncreaseKits(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: trigger increase kits finale on kit pickup
|
||||
|
||||
UpdatePlayerInventory(client);
|
||||
QueueSaveInventory(client);
|
||||
}
|
||||
|
@ -1197,6 +1219,11 @@ int SpawnItem(const char[] itemName, float pos[3], float ang[3] = NULL_VECTOR) {
|
|||
}
|
||||
|
||||
void IncreaseKits(bool inFinale) {
|
||||
if(inFinale) {
|
||||
if(g_extraKitsSpawnedFinale) return;
|
||||
g_extraKitsSpawnedFinale = true;
|
||||
g_extraKitsAmount = g_realSurvivorCount - 4;
|
||||
}
|
||||
float pos[3];
|
||||
int entity = FindEntityByClassname(-1, "weapon_first_aid_kit_spawn");
|
||||
if(entity == INVALID_ENT_REFERENCE) {
|
||||
|
@ -1289,12 +1316,13 @@ void Debug_GetAttributes(int attributes, char[] output, int maxlen) {
|
|||
}
|
||||
|
||||
public void L4D2_OnChangeFinaleStage_Post(int stage) {
|
||||
if(stage == 1) {
|
||||
if(stage == 1 && IsEPIActive()) {
|
||||
IncreaseKits(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapStart() {
|
||||
g_extraKitsSpawnedFinale = false;
|
||||
char map[32];
|
||||
GetCurrentMap(map, sizeof(map));
|
||||
// If map starts with c#m#, 98% an official map
|
||||
|
@ -2031,14 +2059,7 @@ bool DoesInventoryDiffer(int client) {
|
|||
bool IsEPIActive() {
|
||||
return g_epiEnabled;
|
||||
}
|
||||
/*
|
||||
[Debug] UpdateSurvivorCount: total=4 real=4 active=4
|
||||
[Debug] UpdateSurvivorCount: total=4 real=4 active=4
|
||||
Player no longer idle
|
||||
[Debug] UpdateSurvivorCount: total=5 real=4 active=5
|
||||
[Debug] UpdateSurvivorCount: total=4 real=4 active=4
|
||||
Player no longer idle
|
||||
*/
|
||||
bool wasActive;
|
||||
void UpdateSurvivorCount() {
|
||||
#if defined DEBUG_FORCE_PLAYERS
|
||||
g_survivorCount = DEBUG_FORCE_PLAYERS;
|
||||
|
@ -2075,9 +2096,35 @@ void UpdateSurvivorCount() {
|
|||
isActive = (g_isOfficialMap || cvEPIEnabledMode.IntValue == 2) && g_realSurvivorCount > 4;
|
||||
}
|
||||
g_epiEnabled = isActive;
|
||||
if(g_epiEnabled && !wasActive) {
|
||||
OnEPIActive();
|
||||
wasActive = true;
|
||||
} else if(wasActive) {
|
||||
OnEPIInactive();
|
||||
}
|
||||
|
||||
if(isActive)
|
||||
SetFFFactor(g_epiEnabled);
|
||||
}
|
||||
|
||||
|
||||
void OnEPIActive() {
|
||||
zCommonLimitPrevValue = cvZCommonLimit.IntValue;
|
||||
// TODO: lag check for common limit
|
||||
if(cvEPICommonCountScale.IntValue > 0) {
|
||||
int newLimit = zCommonLimitPrevValue + RoundFloat(cvEPICommonCountScale.FloatValue * float(g_realSurvivorCount));
|
||||
if(newLimit > 0) {
|
||||
if(newLimit > cvEPICommonCountScaleMax.IntValue) {
|
||||
newLimit = cvEPICommonCountScaleMax.IntValue;
|
||||
}
|
||||
}
|
||||
cvZCommonLimit.IntValue = newLimit;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEPIInactive() {
|
||||
cvZCommonLimit.IntValue = zCommonLimitPrevValue;
|
||||
}
|
||||
void SetFFFactor(bool enabled) {
|
||||
static float prevValue;
|
||||
// Restore the previous value (we use the value for the calculations of new value)
|
||||
|
|
|
@ -343,6 +343,9 @@ public void OnMapStart() {
|
|||
}
|
||||
Game.State = State_Unknown;
|
||||
}
|
||||
public void OnMapEnd() {
|
||||
Game.Cleanup();
|
||||
}
|
||||
|
||||
public void ThinkPost(int entity) {
|
||||
static int iTeamNum[MAXPLAYERS+1];
|
||||
|
|
|
@ -248,6 +248,9 @@ public void OnMapStart() {
|
|||
lateLoaded = false;
|
||||
}
|
||||
}
|
||||
public void OnMapEnd() {
|
||||
Game.Cleanup();
|
||||
}
|
||||
|
||||
public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
|
||||
if(isEnabled) {
|
||||
|
|
225
scripting/l4d2_mimic.sp
Normal file
225
scripting/l4d2_mimic.sp
Normal file
|
@ -0,0 +1,225 @@
|
|||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
#include <jutils>
|
||||
#include <left4dhooks>
|
||||
|
||||
int g_mimicBot;
|
||||
int g_mimicController;
|
||||
int g_mimicCamera;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "L4D2 Mimic",
|
||||
author = "jackzmc",
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://github.com/Jackzmc/sourcemod-plugins"
|
||||
};
|
||||
|
||||
public void OnPluginStart() {
|
||||
EngineVersion g_Game = GetEngineVersion();
|
||||
if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) {
|
||||
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||
}
|
||||
|
||||
RegAdminCmd("sm_mimic", Command_Mimic, ADMFLAG_CHEATS);
|
||||
HookEvent("player_bot_replace", Event_BotToIdle);
|
||||
AddCommandListener(OnCommand);
|
||||
}
|
||||
|
||||
public void OnPluginEnd() {
|
||||
StopMimic();
|
||||
}
|
||||
|
||||
#define PASSTHROUGH_COMMANDS_MAX 10
|
||||
char PASSTHROUGH_COMMANDS[PASSTHROUGH_COMMANDS_MAX][] = {
|
||||
"say",
|
||||
"vocalize",
|
||||
"sm_give",
|
||||
"give",
|
||||
"sm_say",
|
||||
"sm_chat",
|
||||
"use",
|
||||
"sm_slay",
|
||||
"sm_model",
|
||||
"sm_surv",
|
||||
};
|
||||
|
||||
Action OnCommand(int client, const char[] command, int argc) {
|
||||
if(g_mimicController == 0 || client != g_mimicController) return Plugin_Continue;
|
||||
for(int i = 0; i < PASSTHROUGH_COMMANDS_MAX; i++) {
|
||||
if(StrEqual(command, PASSTHROUGH_COMMANDS[i])) {
|
||||
char args[256];
|
||||
GetCmdArgString(args, sizeof(args));
|
||||
// PrintToServer("pass: %s %s", command, args);
|
||||
FakeClientCommandEx(g_mimicBot, "%s %s", command, args);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
// PrintToServer("ignore: %s", command);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
void Event_BotToIdle(Event event, const char[] name, bool dontBroadcast) {
|
||||
int player = GetClientOfUserId(event.GetInt("player"));
|
||||
int bot = GetClientOfUserId(event.GetInt("bot"));
|
||||
if(g_mimicBot == player) {
|
||||
KickClient(bot);
|
||||
RequestFrame(SetupMimic);
|
||||
}
|
||||
}
|
||||
public void OnClientDisconnect(int client) {
|
||||
if(client == g_mimicBot || client == g_mimicController) {
|
||||
StopMimic();
|
||||
}
|
||||
}
|
||||
|
||||
Action Command_Mimic(int client, int args) {
|
||||
if(g_mimicController != 0) {
|
||||
if(g_mimicController == client) {
|
||||
StopMimic();
|
||||
} else {
|
||||
ReplyToCommand(client, "Mimic is currently active by another player.");
|
||||
}
|
||||
} else if(args > 0) {
|
||||
char name[32], id[4];
|
||||
GetCmdArg(1, name, sizeof(name));
|
||||
int survivorId = -1;
|
||||
if(args > 1) {
|
||||
GetCmdArg(2, id, sizeof(id));
|
||||
survivorId = GetSurvivorId(id, L4D2_GetSurvivorSetMap() == 0);
|
||||
}
|
||||
StartMimic(client, name, survivorId);
|
||||
} else {
|
||||
ReplyToCommand(client, "Enter name");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void StartMimic(int controller, const char[] name, int survivorId = -1) {
|
||||
int bot = CreateFakeClient(name);
|
||||
if(bot == -1) {
|
||||
PrintToChat(controller, "Could not spawn fake client");
|
||||
return;
|
||||
}
|
||||
DispatchKeyValue(bot, "classname", "SurvivorBot");
|
||||
|
||||
ChangeClientTeam(bot, 2);
|
||||
if(!DispatchSpawn(bot)) {
|
||||
PrintToChat(controller, "Could not dispatch spawn");
|
||||
return;
|
||||
}
|
||||
L4D_RespawnPlayer(bot);
|
||||
|
||||
char model[128];
|
||||
GetEntPropString(controller, Prop_Data, "m_ModelName", model, sizeof(model));
|
||||
SetEntityModel(bot, model);
|
||||
|
||||
int camera = CreateEntityByName("point_viewcontrol_survivor");
|
||||
DispatchKeyValue(camera, "targetname", "mimic_cam");
|
||||
DispatchSpawn(camera);
|
||||
|
||||
g_mimicBot = bot;
|
||||
g_mimicController = controller;
|
||||
g_mimicCamera = camera;
|
||||
|
||||
SDKHook(controller, SDKHook_WeaponSwitchPost, OnWeaponSwitchPost);
|
||||
PrintToServer("controller: %N | bot: %N(#%d) | camera: %d", controller, bot, GetClientUserId(bot), camera);
|
||||
|
||||
RequestFrame(SetupMimic, survivorId);
|
||||
}
|
||||
|
||||
void SetupMimic(int survivorId = -1) {
|
||||
if(g_mimicController == 0) return;
|
||||
float pos[3], ang[3];
|
||||
GetClientAbsOrigin(g_mimicController, pos);
|
||||
int nav = L4D_GetNearestNavArea(pos);
|
||||
if(nav > 0) {
|
||||
L4D_FindRandomSpot(nav, pos);
|
||||
}
|
||||
TeleportEntity(g_mimicBot, pos, NULL_VECTOR, NULL_VECTOR);
|
||||
CheatCommand(g_mimicBot, "give", "rifle_ak47", "");
|
||||
CheatCommand(g_mimicBot, "give", "first_aid_kit", "");
|
||||
|
||||
if(survivorId == -1) survivorId = GetEntProp(g_mimicController, Prop_Send, "m_survivorCharacter");
|
||||
SetEntProp(g_mimicBot, Prop_Send, "m_survivorCharacter", survivorId);
|
||||
|
||||
|
||||
AcceptEntityInput(g_mimicCamera, "Disable", g_mimicController);
|
||||
GetClientEyePosition(g_mimicBot, pos);
|
||||
GetClientEyeAngles(g_mimicBot, ang);
|
||||
ClearParent(g_mimicCamera);
|
||||
TeleportEntity(g_mimicCamera, pos, NULL_VECTOR, NULL_VECTOR);
|
||||
SetParent(g_mimicCamera, g_mimicBot);
|
||||
AcceptEntityInput(g_mimicCamera, "Enable", g_mimicController);
|
||||
}
|
||||
|
||||
void OnWeaponSwitchPost(int client, int weapon) {
|
||||
if(g_mimicBot == 0) return;
|
||||
for(int slot = 0; slot < 5; slot++) {
|
||||
int slotWpn = GetPlayerWeaponSlot(client, slot);
|
||||
if(slotWpn == weapon) {
|
||||
ClientCommand(g_mimicBot, "slot%d", slot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StopMimic() {
|
||||
if(g_mimicBot > 0 && IsClientInGame(g_mimicBot)) {
|
||||
if(L4D_GoAwayFromKeyboard(g_mimicBot)) {
|
||||
int bot = L4D_GetBotOfIdlePlayer(g_mimicBot);
|
||||
if(bot > 0) {
|
||||
KickClient(bot);
|
||||
}
|
||||
}
|
||||
KickClient(g_mimicBot);
|
||||
}
|
||||
if(g_mimicCamera > 0 && IsValidEntity(g_mimicCamera)) {
|
||||
AcceptEntityInput(g_mimicCamera, "Disable");
|
||||
RemoveEntity(g_mimicCamera);
|
||||
}
|
||||
if(g_mimicController > 0 && IsClientConnected(g_mimicController)) {
|
||||
SDKUnhook(g_mimicController, SDKHook_WeaponSwitchPost, OnWeaponSwitchPost);
|
||||
}
|
||||
|
||||
g_mimicBot = 0;
|
||||
g_mimicController = 0;
|
||||
g_mimicCamera = 0;
|
||||
}
|
||||
|
||||
int prevButtons;
|
||||
float prevAngles[3], prevVel[3];
|
||||
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon) {
|
||||
if(client == g_mimicController) {
|
||||
if(!IsPlayerAlive(client)) {
|
||||
StopMimic();
|
||||
}
|
||||
prevAngles = angles;
|
||||
prevButtons = buttons;
|
||||
prevVel = vel;
|
||||
SetEntPropFloat(client, Prop_Send, "m_flNextAttack", GetGameTime() + 1.0);
|
||||
// TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR);
|
||||
return Plugin_Handled;
|
||||
} else if(client == g_mimicBot) {
|
||||
if(!IsPlayerAlive(client)) {
|
||||
StopMimic();
|
||||
}
|
||||
buttons = prevButtons | IN_BULLRUSH;
|
||||
angles = prevAngles;
|
||||
vel = prevVel;
|
||||
TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR);
|
||||
return Plugin_Changed;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
#include <anymap>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
|
@ -33,7 +34,7 @@ static ConVar hZCommonLimit;
|
|||
static bool IsDoneLoading, clownMusicPlayed;
|
||||
|
||||
static int iCurrentCommons, commonLimit, clownCommonsSpawned;
|
||||
static int commonType[2048];
|
||||
static AnyMap commonType;
|
||||
|
||||
#define COMMON_MODELS_COUNT 6
|
||||
static char INFECTED_MODELS[COMMON_MODELS_COUNT][] = {
|
||||
|
@ -50,13 +51,15 @@ static char WORKER_MODELS[3][] = {
|
|||
"models/infected/common_male_roadcrew.mdl",
|
||||
"models/infected/common_male_roadcrew_rain.mdl"
|
||||
};
|
||||
enum CommonTypes {
|
||||
enum CommonType {
|
||||
Common_Worker = -2,
|
||||
Common_Any = -1,
|
||||
Common_Clown,
|
||||
Common_Mud,
|
||||
Common_Ceda,
|
||||
Common_Riot,
|
||||
Common_Jimmy,
|
||||
Common_Worker = -1,
|
||||
Common_Fallen,
|
||||
};
|
||||
|
||||
//TODO: Add back survivor zombie, inc z_fallen_max_count
|
||||
|
@ -67,6 +70,8 @@ public void OnPluginStart() {
|
|||
SetFailState("This plugin is for L4D2 only.");
|
||||
}
|
||||
|
||||
commonType = new AnyMap();
|
||||
|
||||
HookEvent("game_start", OnGameStart);
|
||||
|
||||
hPercentTotal = CreateConVar("l4d2_population_global_chance", "1.0", "The % chance that any the below chances occur.\n0.0 = NEVER, 1.0: ALWAYS");
|
||||
|
@ -114,6 +119,7 @@ public void OnMapEnd() {
|
|||
IsDoneLoading = false;
|
||||
iCurrentCommons = 0;
|
||||
clownCommonsSpawned = 0;
|
||||
commonType.Clear();
|
||||
}
|
||||
|
||||
public void CVAR_hTotalZombiesChanged(ConVar convar, const char[] oldValue, const char[] newValue) {
|
||||
|
@ -137,28 +143,28 @@ public void OnEntityCreated(int entity, const char[] classname) {
|
|||
if(GetRandomFloat() <= hPercentTotal.FloatValue) {
|
||||
if(GetRandomFloat() <= hPercentClown.FloatValue) {
|
||||
SetEntityModel(entity, INFECTED_MODELS[Common_Clown]);
|
||||
commonType[entity] = 2;
|
||||
commonType.SetValue(entity, Common_Clown);
|
||||
}else if(GetRandomFloat() <= hPercentMud.FloatValue) {
|
||||
SetEntityModel(entity, INFECTED_MODELS[Common_Mud]);
|
||||
commonType[entity] = 3;
|
||||
commonType.SetValue(entity, Common_Mud);
|
||||
}else if(GetRandomFloat() <= hPercentCeda.FloatValue) {
|
||||
SetEntityModel(entity, INFECTED_MODELS[Common_Ceda]);
|
||||
commonType[entity] = 4;
|
||||
commonType.SetValue(entity, Common_Ceda);
|
||||
}else if(GetRandomFloat() <= hPercentWorker.FloatValue) {
|
||||
//worker has multiple models:
|
||||
SetEntityModel(entity, WORKER_MODELS[GetRandomInt(0,2)]);
|
||||
commonType[entity] = 5;
|
||||
commonType.SetValue(entity, Common_Worker);
|
||||
}else if(GetRandomFloat() <= hPercentRiot.FloatValue) {
|
||||
SetEntityModel(entity, INFECTED_MODELS[Common_Riot]);
|
||||
commonType[entity] = 6;
|
||||
commonType.SetValue(entity, Common_Riot);
|
||||
}else if(GetRandomFloat() <= hPercentJimmy.FloatValue) {
|
||||
SetEntityModel(entity, INFECTED_MODELS[Common_Jimmy]);
|
||||
commonType[entity] = 7;
|
||||
commonType.SetValue(entity, Common_Jimmy);
|
||||
}else{
|
||||
commonType[entity] = 1;
|
||||
commonType.SetValue(entity, Common_Any);
|
||||
}
|
||||
}else{
|
||||
commonType[entity] = 1;
|
||||
commonType.SetValue(entity, Common_Any);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +177,8 @@ public Action Hook_SpawnPost(int entity) {
|
|||
}
|
||||
}
|
||||
++iCurrentCommons;
|
||||
if(commonType[entity] == 2) {
|
||||
CommonType type;
|
||||
if(commonType.GetValue(entity, type) && type == Common_Clown) {
|
||||
if(++clownCommonsSpawned > CLOWN_MUSIC_THRESHOLD && !clownMusicPlayed) {
|
||||
clownMusicPlayed = true;
|
||||
EmitSoundToAll("custom/clown.mp3");
|
||||
|
@ -189,11 +196,12 @@ public Action Hook_SpawnPost(int entity) {
|
|||
// }
|
||||
|
||||
public void OnEntityDestroyed(int entity) {
|
||||
if(entity > 0 && entity <= 2048 && commonType[entity] > 0) {
|
||||
commonType[entity] = 0;
|
||||
if(commonType[entity] == 2) {
|
||||
if(entity > 0 && entity <= 2048 && commonType.ContainsKey(entity)) {
|
||||
CommonType type;
|
||||
if(commonType.GetValue(entity, type) && type == Common_Clown) {
|
||||
--clownCommonsSpawned;
|
||||
}
|
||||
commonType.Remove(entity);
|
||||
if(--iCurrentCommons < CLOWN_MUSIC_THRESHOLD - 10) {
|
||||
clownMusicPlayed = false;
|
||||
}
|
||||
|
|
|
@ -273,6 +273,7 @@ public void OnMapEnd() {
|
|||
Game.UnsetupPlayer(i);
|
||||
}
|
||||
}
|
||||
Game.Cleanup();
|
||||
}
|
||||
|
||||
void ClearInventory(int client) {
|
||||
|
@ -347,7 +348,7 @@ stock void GlowEntity(int entity, int client, float lifetime = 5.0) {
|
|||
GetEntPropVector(entity, Prop_Data, "m_vecMaxs", maxs);
|
||||
GetEntPropVector(entity, Prop_Data, "m_angRotation", ang);
|
||||
|
||||
Effect_DrawBeamBoxRotatableToClient(client, pos, mins, maxs, ang, g_iLaserIndex, 0, 0, 1, lifetime, 1.0, 1.0, 100, 0.1, COLOR_PROPFINDER, 0.0);
|
||||
Effect_DrawBeamBoxRotatableToClient(client, pos, mins, maxs, ang, g_iLaserIndex, 0, 0, 1, lifetime, 1.0, 1.0, 100, 0.1, COLOR_PROPFINDER, 0);
|
||||
}
|
||||
|
||||
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]) {
|
||||
|
|
|
@ -181,7 +181,7 @@ public void Event_PlayerToBot(Handle event, char[] name, bool dontBroadcast)
|
|||
}
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if (strcmp(g_Models[player], survivor_models[i], false) == 0) {
|
||||
// SetClientInfo(bot, "name", survivor_names[i]);
|
||||
SetClientInfo(bot, "name", survivor_names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
|
|||
// TODO: escape content
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgsTrimmed);
|
||||
DB.Query(DB_AddNote, query);
|
||||
LogAction(client, -1, "\"%L\" added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgsTrimmed);
|
||||
LogAction(client, -1, "added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgsTrimmed);
|
||||
Format(buffer, sizeof(buffer), "%N: ", client);
|
||||
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTargetName, sArgs);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue