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