VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
Pawn.cpp
Go to the documentation of this file.
5#include <algorithm>
6#include <entityhandle.h>
7#include <shareddefs.h>
8#include <string>
9#include <tier1/utlvector.h>
10#include <utility>
11
12namespace VoltMod
13{
14
15static_assert(std::to_underlying(ObserverMode::None) == OBS_MODE_NONE);
16static_assert(std::to_underlying(ObserverMode::Fixed) == OBS_MODE_FIXED);
17static_assert(std::to_underlying(ObserverMode::InEye) == OBS_MODE_IN_EYE);
18static_assert(std::to_underlying(ObserverMode::Chase) == OBS_MODE_CHASE);
19static_assert(std::to_underlying(ObserverMode::Roaming) == OBS_MODE_ROAMING);
20
21// Passed by value across the plugin boundary, so the layout is ABI.
22static_assert(sizeof(Pawn) <= 160, "Pawn is a frame-local value; keep the field list tight.");
23
25{
26 return Origin() + ViewOffset();
27}
28
29void Pawn::SetGodmode(bool on) const
30{
31 SetFlags(on ? (Flags() | FL_GODMODE) : (Flags() & ~FL_GODMODE));
32}
33
35{
36 // Written directly: a Teleport carrying only a velocity has crashed CS2 builds.
37 SetAbsVelocity(velocity);
38 // Otherwise the engine grounds the pawn again on the next tick.
39 SetFlags(Flags() & ~FL_ONGROUND);
40}
41
42bool Pawn::Heal(int amount) const
43{
44 const int health = Health();
45 const int healed = std::min(health + amount, MaxHealth());
46 if (!IsAlive() || healed <= health)
47 {
48 return false;
49 }
50 SetHealth(healed);
51 return true;
52}
53
54Entity Pawn::GiveItem(std::string_view item) const
55{
56 const Schema::CPlayer_ItemServices services = ItemServices();
57 if (!_sys || !services || item.empty())
58 {
59 return {};
60 }
61 const auto& give = _sys->Bindings().GiveNamedItem;
62 if (!give)
63 {
64 return {};
65 }
66
67 const std::string className(item);
68 if (void* given = give(services.Base(), className.c_str()))
69 {
70 return {*_sys, static_cast<CEntityInstance*>(given)};
71 }
72
73 // The engine refuses a weapon only the other team can buy; the swap is undone before anyone sees it.
74 const VoltMod::Team team = Team();
77 {
78 return {};
79 }
80 SetTeamNum(other);
81 void* given = give(services.Base(), className.c_str());
82 SetTeamNum(team);
83
84 if (!given)
85 {
86 Log::Warn("The engine refused '{}' for both teams.", item);
87 }
88 return {*_sys, static_cast<CEntityInstance*>(given)};
89}
90
92{
93 const Schema::CPlayer_ItemServices services = ItemServices();
95 {
96 return false;
97 }
99 return true;
100}
101
102std::vector<Entity> Pawn::Weapons() const
103{
104 std::vector<Entity> weapons;
105 const Schema::CPlayer_WeaponServices services = WeaponServices();
106 // The schema's CNetworkUtlVectorBase<CHandle<T>> is laid out as a CUtlVector.
107 const auto* handles = services ? static_cast<const CUtlVector<CEntityHandle>*>(services.MyWeapons()) : nullptr;
108 if (!_sys || !handles)
109 {
110 return weapons;
111 }
112 for (int i = 0; i < handles->Count(); ++i)
113 {
114 if (const Entity weapon = _sys->Get(EntityRef{static_cast<uint32_t>(handles->Element(i).ToInt())}))
115 {
116 weapons.push_back(weapon);
117 }
118 }
119 return weapons;
120}
121
123{
124 const Schema::CPlayer_WeaponServices services = WeaponServices();
125 const bool bound = _sys && services && _sys->Bindings().SelectItem;
126 if (!weapon || !bound)
127 {
128 return;
129 }
130 const bool carried =
131 std::ranges::any_of(Weapons(), [&](const Entity& held) { return held.Ref() == weapon.Ref(); });
132 if (!carried)
133 {
134 return;
135 }
136 _sys->Bindings().SelectItem(static_cast<EngineWeaponServices*>(services.Base()), weapon.Raw(), 0);
137}
138
140{
141 const Schema::CPlayer_WeaponServices services = WeaponServices();
142 const bool inHand = weapon && _sys && services && services.ActiveWeapon() == weapon.Ref();
143 // Removing the weapon in hand otherwise leaves the pawn with empty hands.
144 if (inHand && IsAlive())
145 {
146 SelectWeapon(_sys->Get(services.LastWeapon()));
147 }
148 weapon.RemoveAfter(0.0f);
149}
150
151void Pawn::HoldFire(int tick) const
152{
153 const Schema::CPlayer_WeaponServices services = WeaponServices();
154 const Entity active = services && _sys ? _sys->Get(services.ActiveWeapon()) : Entity{};
156 if (!weapon)
157 {
158 return;
159 }
160 if (weapon.NextPrimaryAttackTick() < tick)
161 {
163 }
164 if (weapon.NextSecondaryAttackTick() < tick)
165 {
166 weapon.SetNextSecondaryAttackTick(tick);
167 }
168}
169
170void Pawn::Slay() const
171{
172 if (_e && _sys && _sys->Bindings().CommitSuicide)
173 {
174 _sys->Bindings().CommitSuicide(_e, false, true);
175 }
176}
177
179{
180 return (_sys && ClassName() == "player") ? Pawn{*_sys, _e} : Pawn{};
181}
182
184{
185 const Schema::CPlayer_ObserverServices services = ObserverServices();
186 return services ? static_cast<VoltMod::ObserverMode>(services.ObserverMode()) : VoltMod::ObserverMode::None;
187}
188
190{
191 const Schema::CPlayer_ObserverServices services = ObserverServices();
192 if (!services)
193 {
194 return std::unexpected(Error::NotReady("observer services unavailable"));
195 }
196
197 services.SetObserverMode(std::to_underlying(value));
198 return {};
199}
200
201std::string Pawn::ModelName() const
202{
203 const Schema::CSkeletonInstance skeleton{BodyComponent().SceneNode().Base()};
204 if (!skeleton)
205 {
206 return {};
207 }
208
209 const char* path = skeleton.ModelState().ModelName();
210 return path ? std::string(path) : std::string{};
211}
212
217
218int Pawn::Slot() const
219{
220 // Controllers sit at entity index slot + 1.
221 const Entity controller = _sys ? _sys->Get(ControllerRef()) : Entity{};
222 const int slot = controller.Index() - 1;
223 return controller && IsValidSlot(slot) ? slot : -1;
224}
225
226} // namespace VoltMod
A player's identity behind a slot: name, money, team, scoreboard row. It survives death and respawn; ...
const VoltMod::Bindings & Bindings() const noexcept
VoltMod::Controller Controller(int slot)
Entity Get(EntityRef ref)
A live entity, valid for this frame only.
Definition Entity.hpp:29
int Index() const
Definition Entity.cpp:77
Pawn AsPawn() const
Definition Pawn.cpp:178
std::string_view ClassName() const
Definition Entity.cpp:87
EntitySystem * _sys
Definition Entity.hpp:31
Vector Origin() const
Definition Entity.cpp:97
CEntityInstance * _e
Definition Entity.hpp:32
A player's body: health, armor, movement, aim, weapons. The engine replaces it on every spawn; the Co...
Definition Pawn.hpp:19
std::string ModelName() const
Definition Pawn.cpp:201
bool IsAlive() const
Definition Pawn.hpp:29
void SetGodmode(bool on) const
Definition Pawn.cpp:29
Vector EyePosition() const
Definition Pawn.cpp:24
void Slay() const
Definition Pawn.cpp:170
bool StripWeapons(bool removeSuit=true) const
Definition Pawn.cpp:91
void SelectWeapon(const Entity &weapon) const
Definition Pawn.cpp:122
std::vector< Entity > Weapons() const
Definition Pawn.cpp:102
Entity GiveItem(std::string_view item) const
Definition Pawn.cpp:54
VoltMod::ObserverMode ObserverMode() const
Definition Pawn.cpp:183
void RemoveWeapon(const Entity &weapon) const
Definition Pawn.cpp:139
Status SetObserverMode(VoltMod::ObserverMode mode) const
Definition Pawn.cpp:189
bool Heal(int amount) const
Definition Pawn.cpp:42
void HoldFire(int tick) const
Definition Pawn.cpp:151
VoltMod::Controller Controller() const
Definition Pawn.cpp:213
void SetVelocity(Vector velocity) const
Definition Pawn.cpp:34
int Slot() const
Definition Pawn.cpp:218
void SetNextPrimaryAttackTick(int32_t value) const
void * Base() const noexcept
Definition View.hpp:33
void Warn(std::format_string< Args... > fmt, Args &&... args)
Definition Log.hpp:88
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
constexpr bool IsValidSlot(int slot)
Definition Slot.hpp:13
constexpr Team Opposite(Team team)
Definition Team.hpp:24
std::expected< void, Error > Status
Definition Result.hpp:67
::Vector Vector
Definition Math.hpp:11
VirtualFn< void(void *, bool)> RemoveAllItems
Definition Bindings.hpp:87
VirtualFn< bool(EngineWeaponServices *, CEntityInstance *, int)> SelectItem
Definition Bindings.hpp:89
VirtualFn< void(CEntityInstance *, bool, bool)> CommitSuicide
Definition Bindings.hpp:73
VirtualFn< void *(void *, const char *)> GiveNamedItem
Definition Bindings.hpp:85
static Error NotReady(std::string detail)
Definition Result.hpp:50