]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/weapons/selection.qc
Clean up some for() loops, also replace a few remaining cases of VHF_ISVEHICLE with...
[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         FOREACH_ENTITY_FLOAT(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 this, Weapon wpn, float andammo, bool complain)
44 {
45         float f = 0;
46
47         if (time < this.hasweapon_complain_spam)
48                 complain = 0;
49
50         // ignore hook button when using other offhand equipment
51         if (this.offhand != OFFHAND_HOOK)
52         if (wpn == WEP_HOOK && !((this.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
53             complain = 0;
54
55         if (complain)
56                 this.hasweapon_complain_spam = time + 0.2;
57
58         if (wpn == WEP_Null)
59         {
60                 if (complain)
61                         sprint(this, "Invalid weapon\n");
62                 return false;
63         }
64         if (this.weapons & WepSet_FromWeapon(wpn))
65         {
66                 if (andammo)
67                 {
68                         if(this.items & IT_UNLIMITED_WEAPON_AMMO)
69                         {
70                                 f = 1;
71                         }
72                         else
73                         {
74                                 f = wpn.wr_checkammo1(wpn, this) + wpn.wr_checkammo2(wpn, this);
75
76                                 // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
77                                 if(wpn == WEP_MINE_LAYER)
78                                         FOREACH_ENTITY_CLASS("mine", it.owner == this,
79                                         {
80                                                 f = 1;
81                                                 break; // no need to continue
82                                         });
83                         }
84                         if (!f)
85                         {
86                                 if (complain)
87                                 if(IS_REAL_CLIENT(this))
88                                 {
89                                         play2(this, SND(UNAVAILABLE));
90                                         Send_WeaponComplain (this, wpn.m_id, 0);
91                                 }
92                                 return false;
93                         }
94                 }
95                 return true;
96         }
97         if (complain)
98         {
99                 // DRESK - 3/16/07
100                 // Report Proper Weapon Status / Modified Weapon Ownership Message
101                 if (weaponsInMap & WepSet_FromWeapon(wpn))
102                 {
103                         Send_WeaponComplain(this, wpn.m_id, 1);
104                         Weapon_whereis(wpn, this);
105                 }
106                 else
107                 {
108                         Send_WeaponComplain (this, wpn.m_id, 2);
109                 }
110
111                 play2(this, SND(UNAVAILABLE));
112         }
113         return false;
114 }
115
116 float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, float complain, float skipmissing)
117 {
118         // We cannot tokenize in this function, as GiveItems calls this
119         // function. Thus we must use car/cdr.
120         float weaponwant, first_valid, prev_valid, switchtonext, switchtolast;
121         WepSet wepset = '0 0 0';
122         switchtonext = switchtolast = 0;
123         first_valid = prev_valid = 0;
124         float weaponcur;
125         entity wep;
126
127         if(skipmissing || this.selectweapon == 0)
128                 weaponcur = PS(this).m_switchweapon.m_id;
129         else
130                 weaponcur = this.selectweapon;
131
132         if(dir == 0)
133                 switchtonext = 1;
134
135         int c = 0;
136
137         string rest = weaponorder;
138         while(rest != "")
139         {
140                 weaponwant = stof(car(rest)); rest = cdr(rest);
141                 wep = Weapons_from(weaponwant);
142                 wepset = wep.m_wepset;
143                 if(imp >= 0)
144                 if(wep.impulse != imp)
145                         continue;
146
147                 bool have_other = false;
148                 FOREACH(Weapons, it != WEP_Null, {
149                         if(i != weaponwant)
150                         if(it.impulse == imp || imp < 0)
151                         if((this.weapons & (it.m_wepset)) || (weaponsInMap & (it.m_wepset)))
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(!(this.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(this, wep, 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                 this.weaponcomplainindex += 1;
192                 c = (this.weaponcomplainindex % c) + 1;
193                 rest = weaponorder;
194                 while(rest != "")
195                 {
196                         weaponwant = stof(car(rest)); rest = cdr(rest);
197                         wep = Weapons_from(weaponwant);
198                         wepset = wep.m_wepset;
199                         if(imp >= 0)
200                                 if(wep.impulse != imp)
201                                         continue;
202
203                         bool have_other = false;
204                         FOREACH(Weapons, it != WEP_Null, {
205                                 if(i != weaponwant)
206                                 if(it.impulse == imp || imp < 0)
207                                 if((this.weapons & (it.m_wepset)) || (weaponsInMap & (it.m_wepset)))
208                                         have_other = true;
209                         });
210
211                         // skip weapons we don't own that aren't normal and aren't in the map
212                         if(!(this.weapons & wepset))
213                         if(!(weaponsInMap & wepset))
214                         if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other)
215                                 continue;
216
217                         --c;
218                         if(c == 0)
219                         {
220                                 client_hasweapon(this, wep, true, true);
221                                 break;
222                         }
223                 }
224         }
225         return 0;
226 }
227
228 void W_SwitchWeapon_Force(Player this, Weapon wep)
229 {
230     TC(Player, this); TC(Weapon, wep);
231         this.cnt = PS(this).m_switchweapon.m_id;
232         PS(this).m_switchweapon = wep;
233         this.selectweapon = wep.m_id;
234 }
235
236 // perform weapon to attack (weaponstate and attack_finished check is here)
237 void W_SwitchToOtherWeapon(entity this)
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         Weapon ww;
241         WepSet set = WepSet_FromWeapon(PS(this).m_weapon);
242         if (this.weapons & set)
243         {
244                 this.weapons &= ~set;
245                 ww = w_getbestweapon(this);
246                 this.weapons |= set;
247         }
248         else
249         {
250                 ww = w_getbestweapon(this);
251         }
252         if (ww == WEP_Null) return;
253         W_SwitchWeapon_Force(this, ww);
254 }
255
256 void W_SwitchWeapon(entity this, Weapon w)
257 {
258         if (PS(this).m_switchweapon != w)
259         {
260                 if (client_hasweapon(this, w, true, true))
261                         W_SwitchWeapon_Force(this, w);
262                 else
263                         this.selectweapon = w.m_id; // update selectweapon ANYWAY
264         }
265         else if(!forbidWeaponUse(this)) {
266                 entity actor = this;
267                 .entity weaponentity = weaponentities[0]; // TODO: unhardcode
268                 w.wr_reload(w, actor, weaponentity);
269         }
270 }
271
272 void W_CycleWeapon(entity this, string weaponorder, float dir)
273 {
274         float w;
275         w = W_GetCycleWeapon(this, weaponorder, dir, -1, 1, true);
276         if(w > 0)
277                 W_SwitchWeapon(this, Weapons_from(w));
278 }
279
280 void W_NextWeaponOnImpulse(entity this, float imp)
281 {
282         float w;
283         w = W_GetCycleWeapon(this, this.cvar_cl_weaponpriority, +1, imp, 1, (this.cvar_cl_weaponimpulsemode == 0));
284         if(w > 0)
285                 W_SwitchWeapon(this, Weapons_from(w));
286 }
287
288 // next weapon
289 void W_NextWeapon(entity this, int list)
290 {
291         if(list == 0)
292                 W_CycleWeapon(this, weaponorder_byid, -1);
293         else if(list == 1)
294                 W_CycleWeapon(this, this.weaponorder_byimpulse, -1);
295         else if(list == 2)
296                 W_CycleWeapon(this, this.cvar_cl_weaponpriority, -1);
297 }
298
299 // prev weapon
300 void W_PreviousWeapon(entity this, float list)
301 {
302         if(list == 0)
303                 W_CycleWeapon(this, weaponorder_byid, +1);
304         else if(list == 1)
305                 W_CycleWeapon(this, this.weaponorder_byimpulse, +1);
306         else if(list == 2)
307                 W_CycleWeapon(this, this.cvar_cl_weaponpriority, +1);
308 }
309
310 // previously used if exists and has ammo, (second) best otherwise
311 void W_LastWeapon(entity this)
312 {
313         Weapon wep = Weapons_from(this.cnt);
314         if (client_hasweapon(this, wep, true, false))
315                 W_SwitchWeapon(this, wep);
316         else
317                 W_SwitchToOtherWeapon(this);
318 }