cs16-dm-server: CS 1.6 deathmatch server in Docker
One-command setup (SteamCMD + pinned, checksum-verified ReHLDS / ReGameDLL / Metamod-R / AMXX / ReAPI / ReUnion / CSDM-ReAPI / YaPB / GunGame), .env settings, ./cs menu (map, DM/GunGame mode, bots, kill counter, restart, update), own plugins (extras, rtv, mode switch), all-time kill counter, watchdog + nightly backup, docs for admins and players. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
7e9cbee14d
33 files changed
+1890
No files matched your search
@@ -0,0 +1,233 @@
|
||||
// esef_extras.sma - game-feel extras for cs16-dm-server.
|
||||
// - damage numbers: health you took off (blue, right of crosshair) / lost (red, left)
|
||||
// - killstreak + multikill announcements (DHUD) with Quake/UT announcer sounds
|
||||
// - headshot / knife / first blood sounds
|
||||
// - HP on kill (esef_kill_hp, esef_kill_hp_hs), capped at 100
|
||||
// - small HUD line: kills, deaths, streak, best streak (esef_hudinfo; hidden while GunGame runs)
|
||||
// Sounds live in cstrike/sound/misc and are precached, so clients download them once (~800 KB).
|
||||
|
||||
#include <amxmodx>
|
||||
#include <reapi>
|
||||
|
||||
#define MULTIKILL_WINDOW 2.0
|
||||
#define TASK_HUD 4100
|
||||
|
||||
new const SND_HEADSHOT[] = "misc/headshot.wav"
|
||||
new const SND_FIRSTBLOOD[] = "misc/firstblood.wav"
|
||||
new const SND_KNIFE[] = "misc/humiliation.wav"
|
||||
|
||||
// multikill chain: 2, 3, 4, 5+ kills inside MULTIKILL_WINDOW
|
||||
new const SND_MULTI[][] = { "misc/doublekill.wav", "misc/multikill.wav", "misc/ultrakill.wav", "misc/monsterkill.wav" }
|
||||
new const TXT_MULTI[][] = { "DOUBLE KILL", "MULTI KILL", "ULTRA KILL", "MONSTER KILL" }
|
||||
|
||||
// streaks without dying
|
||||
new const STREAK_AT[] = { 3, 5, 7, 9, 11, 13, 15 }
|
||||
new const SND_STREAK[][] = {
|
||||
"misc/killingspree.wav", "misc/rampage.wav", "misc/dominating.wav", "misc/unstoppable.wav",
|
||||
"misc/godlike.wav", "misc/wickedsick.wav", "misc/holyshit.wav"
|
||||
}
|
||||
new const TXT_STREAK[][] = {
|
||||
"is on a KILLING SPREE", "is on a RAMPAGE", "is DOMINATING", "is UNSTOPPABLE",
|
||||
"is GODLIKE", "is WICKED SICK", "HOLY SHIT"
|
||||
}
|
||||
|
||||
new g_streak[MAX_PLAYERS + 1], g_best[MAX_PLAYERS + 1], g_kills[MAX_PLAYERS + 1], g_deaths[MAX_PLAYERS + 1]
|
||||
new g_chain[MAX_PLAYERS + 1], Float:g_lastKill[MAX_PLAYERS + 1]
|
||||
new Float:g_preHealth[MAX_PLAYERS + 1]
|
||||
new bool:g_firstBlood
|
||||
new g_syncDealt, g_syncTaken, g_syncInfo
|
||||
new g_pKillHp, g_pKillHpHs, g_pHudInfo, g_pSounds, g_pDamage
|
||||
new bool:g_gunGame // GunGame shows its own level/kills HUD, so skip ours
|
||||
|
||||
public plugin_precache()
|
||||
{
|
||||
precache_sound(SND_HEADSHOT)
|
||||
precache_sound(SND_FIRSTBLOOD)
|
||||
precache_sound(SND_KNIFE)
|
||||
for (new i = 0; i < sizeof SND_MULTI; i++)
|
||||
precache_sound(SND_MULTI[i])
|
||||
for (new i = 0; i < sizeof SND_STREAK; i++)
|
||||
precache_sound(SND_STREAK[i])
|
||||
}
|
||||
|
||||
public plugin_init()
|
||||
{
|
||||
register_plugin("DM extras", "1.0", "cs16-dm-server")
|
||||
|
||||
g_pKillHp = register_cvar("esef_kill_hp", "10")
|
||||
g_pKillHpHs = register_cvar("esef_kill_hp_hs", "20")
|
||||
g_pHudInfo = register_cvar("esef_hudinfo", "1")
|
||||
g_pSounds = register_cvar("esef_sounds", "1")
|
||||
g_pDamage = register_cvar("esef_damage", "1")
|
||||
|
||||
RegisterHookChain(RG_CBasePlayer_TakeDamage, "TakeDamage_Pre", .post = false)
|
||||
RegisterHookChain(RG_CBasePlayer_TakeDamage, "TakeDamage_Post", .post = true)
|
||||
RegisterHookChain(RG_CBasePlayer_Killed, "Killed_Post", .post = true)
|
||||
|
||||
g_syncDealt = CreateHudSyncObj()
|
||||
g_syncTaken = CreateHudSyncObj()
|
||||
g_syncInfo = CreateHudSyncObj()
|
||||
|
||||
set_task(1.0, "ShowInfoAll", TASK_HUD, .flags = "b")
|
||||
}
|
||||
|
||||
public plugin_cfg()
|
||||
{
|
||||
g_gunGame = (find_plugin_byfile("gungame.amxx") != INVALID_PLUGIN_ID)
|
||||
}
|
||||
|
||||
public client_putinserver(id)
|
||||
{
|
||||
g_streak[id] = g_best[id] = g_kills[id] = g_deaths[id] = g_chain[id] = 0
|
||||
g_lastKill[id] = 0.0
|
||||
}
|
||||
|
||||
// --- damage numbers -------------------------------------------------------
|
||||
|
||||
public TakeDamage_Pre(victim, inflictor, attacker, Float:damage, damagebits)
|
||||
{
|
||||
g_preHealth[victim] = Float:get_entvar(victim, var_health)
|
||||
return HC_CONTINUE
|
||||
}
|
||||
|
||||
public TakeDamage_Post(victim, inflictor, attacker, Float:damage, damagebits)
|
||||
{
|
||||
if (!get_pcvar_num(g_pDamage))
|
||||
return HC_CONTINUE
|
||||
|
||||
new Float:now = Float:get_entvar(victim, var_health)
|
||||
if (now < 0.0)
|
||||
now = 0.0
|
||||
new lost = floatround(g_preHealth[victim] - now)
|
||||
if (lost <= 0)
|
||||
return HC_CONTINUE
|
||||
|
||||
if (is_user_connected(victim) && !is_user_bot(victim))
|
||||
{
|
||||
set_hudmessage(220, 40, 40, 0.44, 0.52, 0, 0.0, 1.0, 0.0, 0.3, -1)
|
||||
ShowSyncHudMsg(victim, g_syncTaken, "%d", lost)
|
||||
}
|
||||
if (attacker != victim && is_user_connected(attacker) && !is_user_bot(attacker))
|
||||
{
|
||||
set_hudmessage(40, 160, 255, 0.54, 0.47, 0, 0.0, 1.0, 0.0, 0.3, -1)
|
||||
ShowSyncHudMsg(attacker, g_syncDealt, "%d", lost)
|
||||
}
|
||||
return HC_CONTINUE
|
||||
}
|
||||
|
||||
// --- kills -----------------------------------------------------------------
|
||||
|
||||
public Killed_Post(victim, killer, gib)
|
||||
{
|
||||
if (is_user_connected(victim))
|
||||
{
|
||||
g_deaths[victim]++
|
||||
g_streak[victim] = 0
|
||||
g_chain[victim] = 0
|
||||
ShowInfo(victim)
|
||||
}
|
||||
|
||||
if (killer == victim || !is_user_connected(killer))
|
||||
return HC_CONTINUE
|
||||
|
||||
g_kills[killer]++
|
||||
g_streak[killer]++
|
||||
if (g_streak[killer] > g_best[killer])
|
||||
g_best[killer] = g_streak[killer]
|
||||
|
||||
new Float:t = get_gametime()
|
||||
g_chain[killer] = (t - g_lastKill[killer] <= MULTIKILL_WINDOW) ? g_chain[killer] + 1 : 1
|
||||
g_lastKill[killer] = t
|
||||
|
||||
new bool:headshot = bool:get_member(victim, m_bHeadshotKilled)
|
||||
new bool:knife = (get_user_weapon(killer) == CSW_KNIFE && get_entvar(victim, var_dmg_inflictor) == killer)
|
||||
|
||||
// HP on kill
|
||||
if (is_user_alive(killer))
|
||||
{
|
||||
new bonus = headshot ? get_pcvar_num(g_pKillHpHs) : get_pcvar_num(g_pKillHp)
|
||||
if (bonus > 0)
|
||||
{
|
||||
new Float:hp = Float:get_entvar(killer, var_health) + float(bonus)
|
||||
set_entvar(killer, var_health, hp > 100.0 ? 100.0 : hp)
|
||||
}
|
||||
}
|
||||
|
||||
new name[32]
|
||||
get_user_name(killer, name, charsmax(name))
|
||||
|
||||
// pick one sound per kill: streak (everyone) > multikill (killer) > knife (everyone) > headshot (killer) > first blood
|
||||
new streakIdx = -1
|
||||
for (new i = 0; i < sizeof STREAK_AT; i++)
|
||||
if (g_streak[killer] == STREAK_AT[i])
|
||||
streakIdx = i
|
||||
|
||||
if (streakIdx >= 0)
|
||||
{
|
||||
set_dhudmessage(255, 160, 0, -1.0, 0.25, 0, 0.0, 2.5, 0.1, 0.4)
|
||||
show_dhudmessage(0, "%s %s! (%d)", name, TXT_STREAK[streakIdx], g_streak[killer])
|
||||
PlaySound(0, SND_STREAK[streakIdx])
|
||||
}
|
||||
else if (g_chain[killer] >= 2)
|
||||
{
|
||||
new m = g_chain[killer] - 2
|
||||
if (m >= sizeof TXT_MULTI)
|
||||
m = sizeof TXT_MULTI - 1
|
||||
set_dhudmessage(255, 60, 60, -1.0, 0.30, 0, 0.0, 1.5, 0.1, 0.3)
|
||||
show_dhudmessage(killer, TXT_MULTI[m])
|
||||
PlaySound(killer, SND_MULTI[m])
|
||||
}
|
||||
else if (knife)
|
||||
{
|
||||
new vname[32]
|
||||
get_user_name(victim, vname, charsmax(vname))
|
||||
set_dhudmessage(255, 255, 255, -1.0, 0.25, 0, 0.0, 2.0, 0.1, 0.3)
|
||||
show_dhudmessage(0, "%s knifed %s!", name, vname)
|
||||
PlaySound(0, SND_KNIFE)
|
||||
}
|
||||
else if (headshot)
|
||||
{
|
||||
PlaySound(killer, SND_HEADSHOT)
|
||||
}
|
||||
else if (!g_firstBlood)
|
||||
{
|
||||
set_dhudmessage(255, 0, 0, -1.0, 0.25, 0, 0.0, 2.0, 0.1, 0.3)
|
||||
show_dhudmessage(0, "%s drew FIRST BLOOD", name)
|
||||
PlaySound(0, SND_FIRSTBLOOD)
|
||||
}
|
||||
g_firstBlood = true
|
||||
|
||||
ShowInfo(killer)
|
||||
return HC_CONTINUE
|
||||
}
|
||||
|
||||
PlaySound(id, const snd[])
|
||||
{
|
||||
if (!get_pcvar_num(g_pSounds))
|
||||
return
|
||||
// spk wants the path without "sound/" and without extension
|
||||
new path[64]
|
||||
copy(path, charsmax(path), snd)
|
||||
replace(path, charsmax(path), ".wav", "")
|
||||
client_cmd(id, "spk ^"%s^"", path)
|
||||
}
|
||||
|
||||
// --- HUD info line ----------------------------------------------------------
|
||||
|
||||
public ShowInfoAll()
|
||||
{
|
||||
if (g_gunGame || !get_pcvar_num(g_pHudInfo))
|
||||
return
|
||||
new players[MAX_PLAYERS], num
|
||||
get_players(players, num, "ach") // alive, no bots, no HLTV
|
||||
for (new i = 0; i < num; i++)
|
||||
ShowInfo(players[i])
|
||||
}
|
||||
|
||||
ShowInfo(id)
|
||||
{
|
||||
if (g_gunGame || !get_pcvar_num(g_pHudInfo) || !is_user_connected(id) || is_user_bot(id))
|
||||
return
|
||||
set_hudmessage(200, 200, 200, 0.01, 0.88, 0, 0.0, 1.2, 0.0, 0.0, -1)
|
||||
ShowSyncHudMsg(id, g_syncInfo, "K %d D %d | streak %d (best %d)", g_kills[id], g_deaths[id], g_streak[id], g_best[id])
|
||||
}
|
||||
Reference in new issue
Block a user