KZ-style checkpoints: /cp saves origin, angles and velocity, /tp restores, /gocheck cycles saved points.
csb_checkpoints.sma v1.0.0
1
/*
2
* CSB Checkpoints
3
* Copyright (C) 2026 counter-strike-boost.com
4
*
5
* A lightweight KZ-style checkpoint system. /cp saves the player's origin,
6
* angles and velocity, /tp teleports back to the last saved point with
7
* set_pev + EngFunc_SetOrigin, and /gocheck cycles through every point saved on
8
* the current map. A cvar can force saving only while on the ground, and a HUD
9
* counter shows how many points are stored.
10
*
11
* Inspired by KZ / climb checkpoint plugins. Independent GPL re-implementation.
12
*
13
* This program is free software: you can redistribute it and/or modify it under
14
* the terms of the GNU General Public License as published by the Free Software
15
* Foundation, either version 3 of the License, or (at your option) any later
16
* version. It is distributed in the hope that it will be useful, but WITHOUT
17
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
* FOR A PARTICULAR PURPOSE. See <https://www.gnu.org/licenses/> for details.
19
*/
20
21
#include <amxmodx>
22
#include <fakemeta>
23
24
new const PLUGIN[] = "CSB Checkpoints"
25
new const VERSION[] = "1.0.0"
26
new const AUTHOR[] = "counter-strike-boost.com"
27
28
#define MAX_CP 8
29
#define FL_ONGROUND (1<<9)
30
31
new g_pEnabled, g_pGroundOnly
32
33
new Float:g_cpOrigin[33][MAX_CP][3]
34
new Float:g_cpAngles[33][MAX_CP][3]
35
new Float:g_cpVel[33][MAX_CP][3]
36
new g_cpCount[33]
37
new g_cpCycle[33]
38
39
public plugin_init()
40
{
41
register_plugin(PLUGIN, VERSION, AUTHOR)
42
43
g_pEnabled = register_cvar("csb_cp_enabled", "1")
44
g_pGroundOnly = register_cvar("csb_cp_groundonly", "1")
45
46
register_clcmd("say /cp", "cmdSave")
47
register_clcmd("say_team /cp", "cmdSave")
48
register_clcmd("say /tp", "cmdRestore")
49
register_clcmd("say_team /tp", "cmdRestore")
50
register_clcmd("say /gocheck", "cmdCycle")
51
register_clcmd("say_team /gocheck", "cmdCycle")
52
register_clcmd("say /clearcp", "cmdClear")
53
}
54
55
public client_putinserver(id)
56
{
57
g_cpCount[id] = 0
58
g_cpCycle[id] = 0
59
}
60
61
public cmdSave(id)
62
{
63
if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id))
64
return PLUGIN_HANDLED
65
66
if (get_pcvar_num(g_pGroundOnly) && !(pev(id, pev_flags) & FL_ONGROUND))
67
{
68
client_print(id, print_chat, "[CSB] You must be on the ground to save a checkpoint.")
69
return PLUGIN_HANDLED
70
}
71
72
if (g_cpCount[id] >= MAX_CP)
73
{
74
/* drop the oldest by shifting down */
75
for (new i = 1; i < MAX_CP; i++)
76
{
77
copyVec(g_cpOrigin[id][i - 1], g_cpOrigin[id][i])
78
copyVec(g_cpAngles[id][i - 1], g_cpAngles[id][i])
79
copyVec(g_cpVel[id][i - 1], g_cpVel[id][i])
80
}
81
82
g_cpCount[id] = MAX_CP - 1
83
}
84
85
new slot = g_cpCount[id]
86
87
pev(id, pev_origin, g_cpOrigin[id][slot])
88
pev(id, pev_v_angle, g_cpAngles[id][slot])
89
pev(id, pev_velocity, g_cpVel[id][slot])
90
91
g_cpCount[id]++
92
g_cpCycle[id] = g_cpCount[id] - 1
93
94
showCount(id, "Checkpoint saved")
95
return PLUGIN_HANDLED
96
}
97
98
public cmdRestore(id)
99
{
100
if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id))
101
return PLUGIN_HANDLED
102
103
if (g_cpCount[id] <= 0)
104
{
105
client_print(id, print_chat, "[CSB] No checkpoint saved yet. Use /cp first.")
106
return PLUGIN_HANDLED
107
}
108
109
teleport(id, g_cpCount[id] - 1)
110
showCount(id, "Teleported to last checkpoint")
111
return PLUGIN_HANDLED
112
}
113
114
public cmdCycle(id)
115
{
116
if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id))
117
return PLUGIN_HANDLED
118
119
if (g_cpCount[id] <= 0)
120
{
121
client_print(id, print_chat, "[CSB] No checkpoints saved.")
122
return PLUGIN_HANDLED
123
}
124
125
g_cpCycle[id] = (g_cpCycle[id] + 1) % g_cpCount[id]
126
teleport(id, g_cpCycle[id])
127
128
client_print(id, print_chat, "[CSB] Checkpoint %d / %d.", g_cpCycle[id] + 1, g_cpCount[id])
129
return PLUGIN_HANDLED
130
}
131
132
public cmdClear(id)
133
{
134
g_cpCount[id] = 0
135
g_cpCycle[id] = 0
136
client_print(id, print_chat, "[CSB] Checkpoints cleared.")
137
return PLUGIN_HANDLED
138
}
139
140
teleport(id, slot)
141
{
142
if (slot < 0 || slot >= g_cpCount[id])
143
return
144
145
engfunc(EngFunc_SetOrigin, id, g_cpOrigin[id][slot])
146
set_pev(id, pev_angles, g_cpAngles[id][slot])
147
set_pev(id, pev_v_angle, g_cpAngles[id][slot])
148
set_pev(id, pev_fixangle, 1)
149
set_pev(id, pev_velocity, g_cpVel[id][slot])
150
}
151
152
copyVec(Float:dest[3], const Float:src[3])
153
{
154
dest[0] = src[0]
155
dest[1] = src[1]
156
dest[2] = src[2]
157
}
158
159
showCount(id, const what[])
160
{
161
client_print(id, print_chat, "[CSB] %s (%d stored).", what, g_cpCount[id])
162
}
163









