diff --git a/plugins/l4d2_hideandseek.smx b/plugins/l4d2_hideandseek.smx index 29face1..0bd8f47 100644 Binary files a/plugins/l4d2_hideandseek.smx and b/plugins/l4d2_hideandseek.smx differ diff --git a/scripting/include/hideandseek/hscmds.inc b/scripting/include/hideandseek/hscmds.inc index bc42d77..6e7fe8d 100644 --- a/scripting/include/hideandseek/hscmds.inc +++ b/scripting/include/hideandseek/hscmds.inc @@ -41,7 +41,7 @@ public Action Command_HideAndSeek(int client, int args) { } Cleanup(); SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); - ReplyToCommand(client, "Set the current set to \"%s\"", currentSet); + PrintToChatAll("[H&S] Map set has been changed to \"%s\"", currentSet); return Plugin_Handled; } } @@ -216,9 +216,19 @@ public Action Command_HideAndSeek(int client, int args) { } if(IsBotsEnabled()) ReplyToCommand(client, "Bots are enabled"); else ReplyToCommand(client, "Bots are disabled"); - } else if(StrEqual(subcmd, "peek")) { - SetPeekCamTarget(client); - SetPeekCamActive(client, !IsPeekCamActive(client)); + } else if(StrEqual(subcmd, "peekfix")) { + if(seekerCam == INVALID_ENT_REFERENCE) { + SetPeekCamTarget(client); + } + + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i)) { + SetPeekCamActive(client, false); + SetPeekCamActive(client, false); + } + } + AcceptEntityInput(seekerCam, "Kill"); + ReplyToCommand(client, "Killing active camera"); } else if(StrEqual(subcmd, "seeker")) { if(args == 2) { char arg1[32]; @@ -240,8 +250,7 @@ public Action Command_HideAndSeek(int client, int args) { ReplyToTargetError(client, target_count); return Plugin_Handled; } - - SetSlasher(target_list[0]); + SetSlasher(target_list[0], true); ReplyToCommand(client, "Set the current seeker to %N", target_list[0]); } else { ReplyToCommand(client, "The current seeker is: %N", GetSlasher()); @@ -264,17 +273,24 @@ public Action Command_HideAndSeek(int client, int args) { } return Plugin_Handled; } - ReplyToCommand(client, " - Hide & Seek Commands -"); - ReplyToCommand(client, "toggle : Toggles all specified"); - ReplyToCommand(client, "set [new set]: Change the prop set or view current"); - ReplyToCommand(client, "clear : Clear all specified"); - ReplyToCommand(client, "settime [seconds]: Sets the time override for the map"); - ReplyToCommand(client, "settick [tick]: Sets the current tick timer value"); - ReplyToCommand(client, "setspawn: Sets the temporary spawnpoint for the map"); + ReplyToCommand(client, " === [ Hide & Seek Commands ] ==="); + if(GetUserAdmin(client) != INVALID_ADMIN_ID) { + ReplyToCommand(client, "- Dev Commands -"); + ReplyToCommand(client, "r/reload [force]: Reloads map config from file"); + ReplyToCommand(client, "toggle : Toggles all specified entities"); + ReplyToCommand(client, "clear : Clear all specified"); + ReplyToCommand(client, "settime [seconds]: Sets the time override for the map"); + ReplyToCommand(client, "settick [tick]: Sets the current tick timer value"); + ReplyToCommand(client, "- Admin Commands -"); + ReplyToCommand(client, "set [new set]: Change the prop set or view current"); + ReplyToCommand(client, "setspawn: Sets the temporary spawnpoint for the map"); + ReplyToCommand(client, "bots [toggle, [value]]: View if bots are enabled, or turn them on"); + ReplyToCommand(client, "peekfix - Clear peek camera from all players"); + ReplyToCommand(client, "seeker [new seeker]: Get the active seeker, or set a new one."); + ReplyToCommand(client, "sm_cvar hs_peekcam <0/2> - Turn the peek camera on or off"); + ReplyToCommand(client, "- User Commands -"); + } ReplyToCommand(client, "stuck: Teleports you to spawn to unstuck yourself"); - ReplyToCommand(client, "bots [toggle, [value]]: View if bots are enabled, or turn them on"); - ReplyToCommand(client, "seeker [new seeker]: Get the active seeker, or set a new one."); - ReplyToCommand(client, "peek - debug cmd"); return Plugin_Handled; } diff --git a/scripting/include/hideandseek/hscore.inc b/scripting/include/hideandseek/hscore.inc index 5d51238..72c1d22 100644 --- a/scripting/include/hideandseek/hscore.inc +++ b/scripting/include/hideandseek/hscore.inc @@ -33,18 +33,22 @@ bool ReloadMapDB() { } - bool LoadConfigForMap(const char[] map) { kv.Rewind(); if (kv.JumpToKey(map)) { - PrintToServer("[H&S] Loading config data for set %s on %s", currentSet, map); MapConfig config; config.entities = new ArrayList(sizeof(EntityConfig)); config.inputs = new ArrayList(ByteCountToCells(64)); validSets.Clear(); + + static char buffer[64]; + if(StrEqual(currentSet, "default") && kv.GetString("defaultset", buffer, sizeof(buffer))) { + strcopy(currentSet, sizeof(currentSet), buffer); + } + PrintToServer("[H&S] Loading config data for set %s on %s", currentSet, map); + if(kv.JumpToKey("ents")) { kv.GotoFirstSubKey(); - char entSet[16]; do { EntityConfig entCfg; kv.GetVector("origin", entCfg.origin, NULL_VECTOR); @@ -55,17 +59,16 @@ bool LoadConfigForMap(const char[] map) { Format(entCfg.model, sizeof(entCfg.model), "models/%s", entCfg.model); kv.GetVector("scale", entCfg.scale, DEFAULT_SCALE); kv.GetVector("offset", entCfg.offset, NULL_VECTOR); - kv.GetString("set", entSet, sizeof(entSet), "default"); - if(validSets.FindString(entSet) == -1) { - validSets.PushString(entSet); + kv.GetString("set", buffer, sizeof(buffer), "default"); + if(validSets.FindString(buffer) == -1) { + validSets.PushString(buffer); } - char debug_str[64]; - if(StrEqual(entSet, "default") || StrEqual(currentSet, entSet, false)) { + if(StrEqual(buffer, "default") || StrEqual(currentSet, buffer, false)) { config.entities.PushArray(entCfg); } else { - kv.GetSectionName(debug_str, sizeof(debug_str)); - PrintToServer("Skipping %s", debug_str); + kv.GetSectionName(buffer, sizeof(buffer)); + PrintToServer("Skipping %s", buffer); } } while (kv.GotoNextKey()); // JumpToKey and GotoFirstSubKey both traverse, i guess, go back @@ -74,7 +77,6 @@ bool LoadConfigForMap(const char[] map) { } if(kv.JumpToKey("inputs")) { kv.GotoFirstSubKey(false); - static char buffer[64]; do { kv.GetSectionName(buffer, sizeof(buffer)); config.inputs.PushString(buffer); @@ -110,6 +112,18 @@ bool LoadConfigForMap(const char[] map) { kv.GetString("buttons", buf, sizeof(buf)); config.pressButtons = !StrEqual(buf, "no"); mapTime = kv.GetNum("maptime", 0); + if(kv.JumpToKey("inputs")) { + kv.GotoFirstSubKey(false); + do { + kv.GetSectionName(buffer, sizeof(buffer)); + config.inputs.PushString(buffer); + + kv.GetString(NULL_STRING, buffer, sizeof(buffer)); + config.inputs.PushString(buffer); + } while (kv.GotoNextKey(false)); + kv.GoBack(); + kv.GoBack(); + } break; } @@ -164,4 +178,23 @@ bool LoadConfigForMap(const char[] map) { PrintToServer("[H&S] No map config exists for %s", map); return false; } +} + +int COLOR_GREY[4] = { 194, 197, 204, 255 }; +int COLOR_GREEN[4] = { 0, 128, 0, 255 }; + +void ShowBeacon(int target, float radius = 100.0) { + float vec[3]; + GetClientAbsOrigin(target, vec); + vec[2] += 10; + + if (g_BeamSprite > -1 && g_HaloSprite > -1) { + TE_SetupBeamRingPoint(vec, 10.0, radius, g_BeamSprite, g_HaloSprite, 0, 15, 0.5, 5.0, 0.0, COLOR_GREY, 10, 0); + TE_SendToAll(); + TE_SetupBeamRingPoint(vec, 10.0, radius, g_BeamSprite, g_HaloSprite, 0, 10, 0.6, 10.0, 0.5, COLOR_GREEN, 10, 0); + TE_SendToAll(); + } + + GetClientEyePosition(target, vec); + EmitAmbientSound("buttons/button17.wav", vec, target, SNDLEVEL_RAIDSIREN); } \ No newline at end of file diff --git a/scripting/include/hideandseek/hsgame.inc b/scripting/include/hideandseek/hsgame.inc index 0f8f906..0b3b658 100644 --- a/scripting/include/hideandseek/hsgame.inc +++ b/scripting/include/hideandseek/hsgame.inc @@ -60,7 +60,10 @@ int FindSlasher() { return -1; } -void SetSlasher(int client) { +void SetSlasher(int client, bool ignoreBalance = false) { + if(ignoreBalance) { + ignoreSeekerBalance = true; + } GameState state = GetState(); char buf[128]; for(int i = 1; i <= MaxClients; i++) { @@ -198,7 +201,21 @@ void SetParent(int child, int parent) { AcceptEntityInput(child, "SetParent", parent); } -void SetPeekCamTarget(int target, int src = 0) { +void SetParentAttachment(int child, const char[] attachment, bool withOffset = false) { + SetVariantString(attachment); + if(withOffset) + AcceptEntityInput(child, "SetParentAttachmentMaintainOffset"); + else + AcceptEntityInput(child, "SetParentAttachment"); +} + +void ClearParent(int child) { + AcceptEntityInput(child, "ClearParent"); +} + +static float EMPTY_ANG[3] = { 0.0, 0.0, 0.0 }; + +void SetPeekCamTarget(int target, bool showFPOV = false) { if(seekerCam == INVALID_ENT_REFERENCE || !IsValidEntity(seekerCam)) { seekerCam = CreateEntityByName("point_viewcontrol_survivor"); DispatchKeyValue(seekerCam, "targetname", "hscam"); @@ -206,24 +223,19 @@ void SetPeekCamTarget(int target, int src = 0) { for(int i = 0; i <= MaxClients; i++) { isViewingCam[i] = false; } - PrintToServer("created new peek cam %d", seekerCam); } AcceptEntityInput(seekerCam, "ClearParent"); + AcceptEntityInput(seekerCam, "Disable"); float pos[3], endPos[3], ang[3]; GetClientEyePosition(target, pos); GetClientEyeAngles(target, ang); - if(src) { - pos[2] += 20.0; + if(showFPOV) { TeleportEntity(seekerCam, pos, ang, NULL_VECTOR); - // SetParent(seekerCam, src); + SetParent(seekerCam, target); + SetParentAttachment(seekerCam, "primary", false); } else { - /*GetHorizontalPositionFromClient(target, -20.0, endPos); - endPos[2] += 80.0; - TeleportEntity(seekerCam, endPos, ang, NULL_VECTOR); - SetParent(seekerCam, target);*/ - TR_TraceRayFilter(pos, ang, CONTENTS_PLAYERCLIP | MASK_SOLID | MASK_VISIBLE, RayType_Infinite, Filter_IgnoreAll); if(TR_DidHit()) { TR_GetEndPosition(endPos); @@ -237,13 +249,27 @@ void SetPeekCamTarget(int target, int src = 0) { ang[0] = RadToDeg(ArcTangent(deltaC / GetVectorDistance(endPos, pos, false) )); ang[1] = RadToDeg(ArcTangent2(deltaA, deltaB)); - // pos[2] += 50.0; - // GetAnglesLookAt(seekerCam, target, ang); TeleportEntity(seekerCam, endPos, ang, NULL_VECTOR); - // SetParent(seekerCam, target);*/ } } +bool IsPeekCamActive(int client) { + return isViewingCam[client]; +} + +// int GetClientsInRange(const float origin[3], ClientRangeType rangeType, int[] clients, int size) +void SetPeekCamActive(int client, bool active) { + if(seekerCam != INVALID_ENT_REFERENCE) { + AcceptEntityInput(seekerCam, "Enable", client); // Need to always activate before deactivating to fix a semi-common bug + if(!active) + AcceptEntityInput(seekerCam, "Disable", client); + } else { + PrintToServer("WARN: SetPeekCamActive(%d, %b) when seekerCam invalid", client, active); + } + isViewingCam[client] = active; +} + + stock void GetAnglesLookAt(int iClient, int iTarget, float fFinalPos[3]) { static float fTargetPos[3]; static float fTargetAngles[3]; @@ -279,21 +305,4 @@ stock void GetViewVector(float fVecAngle[3], float fOutPut[3]) bool Filter_IgnoreAll(int entity, int mask) { return false; -} - -bool IsPeekCamActive(int client) { - return isViewingCam[client]; -} - -// int GetClientsInRange(const float origin[3], ClientRangeType rangeType, int[] clients, int size) -void SetPeekCamActive(int client, bool active) { - if(seekerCam != INVALID_ENT_REFERENCE) { - if(active) - AcceptEntityInput(seekerCam, "Enable", client); - else - AcceptEntityInput(seekerCam, "Disable", client); - } else { - PrintToServer("warn: SetPeekCamActive(%d, %b) when seekerCam invalid", client, active); - } - isViewingCam[client] = active; } \ No newline at end of file diff --git a/scripting/l4d2_hideandseek.sp b/scripting/l4d2_hideandseek.sp index c64aab3..3d4a437 100644 --- a/scripting/l4d2_hideandseek.sp +++ b/scripting/l4d2_hideandseek.sp @@ -26,6 +26,11 @@ public Plugin myinfo = url = "" }; +/*TODO: +2. Seeker helping +3. flare on hunted +*/ + /* script g_ModeScript.DeepPrintTable(g_ModeScript.MutationState) { @@ -44,6 +49,7 @@ script g_ModeScript.DeepPrintTable(g_ModeScript.MutationState) #define SOUND_SUSPENSE_1 "custom/suspense1.mp3" #define SOUND_SUSPENSE_1_FAST "custom/suspense1fast.mp3" +#define SOUND_SHAKE "doors/gate_move1.wav" enum GameState { State_Unknown = -1, @@ -74,6 +80,9 @@ char nextRoundMap[64]; int seekerCam = INVALID_ENT_REFERENCE; bool isViewingCam[MAXPLAYERS+1]; +int g_BeamSprite; +int g_HaloSprite; + enum struct EntityConfig { float origin[3]; float rotation[3]; @@ -97,6 +106,12 @@ MapConfig mapConfig; ArrayList validMaps; ArrayList validSets; +bool hasBeenSeeker[MAXPLAYERS+1]; +bool ignoreSeekerBalance; + +ConVar cvar_peekCam; +ConVar cvar_seekerBalance; + #include public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { @@ -118,8 +133,10 @@ public void OnPluginStart() { SetFailState("Could not load entity config from data/hideandseek.cfg"); } + cvar_peekCam = CreateConVar("hs_peekcam", "3", "Controls the peek camera on events. Set bits\n0 = OFF, 1 = On Game End, 2 = Any death", FCVAR_NONE, true, 0.0, true, 3.0); + cvar_seekerBalance = CreateConVar("hs_seekerbalance", "1", "Enable or disable ensuring every player has played as seeker", FCVAR_NONE, true, 0.0, true, 1.0); + ConVar hGamemode = FindConVar("mp_gamemode"); - hGamemode.GetString(gamemode, sizeof(gamemode)); hGamemode.AddChangeHook(Event_GamemodeChange); Event_GamemodeChange(hGamemode, gamemode, gamemode); @@ -133,6 +150,7 @@ public void OnPluginEnd() { } public void OnClientConnected(int client) { + hasBeenSeeker[client] = false; if(!IsFakeClient(client)) { currentPlayers++; if(isEnabled) { @@ -186,24 +204,29 @@ public void OnMapStart() { #endif PrecacheSound(SOUND_SUSPENSE_1); PrecacheSound(SOUND_SUSPENSE_1_FAST); + PrecacheSound(SOUND_SHAKE); AddFileToDownloadsTable("sound/custom/suspense1.mp3"); AddFileToDownloadsTable("sound/custom/suspense1fast.mp3"); + g_BeamSprite = PrecacheModel("sprites/laser.vmt"); + g_HaloSprite = PrecacheModel("sprites/halo01.vmt"); + PrecacheSound("buttons/button17.wav", true); + if(lateLoaded) { - lateLoaded = false; SetupEntities(); int seeker = GetSlasher(); if(seeker > -1) { currentSeeker = seeker; + SetPeekCamTarget(currentSeeker); PrintToServer("[H&S] Late load, found seeker %N", currentSeeker); } - SetPeekCamTarget(currentSeeker); if(IsGameSoloOrPlayersLoading()) { Handle timer = CreateTimer(10.0, Timer_KeepWaiting, _, TIMER_REPEAT); TriggerTimer(timer); PrintToServer("[H&S] Late load, player(s) are connecting, or solo. Waiting..."); SetState(State_Startup); } + lateLoaded = false; } } @@ -225,13 +248,17 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ isEnabled = StrEqual(gamemode, "hideandseek", false); #endif if(isEnabled) { + PrintToChatAll("[H&S] Hide and seek gamemode activated, starting"); HookEvent("round_end", Event_RoundEnd); HookEvent("round_start", Event_RoundStart); HookEvent("player_spawn", Event_PlayerSpawn); HookEvent("item_pickup", Event_ItemPickup); HookEvent("player_death", Event_PlayerDeath); - SetupEntities(); - CreateTimer(12.0, Timer_RoundStart); + // OnMapStart(); // Don't use, need to track OnMapStart + if(lateLoaded) + CreateTimer(2.0, Timer_RoundStart); + else + CreateTimer(10.0, Timer_RoundStart); if(suspenseTimer != null) delete suspenseTimer; suspenseTimer = CreateTimer(20.0, Timer_Music, _, TIMER_REPEAT); @@ -240,8 +267,9 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ thirdPersonTimer = CreateTimer(1.0, Timer_CheckPlayers, _, TIMER_REPEAT); if(!lateLoaded) { for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i)) + if(IsClientConnected(i) && IsClientInGame(i)) { ForcePlayerSuicide(i); + } } } } else if(!lateLoaded && suspenseTimer != null) { @@ -258,7 +286,6 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ const float DEATH_CAM_MIN_DIST = 150.0; public Action Timer_StopPeekCam(Handle h) { - PrintToServer("clearing cam"); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { SetPeekCamActive(i, false); @@ -271,37 +298,42 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast int attacker = GetClientOfUserId(event.GetInt("attacker")); if(!gameOver && client && GetClientTeam(client) == 2) { - SetPeekCamTarget(attacker > 0 ? attacker : client, client); + SetPeekCamTarget(attacker > 0 ? attacker : client, true); int alive = 0; float pos[3], checkPos[3]; + if(attacker <= 0) attacker = client; GetClientAbsOrigin(attacker, pos); SetPeekCamActive(attacker, true); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { if(attacker > 0 && attacker != client) { - GetClientAbsOrigin(i, checkPos); - if(GetVectorDistance(checkPos, pos) > DEATH_CAM_MIN_DIST) { - SetPeekCamActive(i, true); + if(cvar_peekCam.IntValue & 2) { + GetClientAbsOrigin(i, checkPos); + if(GetVectorDistance(checkPos, pos) > DEATH_CAM_MIN_DIST) { + SetPeekCamActive(i, true); + } } alive++; } } } - if(client == currentSeeker) { + + if(client == currentSeeker && alive == 1) { PrintToChatAll("Hiders win!"); gameOver = true; } else { if(alive == 2) { PrintToChatAll("One hider remains."); - } else if(alive == 1) { + } else if(alive <= 0) { // Player died and not seeker, therefore seeker killed em if(client != currentSeeker) { PrintToChatAll("Seeker %N won!", currentSeeker); } else { - SetPeekCamTarget(client); PrintToChatAll("Hiders win! The last survivor was %N!", client); - + } + if(cvar_peekCam.IntValue & 1) { + SetPeekCamTarget(client, false); } gameOver = true; return; @@ -343,6 +375,33 @@ public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) PrintToChatAll("[H&S] Seeker does not equal axe-receiver. Possible seeker: %N", client); } SetPeekCamTarget(currentSeeker); + if(!ignoreSeekerBalance && cvar_seekerBalance.BoolValue) { + if(hasBeenSeeker[currentSeeker]) { + ArrayList notPlayedSeekers = new ArrayList(1); + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && !hasBeenSeeker[i]) { + notPlayedSeekers.Push(i); + } + } + if(notPlayedSeekers.Length > 0) { + int newSlasher = notPlayedSeekers.Get(GetURandomInt() % notPlayedSeekers.Length); + PrintToServer("[H&S] Switching seeker to a new random seeker: %N", newSlasher); + SetSlasher(newSlasher); + return; + } else { + PrintToServer("[H&S] All players have played as seeker, resetting"); + PrintToChatAll("[H&S] Everyone has played as a seeker once"); + for(int i = 1; i <= MaxClients; i++) { + hasBeenSeeker[i] = false; + } + hasBeenSeeker[currentSeeker] = true; + } + delete notPlayedSeekers; + } else { + hasBeenSeeker[currentSeeker] = true; + } + } + ignoreSeekerBalance = false; PrintToChatAll("%N is the seeker", currentSeeker); } } @@ -367,6 +426,7 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) } } EntFire("relay_intro_start", "Kill"); + EntFire("outro", "Kill"); SetupEntities(); CreateTimer(15.0, Timer_RoundStart); } @@ -433,6 +493,7 @@ public Action Timer_Music(Handle h) { if(state == State_Hunting) { if(prevState == State_Hiding) { changedToHunting = true; + ShowBeacon(currentSeeker); SetPeekCamTarget(currentSeeker); } EmitSoundToClient(currentSeeker, SOUND_SUSPENSE_1, currentSeeker, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 0.2, 90, currentSeeker, seekerLoc, seekerLoc, true); @@ -478,10 +539,6 @@ public Action Timer_RoundStart(Handle h) { } } entity = INVALID_ENT_REFERENCE; - while ((entity = FindEntityByClassname(entity, "func_brush")) != INVALID_ENT_REFERENCE) { - AcceptEntityInput(entity, "Kill"); - } - entity = INVALID_ENT_REFERENCE; while ((entity = FindEntityByClassname(entity, "infected")) != INVALID_ENT_REFERENCE) { AcceptEntityInput(entity, "Kill"); } @@ -493,9 +550,14 @@ public Action Timer_RoundStart(Handle h) { } if(mapConfig.canClimb) { while ((entity = FindEntityByClassname(entity, "func_simpleladder")) != INVALID_ENT_REFERENCE) { + SDKHook(entity, SDKHook_TraceAttackPost, Hook_OnAttackPost); SetEntProp(entity, Prop_Send, "m_iTeamNum", 0); } } + while ((entity = FindEntityByClassname(entity, "env_soundscape")) != INVALID_ENT_REFERENCE) { + AcceptEntityInput(entity, "Disable"); + AcceptEntityInput(entity, "Kill"); + } if(mapConfig.mapTime > 0) { SetMapTime(mapConfig.mapTime); } @@ -503,6 +565,30 @@ public Action Timer_RoundStart(Handle h) { PrintToServer("[H&S] Map time is %d seconds", GetMapTime()); return Plugin_Continue; } +static float SHAKE_SIZE[3] = { 40.0, 40.0, 20.0 }; + +static float lastShakeTime; +public void Hook_OnAttackPost(int entity, int attacker, int inflictor, float damage, int damagetype, int ammotype, int hitbox, int hitgroup) { + if(attacker == currentSeeker && attacker > 0 && GetGameTime() - lastShakeTime > 2.0) { + lastShakeTime = GetGameTime(); + float min[3], max[3], origin[3], top[3]; + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin); + GetEntPropVector(entity, Prop_Send, "m_vecMins", min); + GetEntPropVector(entity, Prop_Send, "m_vecMaxs", max); + PrintHintTextToAll("Shaking ladder"); + top = origin; + top[2] = max[2]; + origin[2] = min[2]; + TR_EnumerateEntitiesHull(origin, top, min, max, PARTITION_SOLID_EDICTS, Ray_Enumerator, attacker); + EmitAmbientSound(SOUND_SHAKE, top, SOUND_FROM_WORLD, SNDLEVEL_SCREAMING, SND_NOFLAGS, SNDVOL_NORMAL, SNDPITCH_NORMAL); + } +} +bool Ray_Enumerator(int entity, int attacker) { + if(entity > 0 && entity <= MaxClients && entity != attacker) { + L4D_StaggerPlayer(entity, attacker, NULL_VECTOR); + } + return true; +} public Action Timer_CheckWeapons(Handle h) { for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { @@ -515,5 +601,4 @@ public Action Timer_CheckWeapons(Handle h) { } } return Plugin_Continue; -} - +} \ No newline at end of file