/* * CSB Random Spawns * Copyright (C) 2026 counter-strike-boost.com * * Overrides the map's normal spawn assignment with a randomized pick from all * info_player_deathmatch / info_player_start / info_vip_start entities. Before * a point is used it is validated with a hull trace (must not start in solid) * and a distance check against living players, so nobody spawns inside a * teammate or in front of an enemy's crosshair. Designed for deathmatch and * gungame servers to reduce spawn camping. * * 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 Random Spawns" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" #define MAX_SPAWNS 128 new g_iSpawnCount new Float:g_fSpawnOrigin[MAX_SPAWNS][3] new Float:g_fSpawnAngles[MAX_SPAWNS][3] new g_pEnabled, g_pMinDist public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pEnabled = register_cvar("csb_rspawns_enabled", "1") g_pMinDist = register_cvar("csb_rspawns_mindist", "70.0") RegisterHam(Ham_Spawn, "player", "OnPlayerSpawn", 1) collectSpawns("info_player_deathmatch") collectSpawns("info_player_start") collectSpawns("info_vip_start") log_amx("[CSB] Random Spawns: collected %d spawn point(s).", g_iSpawnCount) } collectSpawns(const className[]) { new ent = -1 while ((ent = find_ent_by_class(ent, className)) > 0) { if (g_iSpawnCount >= MAX_SPAWNS) break new Float:orig[3], Float:ang[3] pev(ent, pev_origin, orig) pev(ent, pev_angles, ang) /* lift slightly so the hull never clips the floor */ orig[2] += 2.0 g_fSpawnOrigin[g_iSpawnCount][0] = orig[0] g_fSpawnOrigin[g_iSpawnCount][1] = orig[1] g_fSpawnOrigin[g_iSpawnCount][2] = orig[2] g_fSpawnAngles[g_iSpawnCount][0] = ang[0] g_fSpawnAngles[g_iSpawnCount][1] = ang[1] g_fSpawnAngles[g_iSpawnCount][2] = ang[2] g_iSpawnCount++ } } public OnPlayerSpawn(id) { if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id) || g_iSpawnCount < 1) return new team = get_user_team(id) if (team != 1 && team != 2) return new start = random_num(0, g_iSpawnCount - 1) new Float:minDist = get_pcvar_float(g_pMinDist) for (new i = 0; i < g_iSpawnCount; i++) { new idx = (start + i) % g_iSpawnCount if (!spawnFree(idx, id, minDist)) continue new Float:orig[3], Float:ang[3] orig[0] = g_fSpawnOrigin[idx][0] orig[1] = g_fSpawnOrigin[idx][1] orig[2] = g_fSpawnOrigin[idx][2] ang[0] = g_fSpawnAngles[idx][0] ang[1] = g_fSpawnAngles[idx][1] ang[2] = g_fSpawnAngles[idx][2] set_pev(id, pev_origin, orig) set_pev(id, pev_angles, ang) set_pev(id, pev_v_angle, ang) set_pev(id, pev_fixangle, 1) return } } bool:spawnFree(idx, id, Float:minDist) { static Float:orig[3] orig[0] = g_fSpawnOrigin[idx][0] orig[1] = g_fSpawnOrigin[idx][1] orig[2] = g_fSpawnOrigin[idx][2] /* bit 1 (StartSolid) means a player hull does not fit here */ if (trace_hull(orig, HULL_HUMAN, id) & 1) return false static players[32], num get_players(players, num, "a") for (new i = 0; i < num; i++) { if (players[i] == id) continue static Float:o2[3] pev(players[i], pev_origin, o2) if (get_distance_f(orig, o2) < minDist) return false } return true }