Daemon666 / CSB Map Spawn Editor Публичен

In-game spawn editor: /addspawn, /delspawn, /savespawns store points per map and override player spawns via SetOrigin.

v1.0.0 180 изтегляния GPL-3.0 Последна актуализация Aug 22, 2026
csb_map_spawn_editor.sma v1.0.0 263 реда · 6.4 KB Суров
1 /*
2 * CSB Map Spawn Editor
3 * Copyright (C) 2026 counter-strike-boost.com
4 *
5 * In-game spawn editor for admins. /addspawn stores your current origin and
6 * angles, /delspawn removes the closest saved point, /savespawns writes them,
7 * and /listspawns reports the count. Spawns are kept per map in the AMXX data
8 * directory. On respawn the plugin teleports the player to a saved point with
9 * engfunc(EngFunc_SetOrigin) - useful for DM and CSDM style maps.
10 *
11 * Inspired by the CSDM spawn editor by BAILOPAN. Independent GPL
12 * re-implementation.
13 *
14 * This program is free software: you can redistribute it and/or modify it under
15 * the terms of the GNU General Public License as published by the Free Software
16 * Foundation, either version 3 of the License, or (at your option) any later
17 * version. It is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See <https://www.gnu.org/licenses/> for details.
20 */
21
22 #include <amxmodx>
23 #include <amxmisc>
24 #include <fakemeta>
25 #include <hamsandwich>
26
27 #define MAX_SPAWNS 64
28
29 new const PLUGIN[] = "CSB Map Spawn Editor"
30 new const VERSION[] = "1.0.0"
31 new const AUTHOR[] = "counter-strike-boost.com"
32
33 new Float:g_fSpawn[MAX_SPAWNS][6]
34 new g_iSpawnCount = 0
35
36 new g_pEnabled, g_pOverride
37 new g_szFile[192]
38
39 public plugin_init()
40 {
41 register_plugin(PLUGIN, VERSION, AUTHOR)
42
43 g_pEnabled = register_cvar("csb_spawn_enabled", "1")
44 g_pOverride = register_cvar("csb_spawn_override", "1")
45
46 register_clcmd("say /addspawn", "cmdAdd")
47 register_clcmd("say /delspawn", "cmdDel")
48 register_clcmd("say /savespawns", "cmdSave")
49 register_clcmd("say /listspawns", "cmdList")
50
51 RegisterHam(Ham_Spawn, "player", "fwdSpawnPost", 1)
52 }
53
54 public plugin_cfg()
55 {
56 new dir[128]
57 get_localinfo("amxx_datadir", dir, charsmax(dir))
58
59 new map[40]
60 get_mapname(map, charsmax(map))
61
62 formatex(g_szFile, charsmax(g_szFile), "%s/csb_spawns_%s.txt", dir, map)
63
64 loadSpawns()
65 }
66
67 loadSpawns()
68 {
69 g_iSpawnCount = 0
70
71 if (!file_exists(g_szFile))
72 return
73
74 new f = fopen(g_szFile, "rt")
75
76 if (!f)
77 return
78
79 new line[128], p[6][16]
80
81 while (!feof(f) && g_iSpawnCount < MAX_SPAWNS)
82 {
83 fgets(f, line, charsmax(line))
84 trim(line)
85
86 if (!line[0] || line[0] == ';')
87 continue
88
89 if (parse(line, p[0], 15, p[1], 15, p[2], 15, p[3], 15, p[4], 15, p[5], 15) < 6)
90 continue
91
92 for (new j = 0; j < 6; j++)
93 g_fSpawn[g_iSpawnCount][j] = str_to_float(p[j])
94
95 g_iSpawnCount++
96 }
97
98 fclose(f)
99
100 log_amx("[CSB Spawn] loaded %d spawn(s) for this map.", g_iSpawnCount)
101 }
102
103 bool:hasAccess(id)
104 {
105 if (get_user_flags(id) & ADMIN_MAP)
106 return true
107
108 client_print(id, print_chat, "[CSB] You need the map admin flag (e) to edit spawns.")
109 return false
110 }
111
112 public cmdAdd(id)
113 {
114 if (!hasAccess(id))
115 return PLUGIN_HANDLED
116
117 if (g_iSpawnCount >= MAX_SPAWNS)
118 {
119 client_print(id, print_chat, "[CSB] Spawn list is full (%d).", MAX_SPAWNS)
120 return PLUGIN_HANDLED
121 }
122
123 new Float:origin[3], Float:angles[3]
124 pev(id, pev_origin, origin)
125 pev(id, pev_v_angle, angles)
126
127 g_fSpawn[g_iSpawnCount][0] = origin[0]
128 g_fSpawn[g_iSpawnCount][1] = origin[1]
129 g_fSpawn[g_iSpawnCount][2] = origin[2]
130 g_fSpawn[g_iSpawnCount][3] = angles[0]
131 g_fSpawn[g_iSpawnCount][4] = angles[1]
132 g_fSpawn[g_iSpawnCount][5] = angles[2]
133
134 g_iSpawnCount++
135
136 client_print(id, print_chat, "[CSB] Spawn #%d saved at your position. Use /savespawns to write to disk.", g_iSpawnCount)
137 return PLUGIN_HANDLED
138 }
139
140 public cmdDel(id)
141 {
142 if (!hasAccess(id))
143 return PLUGIN_HANDLED
144
145 if (!g_iSpawnCount)
146 {
147 client_print(id, print_chat, "[CSB] No spawns to delete.")
148 return PLUGIN_HANDLED
149 }
150
151 new Float:origin[3]
152 pev(id, pev_origin, origin)
153
154 new closest = -1
155 new Float:best = 999999.0
156
157 for (new i = 0; i < g_iSpawnCount; i++)
158 {
159 new Float:pt[3]
160 pt[0] = g_fSpawn[i][0]
161 pt[1] = g_fSpawn[i][1]
162 pt[2] = g_fSpawn[i][2]
163
164 new Float:d = get_distance_f(origin, pt)
165
166 if (d < best)
167 {
168 best = d
169 closest = i
170 }
171 }
172
173 if (closest < 0)
174 return PLUGIN_HANDLED
175
176 /* shift the rest down */
177 for (new i = closest; i < g_iSpawnCount - 1; i++)
178 {
179 for (new j = 0; j < 6; j++)
180 g_fSpawn[i][j] = g_fSpawn[i + 1][j]
181 }
182
183 g_iSpawnCount--
184
185 client_print(id, print_chat, "[CSB] Removed the closest spawn (%.0f units away). %d left.", best, g_iSpawnCount)
186 return PLUGIN_HANDLED
187 }
188
189 public cmdSave(id)
190 {
191 if (!hasAccess(id))
192 return PLUGIN_HANDLED
193
194 new f = fopen(g_szFile, "wt")
195
196 if (!f)
197 {
198 client_print(id, print_chat, "[CSB] Could not open the spawn file for writing.")
199 return PLUGIN_HANDLED
200 }
201
202 fprintf(f, "; CSB Map Spawn Editor - x y z pitch yaw roll^n")
203
204 for (new i = 0; i < g_iSpawnCount; i++)
205 {
206 fprintf(f, "%.2f %.2f %.2f %.2f %.2f %.2f^n",
207 g_fSpawn[i][0], g_fSpawn[i][1], g_fSpawn[i][2],
208 g_fSpawn[i][3], g_fSpawn[i][4], g_fSpawn[i][5])
209 }
210
211 fclose(f)
212
213 client_print(id, print_chat, "[CSB] Wrote %d spawn(s) to disk.", g_iSpawnCount)
214 log_amx("[CSB Spawn] admin saved %d spawn(s).", g_iSpawnCount)
215 return PLUGIN_HANDLED
216 }
217
218 public cmdList(id)
219 {
220 if (!hasAccess(id))
221 return PLUGIN_HANDLED
222
223 client_print(id, print_chat, "[CSB] %d custom spawn(s) loaded for this map.", g_iSpawnCount)
224 return PLUGIN_HANDLED
225 }
226
227 public fwdSpawnPost(id)
228 {
229 if (!get_pcvar_num(g_pEnabled) || !get_pcvar_num(g_pOverride))
230 return
231
232 if (!g_iSpawnCount || !is_user_alive(id))
233 return
234
235 set_task(0.1, "taskTeleport", id)
236 }
237
238 public taskTeleport(id)
239 {
240 if (!is_user_alive(id) || !g_iSpawnCount)
241 return
242
243 new pick = random_num(0, g_iSpawnCount - 1)
244
245 new Float:origin[3], Float:angles[3]
246 origin[0] = g_fSpawn[pick][0]
247 origin[1] = g_fSpawn[pick][1]
248 origin[2] = g_fSpawn[pick][2]
249 angles[0] = g_fSpawn[pick][3]
250 angles[1] = g_fSpawn[pick][4]
251 angles[2] = g_fSpawn[pick][5]
252
253 engfunc(EngFunc_SetOrigin, id, origin)
254
255 set_pev(id, pev_angles, angles)
256 set_pev(id, pev_v_angle, angles)
257 set_pev(id, pev_fixangle, 1)
258
259 /* stop any residual momentum from the map's default spawn */
260 new Float:zero[3]
261 set_pev(id, pev_velocity, zero)
262 }
263