Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
selection.qc
Go to the documentation of this file.
1#include "selection.qh"
2
3#include <common/constants.qh>
8#include <common/replicate.qh>
9#include <common/state.qh>
10#include <common/util.qh>
12#include <common/wepent.qh>
13#include <server/items/items.qh>
16
18void Send_WeaponComplain(entity e, float wpn, float type)
19{
20 msg_entity = e;
21 WriteHeader(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
22 WriteByte(MSG_ONE, wpn);
23 WriteByte(MSG_ONE, type);
24}
25
27{
29 return;
30 IL_EACH(g_items, it.weapon == this.m_id && it.model,
31 {
32 if (ITEM_IS_LOOT(it) && autocvar_g_showweaponspawns < 2)
33 continue;
34 entity wp = WaypointSprite_Spawn(
35 WP_Weapon,
36 -2, 0,
37 NULL, it.origin + ('0 0 1' * it.maxs.z) * 1.2,
38 cl, 0,
39 NULL, enemy,
40 0,
41 RADARICON_NONE
42 );
43 wp.wp_extra = this.m_id;
44 });
45}
46
47bool client_hasweapon(entity this, Weapon wpn, .entity weaponentity, float andammo, bool complain)
48{
50 complain = 0;
51
52 // ignore hook button when using other offhand equipment
53 if (this.offhand != OFFHAND_HOOK
54 && wpn == WEP_HOOK && !((STAT(WEAPONS, this) | weaponsInMap) & WepSet_FromWeapon(wpn)))
55 complain = 0;
56
57 if (complain)
58 CS(this).hasweapon_complain_spam = time + 0.2;
59
60 if (wpn == WEP_Null)
61 {
62 if (complain)
63 sprint(this, "Invalid weapon\n");
64 return false;
65 }
66 if (autocvar_g_weaponswitch_debug == 2 && weaponslot(weaponentity) > 0
67 && !(wpn.spawnflags & WEP_FLAG_DUALWIELD) && !(PS(this).dual_weapons & wpn.m_wepset))
68 return false; // no complaints needed
69
70 float f = 0;
71 if (STAT(WEAPONS, this) & WepSet_FromWeapon(wpn))
72 {
73 if (andammo)
74 {
75 if (this.items & IT_UNLIMITED_AMMO)
76 f = 1;
77 else
78 {
79 f = wpn.wr_checkammo1(wpn, this, weaponentity) + wpn.wr_checkammo2(wpn, this, weaponentity);
80
81 // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
82 if (wpn == WEP_MINE_LAYER)
83 IL_EACH(g_mines, it.owner == this && it.weaponentity_fld == weaponentity,
84 {
85 f = 1;
86 break; // no need to continue
87 });
88 }
89 if (!f)
90 {
91 if (complain && IS_REAL_CLIENT(this))
92 {
93 play2(this, SND(UNAVAILABLE));
94 Send_WeaponComplain (this, wpn.m_id, 0);
95 }
96 return false;
97 }
98 }
99 return true;
100 }
101 if (complain
102 && IS_REAL_CLIENT(this))
103 {
104 // DRESK - 3/16/07
105 // Report Proper Weapon Status / Modified Weapon Ownership Message
107 {
108 Send_WeaponComplain(this, wpn.m_id, 1);
110 Weapon_whereis(wpn, this);
111 else
112 FOREACH(Weapons, it.impulse == wpn.impulse, Weapon_whereis(it, this));
113 }
114 else
115 Send_WeaponComplain(this, wpn.m_id, 2);
116
117 play2(this, SND(UNAVAILABLE));
118 }
119 return false;
120}
121
122float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, bool complain, bool skipmissing, .entity weaponentity)
123{
124 // We cannot tokenize in this function, as GiveItems calls this
125 // function. Thus we must use car/cdr.
126 entity wep;
127 string rest;
128 float weaponwant;
129 float first_valid = 0, prev_valid = 0;
130 float weaponcur;
131 bool switchtonext = false, switchtolast = false;
132
133 if (skipmissing || this.(weaponentity).selectweapon == 0)
134 weaponcur = this.(weaponentity).m_switchweapon.m_id;
135 else
136 weaponcur = this.(weaponentity).selectweapon;
137
138 if (dir == 0)
139 switchtonext = true;
140
141 // complain-specific vars
142 int c = 0;
143 entity wepcomplain = NULL;
144 int wepcomplainindex = 0;
145 bool have_other = false;
146
147 // see if we have or can locate (not hidden), some other weapon in the group
148 WepSet customgroup = '0 0 0';
149 if (imp < 0) // custom cl_weaponpriorityN group
150 {
151 // These groups are client-specific, and (imp == -1) here,
152 // so generate a weapon set bitmask to represent the group.
153 rest = weaponorder;
154 while (rest != "")
155 {
156 weaponwant = stof(car(rest));
157 rest = cdr(rest);
158 customgroup |= REGISTRY_GET(Weapons, weaponwant).m_wepset;
159 }
160 }
161 else
162 {} // standard weapon_group, defined by impulse sharing
163 FOREACH(Weapons, (imp >= 0 && it.impulse == imp) || (imp < 0 && (it.m_wepset & customgroup)),
164 {
165 if ((it.m_wepset & STAT(WEAPONS, this))
166 || ((it.m_wepset & weaponsInMap) && !(it.spawnflags & WEP_FLAG_HIDDEN)))
167 {
168 have_other = true;
169 break;
170 }
171 });
172
173 rest = weaponorder;
174 while (rest != "")
175 {
176 weaponwant = stof(car(rest)); rest = cdr(rest);
177 wep = REGISTRY_GET(Weapons, weaponwant);
178 if (imp >= 0 && wep.impulse != imp)
179 continue;
180
181 // skip weapons we don't own that aren't normal and aren't in the map
182 if (!(STAT(WEAPONS, this) & wep.m_wepset))
183 {
184 if (wep.spawnflags & WEP_FLAG_HIDDEN)
185 continue;
186
187 if (!(weaponsInMap & wep.m_wepset)
188 && ((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other))
189 continue;
190 }
191
192 if (complain)
193 {
194 if (!wepcomplain || this.weaponcomplainindex == c)
195 {
196 wepcomplain = wep;
197 wepcomplainindex = c;
198 }
199 ++c;
200 }
201
202 if (!skipmissing || client_hasweapon(this, wep, weaponentity, true, false))
203 {
204 if (switchtonext)
205 return weaponwant;
206 if (!first_valid)
207 first_valid = weaponwant;
208 if (weaponwant == weaponcur)
209 {
210 if (dir >= 0)
211 switchtonext = true;
212 else if (prev_valid)
213 return prev_valid;
214 else
215 switchtolast = true;
216 }
217 prev_valid = weaponwant;
218 }
219 }
220 if (first_valid)
221 {
222 if (switchtolast)
223 return prev_valid;
224 else
225 return first_valid;
226 }
227
228 // complain (but only for one weapon on the button that has been pressed)
229 if (wepcomplain)
230 {
231 this.weaponcomplainindex = wepcomplainindex + 1;
232 client_hasweapon(this, wepcomplain, weaponentity, true, true);
233 }
234 return 0;
235}
236
237void W_SwitchWeapon_Force(Player this, Weapon wep, .entity weaponentity)
238{
239 TC(Weapon, wep);
240 entity w_ent = this.(weaponentity);
241 w_ent.cnt = w_ent.m_switchweapon.m_id;
242 w_ent.m_switchweapon = wep;
243 w_ent.selectweapon = wep.m_id;
244}
245
247void W_SwitchToOtherWeapon(entity this, .entity weaponentity)
248{
249 // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
250 Weapon ww;
251 WepSet set = WepSet_FromWeapon(this.(weaponentity).m_weapon);
252 if (STAT(WEAPONS, this) & set)
253 {
254 STAT(WEAPONS, this) &= ~set;
255 ww = w_getbestweapon(this, weaponentity);
256 STAT(WEAPONS, this) |= set;
257 }
258 else
259 ww = w_getbestweapon(this, weaponentity);
260 if (ww == WEP_Null)
261 return;
262 W_SwitchWeapon_Force(this, ww, weaponentity);
263}
264
265bool W_SwitchWeapon(entity this, Weapon w, .entity weaponentity)
266{
267 if (this.(weaponentity).m_switchweapon != w)
268 {
269 if (client_hasweapon(this, w, weaponentity, true, true))
270 {
271 W_SwitchWeapon_Force(this, w, weaponentity);
272 return true;
273 }
274 else
275 {
276 this.(weaponentity).selectweapon = w.m_id; // update selectweapon anyway
277 return false;
278 }
279 }
280 else if (!weaponLocked(this) && CS_CVAR(this).cvar_cl_weapon_switch_reload)
281 {
282 entity actor = this;
283 w.wr_reload(w, actor, weaponentity);
284 }
285
286 return true; // player already has the weapon out or needs to reload
287}
288
289void W_SwitchWeapon_TryOthers(entity this, Weapon w, .entity weaponentity)
290{
291 if (!W_SwitchWeapon(this, w, weaponentity) && CS_CVAR(this).cvar_cl_weapon_switch_fallback_to_impulse)
292 W_NextWeaponOnImpulse(this, w.impulse, weaponentity);
293}
294
295void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity)
296{
297 float w = W_GetCycleWeapon(this, weaponorder, dir, -1, true, true, weaponentity);
298 if (w > 0)
299 W_SwitchWeapon(this, REGISTRY_GET(Weapons, w), weaponentity);
300}
301
302void W_NextWeaponOnImpulse(entity this, float imp, .entity weaponentity)
303{
304 float w = W_GetCycleWeapon(this, CS_CVAR(this).cvar_cl_weaponpriority, +1, imp, true, (CS_CVAR(this).cvar_cl_weaponimpulsemode == 0), weaponentity);
305 if (w > 0)
306 W_SwitchWeapon(this, REGISTRY_GET(Weapons, w), weaponentity);
307}
308
310void W_NextWeapon(entity this, int list, .entity weaponentity)
311{
312 if (list == 0)
313 W_CycleWeapon(this, weaponorder_byid, -1, weaponentity);
314 else if (list == 1)
315 W_CycleWeapon(this, CS_CVAR(this).weaponorder_byimpulse, -1, weaponentity);
316 else if (list == 2)
317 W_CycleWeapon(this, CS_CVAR(this).cvar_cl_weaponpriority, -1, weaponentity);
318}
319
321void W_PreviousWeapon(entity this, float list, .entity weaponentity)
322{
323 if (list == 0)
324 W_CycleWeapon(this, weaponorder_byid, +1, weaponentity);
325 else if (list == 1)
326 W_CycleWeapon(this, CS_CVAR(this).weaponorder_byimpulse, +1, weaponentity);
327 else if (list == 2)
328 W_CycleWeapon(this, CS_CVAR(this).cvar_cl_weaponpriority, +1, weaponentity);
329}
330
332void W_LastWeapon(entity this, .entity weaponentity)
333{
334 Weapon wep = REGISTRY_GET(Weapons, this.(weaponentity).cnt);
335 if (client_hasweapon(this, wep, weaponentity, true, false))
336 W_SwitchWeapon(this, wep, weaponentity);
337 else
338 W_SwitchToOtherWeapon(this, weaponentity);
339}
340
341// fix switchweapon (needed when spectating is disabled, as PutClientInServer comes too early)
342REPLICATE_APPLYCHANGE("cl_weaponpriority",
343 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
344 {
345 .entity weaponentity = weaponentities[slot];
346 if (this.(weaponentity) && (this.(weaponentity).m_weapon != WEP_Null || slot == 0))
347 this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity);
348 }
349);
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
int impulse
M: impulse : weapon impulse.
Definition weapon.qh:58
virtual void wr_reload()
(SERVER) handles reloading for weapon
Definition weapon.qh:128
int m_id
Definition weapon.qh:43
virtual void wr_checkammo2()
(SERVER) checks ammo for weapon second
Definition weapon.qh:105
int spawnflags
M: flags : WEPSPAWNFLAG_... combined.
Definition weapon.qh:60
virtual void wr_checkammo1()
(SERVER) checks ammo for weapon primary
Definition weapon.qh:100
float cnt
Definition powerups.qc:24
int spawnflags
Definition ammo.qh:15
const int IT_UNLIMITED_AMMO
Definition item.qh:23
int items
Definition player.qh:226
vector weaponsInMap
all the weapons actually spawned in the map, does not include filtered items
Definition stats.qh:53
OffhandHook OFFHAND_HOOK
Definition hook.qh:83
float time
Weapons
Definition guide.qh:113
#define IL_EACH(this, cond, body)
Header file that describes the functions related to game items.
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define TC(T, sym)
Definition _all.inc:82
#define WriteHeader(to, id)
Definition net.qh:265
#define REPLICATE_APPLYCHANGE(var, ApplyChange_code)
Allows setting code that will be executed on cvar value changes.
Definition replicate.qh:36
#define STAT(...)
Definition stats.qh:82
float MSG_ONE
Definition menudefs.qc:56
float stof(string val,...)
void sprint(float clientnum, string text,...)
void WriteByte(float data, float dest, float desto)
IntrusiveList g_mines
Definition minelayer.qh:87
#define NULL
Definition post.qh:14
entity msg_entity
Definition progsdefs.qc:63
#define REGISTRY_GET(id, i)
Definition registry.qh:43
bool W_SwitchWeapon(entity this, Weapon w,.entity weaponentity)
Definition selection.qc:265
float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, bool complain, bool skipmissing,.entity weaponentity)
Definition selection.qc:122
void W_PreviousWeapon(entity this, float list,.entity weaponentity)
Goto prev weapon.
Definition selection.qc:321
void W_SwitchWeapon_Force(Player this, Weapon wep,.entity weaponentity)
Definition selection.qc:237
void W_NextWeapon(entity this, int list,.entity weaponentity)
Goto next weapon.
Definition selection.qc:310
void Send_WeaponComplain(entity e, float wpn, float type)
Switch between weapons.
Definition selection.qc:18
void W_CycleWeapon(entity this, string weaponorder, float dir,.entity weaponentity)
Definition selection.qc:295
void W_SwitchWeapon_TryOthers(entity this, Weapon w,.entity weaponentity)
Definition selection.qc:289
void Weapon_whereis(Weapon this, entity cl)
Definition selection.qc:26
void W_NextWeaponOnImpulse(entity this, float imp,.entity weaponentity)
Definition selection.qc:302
bool client_hasweapon(entity this, Weapon wpn,.entity weaponentity, float andammo, bool complain)
Definition selection.qc:47
void W_SwitchToOtherWeapon(entity this,.entity weaponentity)
Perform weapon to attack (weaponstate and attack_finished check is here)
Definition selection.qc:247
void W_LastWeapon(entity this,.entity weaponentity)
Goto previously used if exists and has ammo, (second) best otherwise.
Definition selection.qc:332
int weaponcomplainindex
Definition selection.qh:20
WepSet dual_weapons
Definition selection.qh:12
#define w_getbestweapon(ent, wepent)
Definition selection.qh:23
int selectweapon
Definition selection.qh:10
bool autocvar_g_weaponswitch_debug
Definition selection.qh:7
float hasweapon_complain_spam
Definition selection.qh:17
int autocvar_g_showweaponspawns
Definition selection.qh:6
string weaponorder_byimpulse
Definition client.qh:62
int int int imp
Definition impulse.qc:90
int dir
Definition impulse.qc:89
IntrusiveList g_items
Definition items.qh:119
void play2(entity e, string filename)
Definition all.qc:116
#define SND(id)
Definition all.qh:35
#define CS_CVAR(this)
Definition state.qh:51
#define PS(this)
Definition state.qh:18
ClientState CS(Client this)
Definition state.qh:47
ERASEABLE string car(string s)
returns first word
Definition string.qh:259
ERASEABLE string cdr(string s)
returns all but first word
Definition string.qh:268
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
void Weapon_whereis(Weapon this, entity cl)
Definition selection.qc:26
#define WepSet_FromWeapon(it)
Definition all.qh:48
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
string weaponorder_byid
Definition weapon.qh:272
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
const int WEP_FLAG_MUTATORBLOCKED
Definition weapon.qh:261
const int WEP_FLAG_DUALWIELD
Definition weapon.qh:264
vector WepSet
Definition weapon.qh:14
OffhandWeapon offhand
Definition weapon.qh:241
int weaponslot(.entity weaponentity)
Definition weapon.qh:19
const int WEP_FLAG_HIDDEN
Definition weapon.qh:258
entity weaponorder[REGISTRY_MAX(Weapons)]
Definition weapons.qc:81
bool weaponLocked(entity player)
Weapon m_weapon
Definition wepent.qh:26
Weapon m_switchweapon
Definition wepent.qh:25