]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/weapons/selection.qc
de240ce2ef66997ddbec1bf6d5d50c437f51ee7e
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / selection.qc
1 #include "selection.qh"
2 #include "../_all.qh"
3
4 #include "weaponsystem.qh"
5 #include "../t_items.qh"
6 #include "../../common/constants.qh"
7 #include "../../common/util.qh"
8 #include "../../common/weapons/all.qh"
9
10 // switch between weapons
11 void Send_WeaponComplain(entity e, float wpn, float type)
12 {
13         msg_entity = e;
14         WriteByte(MSG_ONE, SVC_TEMPENTITY);
15         WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
16         WriteByte(MSG_ONE, wpn);
17         WriteByte(MSG_ONE, type);
18 }
19
20 float client_hasweapon(entity cl, float wpn, float andammo, float complain)
21 {SELFPARAM();
22         float f;
23
24         if(time < self.hasweapon_complain_spam)
25                 complain = 0;
26
27         if(wpn == WEP_HOOK.m_id && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
28                 complain = 0;
29
30         if(complain)
31                 self.hasweapon_complain_spam = time + 0.2;
32
33         if (wpn < WEP_FIRST || wpn > WEP_LAST)
34         {
35                 if (complain)
36                         sprint(self, "Invalid weapon\n");
37                 return false;
38         }
39         if (cl.weapons & WepSet_FromWeapon(wpn))
40         {
41                 if (andammo)
42                 {
43                         if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
44                         {
45                                 f = 1;
46                         }
47                         else
48                         {
49                                 setself(cl);
50                                 f = WEP_ACTION(wpn, WR_CHECKAMMO1);
51                                 f = f + WEP_ACTION(wpn, WR_CHECKAMMO2);
52
53                                 // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
54                                 entity mine;
55                                 if(wpn == WEP_MINE_LAYER.m_id)
56                                 for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
57                                         f = 1;
58
59                                 setself(this);
60                         }
61                         if (!f)
62                         {
63                                 if (complain)
64                                 if(IS_REAL_CLIENT(cl))
65                                 {
66                                         play2(cl, W_Sound("unavailable"));
67                                         Send_WeaponComplain (cl, wpn, 0);
68                                 }
69                                 return false;
70                         }
71                 }
72                 return true;
73         }
74         if (complain)
75         {
76                 // DRESK - 3/16/07
77                 // Report Proper Weapon Status / Modified Weapon Ownership Message
78                 if (weaponsInMap & WepSet_FromWeapon(wpn))
79                 {
80                         Send_WeaponComplain(cl, wpn, 1);
81
82                         if(autocvar_g_showweaponspawns)
83                         {
84                                 entity e;
85
86                                 for(e = world; (e = findfloat(e, weapon, wpn)); )
87                                 {
88                                         if(e.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2)
89                                                 continue;
90                                         if(!(e.flags & FL_ITEM))
91                                                 continue;
92                                         entity wp = WaypointSprite_Spawn(
93                                                 WP_Weapon,
94                                                 1, 0,
95                                                 world, e.origin + ('0 0 1' * e.maxs.z) * 1.2,
96                                                 self, 0,
97                                                 world, enemy,
98                                                 0,
99                                                 RADARICON_NONE
100                                         );
101                                         wp.wp_extra = wpn;
102                                 }
103                         }
104                 }
105                 else
106                 {
107                         Send_WeaponComplain (cl, wpn, 2);
108                 }
109
110                 play2(cl, W_Sound("unavailable"));
111         }
112         return false;
113 }
114
115 float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing)
116 {SELFPARAM();
117         // We cannot tokenize in this function, as GiveItems calls this
118         // function. Thus we must use car/cdr.
119         float weaponwant, first_valid, prev_valid, switchtonext, switchtolast;
120         WepSet wepset = '0 0 0';
121         switchtonext = switchtolast = 0;
122         first_valid = prev_valid = 0;
123         float weaponcur;
124         entity wep;
125
126         if(skipmissing || pl.selectweapon == 0)
127                 weaponcur = pl.switchweapon;
128         else
129                 weaponcur = pl.selectweapon;
130
131         if(dir == 0)
132                 switchtonext = 1;
133
134         int c = 0;
135
136         string rest = weaponorder;
137         while(rest != "")
138         {
139                 weaponwant = stof(car(rest)); rest = cdr(rest);
140                 wep = get_weaponinfo(weaponwant);
141                 wepset = WepSet_FromWeapon(weaponwant);
142                 if(imp >= 0)
143                 if(wep.impulse != imp)
144                         continue;
145
146                 float i, have_other = false;
147                 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
148                 {
149                         if(i != weaponwant)
150                         if((get_weaponinfo(i)).impulse == imp || imp < 0)
151                         if((pl.weapons & WepSet_FromWeapon(i)) || (weaponsInMap & WepSet_FromWeapon(i)))
152                                 have_other = true;
153                 }
154
155                 // skip weapons we don't own that aren't normal and aren't in the map
156                 if(!(pl.weapons & wepset))
157                 if(!(weaponsInMap & wepset))
158                 if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other)
159                         continue;
160
161                 ++c;
162
163                 if(!skipmissing || client_hasweapon(pl, weaponwant, true, false))
164                 {
165                         if(switchtonext)
166                                 return weaponwant;
167                         if(!first_valid)
168                                 first_valid = weaponwant;
169                         if(weaponwant == weaponcur)
170                         {
171                                 if(dir >= 0)
172                                         switchtonext = 1;
173                                 else if(prev_valid)
174                                         return prev_valid;
175                                 else
176                                         switchtolast = 1;
177                         }
178                         prev_valid = weaponwant;
179                 }
180         }
181         if(first_valid)
182         {
183                 if(switchtolast)
184                         return prev_valid;
185                 else
186                         return first_valid;
187         }
188         // complain (but only for one weapon on the button that has been pressed)
189         if(complain)
190         {
191                 self.weaponcomplainindex += 1;
192                 c = (self.weaponcomplainindex % c) + 1;
193                 rest = weaponorder;
194                 while(rest != "")
195                 {
196                         weaponwant = stof(car(rest)); rest = cdr(rest);
197                         wep = get_weaponinfo(weaponwant);
198                         wepset = WepSet_FromWeapon(weaponwant);
199                         if(imp >= 0)
200                                 if(wep.impulse != imp)
201                                         continue;
202
203                         float i, have_other = false;
204                         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
205                         {
206                                 if(i != weaponwant)
207                                 if((get_weaponinfo(i)).impulse == imp || imp < 0)
208                                 if((pl.weapons & WepSet_FromWeapon(i)) || (weaponsInMap & WepSet_FromWeapon(i)))
209                                         have_other = true;
210                         }
211
212                         // skip weapons we don't own that aren't normal and aren't in the map
213                         if(!(pl.weapons & wepset))
214                         if(!(weaponsInMap & wepset))
215                         if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other)
216                                 continue;
217
218                         --c;
219                         if(c == 0)
220                         {
221                                 client_hasweapon(pl, weaponwant, true, true);
222                                 break;
223                         }
224                 }
225         }
226         return 0;
227 }
228
229 void W_SwitchWeapon_Force(entity e, float w)
230 {
231         e.cnt = e.switchweapon;
232         e.switchweapon = w;
233         e.selectweapon = w;
234 }
235
236 // perform weapon to attack (weaponstate and attack_finished check is here)
237 void W_SwitchToOtherWeapon(entity pl)
238 {
239         // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
240         float w, ww;
241         w = pl.weapon;
242         if(pl.weapons & WepSet_FromWeapon(w))
243         {
244                 pl.weapons &= ~WepSet_FromWeapon(w);
245                 ww = w_getbestweapon(pl);
246                 pl.weapons |= WepSet_FromWeapon(w);
247         }
248         else
249                 ww = w_getbestweapon(pl);
250         if(ww)
251                 W_SwitchWeapon_Force(pl, ww);
252 }
253
254 void W_SwitchWeapon(float imp)
255 {SELFPARAM();
256         if (self.switchweapon != imp)
257         {
258                 if (client_hasweapon(self, imp, true, true))
259                         W_SwitchWeapon_Force(self, imp);
260                 else
261                         self.selectweapon = imp; // update selectweapon ANYWAY
262         }
263         else if(!forbidWeaponUse(self)) { WEP_ACTION(self.weapon, WR_RELOAD); }
264 }
265
266 void W_CycleWeapon(string weaponorder, float dir)
267 {SELFPARAM();
268         float w;
269         w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, true);
270         if(w > 0)
271                 W_SwitchWeapon(w);
272 }
273
274 void W_NextWeaponOnImpulse(float imp)
275 {SELFPARAM();
276         float w;
277         w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0));
278         if(w > 0)
279                 W_SwitchWeapon(w);
280 }
281
282 // next weapon
283 void W_NextWeapon(float list)
284 {SELFPARAM();
285         if(list == 0)
286                 W_CycleWeapon(weaponorder_byid, -1);
287         else if(list == 1)
288                 W_CycleWeapon(self.weaponorder_byimpulse, -1);
289         else if(list == 2)
290                 W_CycleWeapon(self.cvar_cl_weaponpriority, -1);
291 }
292
293 // prev weapon
294 void W_PreviousWeapon(float list)
295 {SELFPARAM();
296         if(list == 0)
297                 W_CycleWeapon(weaponorder_byid, +1);
298         else if(list == 1)
299                 W_CycleWeapon(self.weaponorder_byimpulse, +1);
300         else if(list == 2)
301                 W_CycleWeapon(self.cvar_cl_weaponpriority, +1);
302 }
303
304 // previously used if exists and has ammo, (second) best otherwise
305 void W_LastWeapon(void)
306 {SELFPARAM();
307         if(client_hasweapon(self, self.cnt, true, false))
308                 W_SwitchWeapon(self.cnt);
309         else
310                 W_SwitchToOtherWeapon(self);
311 }