]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/weapons/spawning.qc
Move weapon spawning into its own file
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / spawning.qc
1 float forbidWeaponUse()
2 {
3         if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
4                 return 1;
5         if(round_handler_IsActive() && !round_handler_IsRoundStarted())
6                 return 1;
7         if(self.player_blocked)
8                 return 1;
9         if(self.freezetag_frozen)
10                 return 1;
11         return 0;
12 }
13
14 void W_WeaponFrame()
15 {
16         vector fo, ri, up;
17
18         if (frametime)
19                 self.weapon_frametime = frametime;
20
21         if (!self.weaponentity || self.health < 1)
22                 return; // Dead player can't use weapons and injure impulse commands
23
24         if(forbidWeaponUse())
25         if(self.weaponentity.state != WS_CLEAR)
26         {
27                 w_ready();
28                 return;
29         }
30
31         if(!self.switchweapon)
32         {
33                 self.weapon = 0;
34                 self.switchingweapon = 0;
35                 self.weaponentity.state = WS_CLEAR;
36                 self.weaponname = "";
37                 self.items &~= IT_AMMO;
38                 return;
39         }
40
41         makevectors(self.v_angle);
42         fo = v_forward; // save them in case the weapon think functions change it
43         ri = v_right;
44         up = v_up;
45
46         // Change weapon
47         if (self.weapon != self.switchweapon)
48         {
49                 if (self.weaponentity.state == WS_CLEAR)
50                 {
51                         // end switching!
52                         self.switchingweapon = self.switchweapon;
53                         entity newwep = get_weaponinfo(self.switchweapon);
54
55                         self.items &~= IT_AMMO;
56                         self.items = self.items | (newwep.items & IT_AMMO);
57
58                         // the two weapon entities will notice this has changed and update their models
59                         self.weapon = self.switchweapon;
60                         self.weaponname = newwep.mdl;
61                         self.bulletcounter = 0; // WEAPONTODO
62                         WEP_ACTION(self.switchweapon, WR_SETUP);
63                         self.weaponentity.state = WS_RAISE;
64
65                         // set our clip load to the load of the weapon we switched to, if it's reloadable
66                         if(newwep.spawnflags & WEP_FLAG_RELOADABLE && cvar(strcat("g_balance_", newwep.netname, "_reload_ammo"))) // prevent accessing undefined cvars
67                         {
68                                 self.clip_load = self.(weapon_load[self.switchweapon]);
69                                 self.clip_size = cvar(strcat("g_balance_", newwep.netname, "_reload_ammo"));
70                         }
71                         else
72                                 self.clip_load = self.clip_size = 0;
73
74                         // VorteX: add player model weapon select frame here
75                         // setcustomframe(PlayerWeaponRaise);
76                         weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
77                         //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))));
78                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0');
79                 }
80                 else if (self.weaponentity.state == WS_DROP)
81                 {
82                         // in dropping phase we can switch at any time
83                         self.switchingweapon = self.switchweapon;
84                 }
85                 else if (self.weaponentity.state == WS_READY)
86                 {
87                         // start switching!
88                         self.switchingweapon = self.switchweapon;
89
90                         entity oldwep = get_weaponinfo(self.weapon);
91                         
92 #ifndef INDEPENDENT_ATTACK_FINISHED
93                         if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
94                         {
95 #endif
96                         sound (self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
97                         self.weaponentity.state = WS_DROP;
98                         // set up weapon switch think in the future, and start drop anim
99                         weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
100                         //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))));
101                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);
102 #ifndef INDEPENDENT_ATTACK_FINISHED
103                         }
104 #endif
105                 }
106         }
107
108         // LordHavoc: network timing test code
109         //if (self.button0)
110         //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");
111
112         float w;
113         w = self.weapon;
114
115         // call the think code which may fire the weapon
116         // and do so multiple times to resolve framerate dependency issues if the
117         // server framerate is very low and the weapon fire rate very high
118         float c;
119         c = 0;
120         while (c < W_TICSPERFRAME)
121         {
122                 c = c + 1;
123                 if(w && !WEPSET_CONTAINS_EW(self, w))
124                 {
125                         if(self.weapon == self.switchweapon)
126                                 W_SwitchWeapon_Force(self, w_getbestweapon(self));
127                         w = 0;
128                 }
129
130                 v_forward = fo;
131                 v_right = ri;
132                 v_up = up;
133
134                 if(w)
135                         WEP_ACTION(self.weapon, WR_THINK);
136                 else
137                         WEP_ACTION(self.weapon, WR_GONETHINK);
138
139                 if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)
140                 {
141                         if(self.weapon_think)
142                         {
143                                 v_forward = fo;
144                                 v_right = ri;
145                                 v_up = up;
146                                 self.weapon_think();
147                         }
148                         else
149                                 bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");
150                 }
151         }
152
153 #if 0
154         if (self.items & IT_CELLS)
155                 self.currentammo = self.ammo_cells;
156         else if (self.items & IT_ROCKETS)
157                 self.currentammo = self.ammo_rockets;
158         else if (self.items & IT_NAILS)
159                 self.currentammo = self.ammo_nails;
160         else if (self.items & IT_SHELLS)
161                 self.currentammo = self.ammo_shells;
162         else
163                 self.currentammo = 1;
164 #endif
165 }
166
167 string W_Apply_Weaponreplace(string in)
168 {
169         float n = tokenize_console(in);
170         string out = "";
171         float i;
172         for(i = 0; i < n; ++i)
173         {
174                 string s = argv(i);
175                 string r = cvar_string(strcat("g_weaponreplace_", s));
176                 if(r == "")
177                         out = strcat(out, " ", s);
178                 else if(r != "0")
179                         out = strcat(out, " ", r);
180         }
181         return substring(out, 1, -1);
182 }
183
184 void weapon_defaultspawnfunc(float wpn)
185 {
186         entity e;
187         float t;
188         var .float ammofield;
189         string s;
190         entity oldself;
191         float i, j;
192         float f;
193
194         if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
195         {
196                 e = get_weaponinfo(wpn);
197
198                 if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
199                 {
200                         objerror("Attempted to spawn a mutator-blocked weapon rejected");
201                         startitem_failed = TRUE;
202                         return;
203                 }
204
205                 s = W_Apply_Weaponreplace(e.netname);
206                 ret_string = s;
207                 other = e;
208                 MUTATOR_CALLHOOK(SetWeaponreplace);
209                 s = ret_string;
210                 if(s == "")
211                 {
212                         remove(self);
213                         startitem_failed = TRUE;
214                         return;
215                 }
216                 t = tokenize_console(s);
217                 if(t >= 2)
218                 {
219                         self.team = --internalteam;
220                         oldself = self;
221                         for(i = 1; i < t; ++i)
222                         {
223                                 s = argv(i);
224                                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
225                                 {
226                                         e = get_weaponinfo(j);
227                                         if(e.netname == s)
228                                         {
229                                                 self = spawn();
230                                                 copyentity(oldself, self);
231                                                 self.classname = "replacedweapon";
232                                                 weapon_defaultspawnfunc(j);
233                                                 break;
234                                         }
235                                 }
236                                 if(j > WEP_LAST)
237                                 {
238                                         print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
239                                 }
240                         }
241                         self = oldself;
242                 }
243                 if(t >= 1) // always the case!
244                 {
245                         s = argv(0);
246                         wpn = 0;
247                         for(j = WEP_FIRST; j <= WEP_LAST; ++j)
248                         {
249                                 e = get_weaponinfo(j);
250                                 if(e.netname == s)
251                                 {
252                                         wpn = j;
253                                         break;
254                                 }
255                         }
256                         if(j > WEP_LAST)
257                         {
258                                 print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
259                         }
260                 }
261                 if(wpn == 0)
262                 {
263                         remove(self);
264                         startitem_failed = TRUE;
265                         return;
266                 }
267         }
268
269         e = get_weaponinfo(wpn);
270
271         if(!self.respawntime)
272         {
273                 if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
274                 {
275                         self.respawntime = g_pickup_respawntime_superweapon;
276                         self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
277                 }
278                 else
279                 {
280                         self.respawntime = g_pickup_respawntime_weapon;
281                         self.respawntimejitter = g_pickup_respawntimejitter_weapon;
282                 }
283         }
284
285         if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
286                 if(!self.superweapons_finished)
287                         self.superweapons_finished = autocvar_g_balance_superweapons_time;
288
289         if(e.items)
290         {
291                 for(i = 0, j = 1; i < 24; ++i, j *= 2)
292                 {
293                         if(e.items & j)
294                         {
295                                 ammofield = Item_CounterField(j);
296                                 if(!self.ammofield)
297                                         self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
298                         }
299                 }
300         }
301
302         // pickup anyway
303         if(g_pickup_weapons_anyway)
304                 self.pickup_anyway = TRUE;
305
306         f = FL_WEAPON;
307
308         // no weapon-stay on superweapons
309         if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
310                 f |= FL_NO_WEAPON_STAY;
311
312         // weapon stay isn't supported for teamed weapons
313         if(self.team)
314                 f |= FL_NO_WEAPON_STAY;
315
316         StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
317         if (self.modelindex) // don't precache if self was removed
318                 WEP_ACTION(e.weapon, WR_INIT);
319 }