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