/*
* CSB Earthquake
* Copyright (C) 2026 counter-strike-boost.com
*
* Shakes everyone's screen with the ScreenShake message, either on an admin
* command (amx_earthquake) or automatically at random intervals. While the
* quake runs it can also give grounded players a small random push and plays an
* optional rumble sound. Duration, amplitude and the push are cvar-controlled.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version. It is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See for details.
*/
#include
#include
#include
#include
new const PLUGIN[] = "CSB Earthquake"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define TASK_AUTO 9100
#define TASK_SHAKE 9101
new g_pEnabled, g_pAuto, g_pMinInt, g_pMaxInt
new g_pDuration, g_pAmp, g_pPush, g_pSound
new bool:g_bQuaking = false
new g_iTicks = 0
new g_szSound[64]
new g_msgShake
public plugin_precache()
{
g_pSound = register_cvar("csb_quake_sound", "")
get_pcvar_string(g_pSound, g_szSound, charsmax(g_szSound))
if (g_szSound[0])
precache_sound(g_szSound)
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_quake_enabled", "1")
g_pAuto = register_cvar("csb_quake_auto", "1")
g_pMinInt = register_cvar("csb_quake_min_interval", "120")
g_pMaxInt = register_cvar("csb_quake_max_interval", "300")
g_pDuration = register_cvar("csb_quake_duration", "5")
g_pAmp = register_cvar("csb_quake_amplitude", "8")
g_pPush = register_cvar("csb_quake_push", "1")
register_concmd("amx_earthquake", "cmdQuake", ADMIN_LEVEL_A, "[seconds] - trigger an earthquake")
g_msgShake = get_user_msgid("ScreenShake")
scheduleNext()
}
scheduleNext()
{
remove_task(TASK_AUTO)
if (!get_pcvar_num(g_pAuto))
return
new mn = get_pcvar_num(g_pMinInt)
new mx = get_pcvar_num(g_pMaxInt)
if (mn < 10)
mn = 10
if (mx < mn)
mx = mn
set_task(float(random_num(mn, mx)), "taskAutoQuake", TASK_AUTO)
}
public taskAutoQuake()
{
if (get_pcvar_num(g_pEnabled) && get_pcvar_num(g_pAuto))
startQuake(get_pcvar_num(g_pDuration))
scheduleNext()
}
public cmdQuake(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
if (!get_pcvar_num(g_pEnabled))
{
console_print(id, "[CSB] Earthquake is disabled (csb_quake_enabled 0).")
return PLUGIN_HANDLED
}
new secs = get_pcvar_num(g_pDuration)
if (read_argc() > 1)
{
new arg[8]
read_argv(1, arg, charsmax(arg))
new n = str_to_num(arg)
if (n > 0)
secs = n
}
new aname[32]
if (id)
get_user_name(id, aname, charsmax(aname))
else
copy(aname, charsmax(aname), "CONSOLE")
log_amx("[CSB Quake] %s triggered an earthquake (%d s)", aname, secs)
startQuake(secs)
return PLUGIN_HANDLED
}
startQuake(secs)
{
if (g_bQuaking)
return
if (secs < 1)
secs = 1
g_bQuaking = true
g_iTicks = secs * 4
client_print(0, print_chat, "^x04[CSB]^x01 The ground is shaking! EARTHQUAKE!")
if (g_szSound[0])
{
new players[32], num
get_players(players, num, "c")
for (new i = 0; i < num; i++)
emit_sound(players[i], CHAN_STATIC, g_szSound, VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
}
set_task(0.25, "taskShake", TASK_SHAKE, _, _, "b")
}
public taskShake()
{
if (!g_bQuaking)
{
remove_task(TASK_SHAKE)
return
}
g_iTicks--
new amp = get_pcvar_num(g_pAmp)
new push = get_pcvar_num(g_pPush)
new players[32], num, pid
get_players(players, num, "a")
for (new i = 0; i < num; i++)
{
pid = players[i]
message_begin(MSG_ONE, g_msgShake, _, pid)
write_short(amp << 12) // amplitude
write_short(1 << 12) // duration
write_short(amp << 12) // frequency
message_end()
if (push && (pev(pid, pev_flags) & FL_ONGROUND))
{
new Float:vel[3]
pev(pid, pev_velocity, vel)
vel[0] += random_float(-90.0, 90.0)
vel[1] += random_float(-90.0, 90.0)
vel[2] += random_float(70.0, 160.0)
set_pev(pid, pev_velocity, vel)
}
}
if (g_iTicks <= 0)
{
g_bQuaking = false
remove_task(TASK_SHAKE)
}
}