]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/all.qc
Remove _all indirection
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / all.qc
1 #ifndef WEAPONS_ALL_C
2 #define WEAPONS_ALL_C
3
4 #include "all.qh"
5
6 #if defined(CSQC)
7         #include <client/defs.qh>
8         #include "../constants.qh"
9         #include "../stats.qh"
10         #include <lib/warpzone/anglestransform.qh>
11         #include <lib/warpzone/common.qh>
12         #include <lib/warpzone/client.qh>
13         #include "../util.qh"
14         #include <client/autocvars.qh>
15         #include "../deathtypes/all.qh"
16         #include <lib/csqcmodel/interpolate.qh>
17         #include "../physics/movetypes/movetypes.qh"
18         #include <client/main.qh>
19         #include <lib/csqcmodel/cl_model.qh>
20 #elif defined(MENUQC)
21 #elif defined(SVQC)
22         #include <common/items/_mod.qh>
23     #include <lib/warpzone/anglestransform.qh>
24     #include <lib/warpzone/common.qh>
25     #include <lib/warpzone/util_server.qh>
26     #include <lib/warpzone/server.qh>
27     #include "../constants.qh"
28     #include "../stats.qh"
29     #include "../teams.qh"
30     #include "../util.qh"
31     #include "../monsters/all.qh"
32     #include "config.qh"
33     #include <server/weapons/csqcprojectile.qh>
34     #include <server/weapons/tracing.qh>
35     #include "../t_items.qh"
36     #include <server/autocvars.qh>
37     #include <server/constants.qh>
38     #include <server/defs.qh>
39     #include "../notifications/all.qh"
40     #include "../deathtypes/all.qh"
41     #include <server/mutators/_mod.qh>
42     #include "../mapinfo.qh"
43     #include <server/command/_mod.qh>
44     #include <lib/csqcmodel/sv_model.qh>
45     #include <server/portals.qh>
46     #include <server/g_hook.qh>
47 #endif
48 #ifndef MENUQC
49         #include "calculations.qc"
50 #endif
51 #ifdef SVQC
52         #include "config.qc"
53 #endif
54 #define IMPLEMENTATION
55 #include "all.inc"
56 #undef IMPLEMENTATION
57
58 // WEAPON PLUGIN SYSTEM
59
60 WepSet _WepSet_FromWeapon(int a)
61 {
62         a -= WEP_FIRST;
63         if (Weapons_MAX > 24)
64                 if (a >= 24)
65                 {
66                         a -= 24;
67                         if (Weapons_MAX > 48)
68                                 if (a >= 24)
69                                 {
70                                         a -= 24;
71                                         return '0 0 1' * power2of(a);
72                                 }
73                         return '0 1 0' * power2of(a);
74                 }
75         return '1 0 0' * power2of(a);
76 }
77 #ifdef SVQC
78         void WriteWepSet(float dst, WepSet w)
79         {
80                 if (Weapons_MAX > 48) WriteInt72_t(dst, w);
81                 else if (Weapons_MAX > 24) WriteInt48_t(dst, w);
82                 else WriteInt24_t(dst, w.x);
83         }
84 #endif
85 #ifdef CSQC
86         WepSet WepSet_GetFromStat()
87         {
88                 return STAT(WEAPONS);
89         }
90         WepSet WepSet_GetFromStat_InMap()
91         {
92                 return STAT(WEAPONSINMAP);
93         }
94         WepSet ReadWepSet()
95         {
96                 if (Weapons_MAX > 48) return ReadInt72_t();
97                 if (Weapons_MAX > 24) return ReadInt48_t();
98                 return ReadInt24_t() * '1 0 0';
99         }
100 #endif
101
102 string W_FixWeaponOrder(string order, float complete)
103 {
104         return fixPriorityList(order, WEP_FIRST, WEP_LAST, WEP_IMPULSE_BEGIN - WEP_FIRST, complete);
105 }
106 string W_NameWeaponOrder_MapFunc(string s)
107 {
108         int i = stof(s);
109         if (s == "0" || i)
110         {
111                 entity wi = Weapons_from(i);
112                 if (wi != WEP_Null) return wi.netname;
113         }
114         return s;
115 }
116
117 string W_UndeprecateName(string s)
118 {
119         switch (s)
120         {
121                 case "nex": return "vortex";
122                 case "rocketlauncher": return "devastator";
123                 case "laser": return "blaster";
124                 case "minstanex": return "vaporizer";
125                 case "grenadelauncher": return "mortar";
126                 case "uzi": return "machinegun";
127                 default: return s;
128         }
129 }
130 string W_NameWeaponOrder(string order)
131 {
132         return mapPriorityList(order, W_NameWeaponOrder_MapFunc);
133 }
134 string W_NumberWeaponOrder_MapFunc(string s)
135 {
136         if (s == "0" || stof(s)) return s;
137         s = W_UndeprecateName(s);
138         FOREACH(Weapons, it != WEP_Null && it.netname == s, return ftos(i));
139         return s;
140 }
141 string W_NumberWeaponOrder(string order)
142 {
143         return mapPriorityList(order, W_NumberWeaponOrder_MapFunc);
144 }
145
146 float W_FixWeaponOrder_BuildImpulseList_buf[Weapons_MAX];
147 string W_FixWeaponOrder_BuildImpulseList_order;
148 void W_FixWeaponOrder_BuildImpulseList_swap(int i, int j, entity pass)
149 {
150         float h;
151         h = W_FixWeaponOrder_BuildImpulseList_buf[i];
152         W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j];
153         W_FixWeaponOrder_BuildImpulseList_buf[j] = h;
154 }
155 float W_FixWeaponOrder_BuildImpulseList_cmp(int i, int j, entity pass)
156 {
157         int si = W_FixWeaponOrder_BuildImpulseList_buf[i];
158         Weapon e1 = Weapons_from(si);
159         int sj = W_FixWeaponOrder_BuildImpulseList_buf[j];
160         Weapon e2 = Weapons_from(sj);
161         int d = (e1.impulse + 9) % 10 - (e2.impulse + 9) % 10;
162         if (d != 0) return -d;  // high impulse first!
163         return strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "),
164                 sprintf(" %d ", si), 0)
165                -
166                strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "),
167                 sprintf(" %d ", sj), 0)
168         ;  // low char index first!
169 }
170 string W_FixWeaponOrder_BuildImpulseList(string o)
171 {
172         int i;
173         W_FixWeaponOrder_BuildImpulseList_order = o;
174         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
175                 W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i;
176         heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp,
177                 NULL);
178         o = "";
179         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
180                 o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST]));
181         W_FixWeaponOrder_BuildImpulseList_order = string_null;
182         return substring(o, 1, -1);
183 }
184
185 string W_FixWeaponOrder_AllowIncomplete(entity this, string order)
186 {
187         return W_FixWeaponOrder(order, 0);
188 }
189
190 string W_FixWeaponOrder_ForceComplete(string order)
191 {
192         if (order == "") order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
193         return W_FixWeaponOrder(order, 1);
194 }
195
196 void W_RandomWeapons(entity e, float n)
197 {
198         int i;
199         WepSet remaining;
200         WepSet result;
201         remaining = e.weapons;
202         result = '0 0 0';
203         for (i = 0; i < n; ++i)
204         {
205                 RandomSelection_Init();
206                 FOREACH(Weapons, it != WEP_Null, {
207                         if (remaining & (it.m_wepset))
208                                 RandomSelection_Add(it, 0, string_null, 1, 1);
209                 });
210                 Weapon w = RandomSelection_chosen_ent;
211                 result |= WepSet_FromWeapon(w);
212                 remaining &= ~WepSet_FromWeapon(w);
213         }
214         e.weapons = result;
215 }
216
217 string GetAmmoPicture(.int ammotype)
218 {
219         switch (ammotype)
220         {
221                 case ammo_shells:  return ITEM_Shells.m_icon;
222                 case ammo_nails:   return ITEM_Bullets.m_icon;
223                 case ammo_rockets: return ITEM_Rockets.m_icon;
224                 case ammo_cells:   return ITEM_Cells.m_icon;
225                 case ammo_plasma:  return ITEM_Plasma.m_icon;
226                 case ammo_fuel:    return ITEM_JetpackFuel.m_icon;
227                 default: return "";  // wtf, no ammo type?
228         }
229 }
230
231 #ifdef CSQC
232         .int GetAmmoFieldFromNum(int i)
233         {
234                 switch (i)
235                 {
236                         case 0: return ammo_shells;
237                         case 1: return ammo_nails;
238                         case 2: return ammo_rockets;
239                         case 3: return ammo_cells;
240                         case 4: return ammo_plasma;
241                         case 5: return ammo_fuel;
242                         default: return ammo_none;
243                 }
244         }
245
246         int GetAmmoStat(.int ammotype)
247         {
248                 switch (ammotype)
249                 {
250                         case ammo_shells: return STAT_SHELLS;
251                         case ammo_nails: return STAT_NAILS;
252                         case ammo_rockets: return STAT_ROCKETS;
253                         case ammo_cells: return STAT_CELLS;
254                         case ammo_plasma: return STAT_PLASMA.m_id;
255                         case ammo_fuel: return STAT_FUEL.m_id;
256                         default: return -1;
257                 }
258         }
259 #endif
260
261 string W_Sound(string w_snd)
262 {
263         string output = strcat("weapons/", w_snd);
264 #ifdef SVQC
265                 MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
266                 return M_ARGV(1, string);
267 #else
268                 return output;
269 #endif
270 }
271
272 string W_Model(string w_mdl)
273 {
274         string output = strcat("models/weapons/", w_mdl);
275         MUTATOR_CALLHOOK(WeaponModel, w_mdl, output);
276         return M_ARGV(1, string);
277 }
278
279 #ifndef MENUQC
280 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
281 {
282         switch (algn)
283         {
284                 default:
285                 case 3:
286                         // right alignment
287                         break;
288                 case 4:
289                         // left
290                         vecs.y = -vecs.y;
291                         break;
292                 case 1:
293                 case 2:
294                         // center
295                         vecs.y = 0;
296                         vecs.z -= 2;
297                         break;
298         }
299         return vecs;
300 }
301
302 vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn)
303 {
304 #ifdef SVQC
305         string s;
306 #endif
307         if (visual)
308         {
309                 vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
310         }
311 #ifdef SVQC
312         else if (autocvar_g_shootfromeye)
313         {
314                 vecs.y = vecs.z = 0;
315         }
316         else if (autocvar_g_shootfromcenter)
317         {
318                 vecs.y = 0;
319                 vecs.z -= 2;
320         }
321         else if ((s = autocvar_g_shootfromfixedorigin) != "")
322         {
323                 vector v = stov(s);
324                 if (y_is_right) v.y = -v.y;
325                 if (v.x != 0) vecs.x = v.x;
326                 vecs.y = v.y;
327                 vecs.z = v.z;
328         }
329 #endif
330         else  // just do the same as top
331         {
332                 vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
333         }
334
335         return vecs;
336 }
337
338 #define shotorg_adjust shotorg_adjust_values
339
340 /**
341  * supported formats:
342  *
343  * 1. simple animated model, muzzle flash handling on h_ model:
344  *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
345  *      tags:
346  *        shot = muzzle end (shot origin, also used for muzzle flashes)
347  *        shell = casings ejection point (must be on the right hand side of the gun)
348  *        weapon = attachment for v_tuba.md3
349  *    v_tuba.md3 - first and third person model
350  *    g_tuba.md3 - pickup model
351  *
352  * 2. simple animated model, muzzle flash handling on v_ model:
353  *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
354  *      tags:
355  *        weapon = attachment for v_tuba.md3
356  *    v_tuba.md3 - first and third person model
357  *      tags:
358  *        shot = muzzle end (shot origin, also used for muzzle flashes)
359  *        shell = casings ejection point (must be on the right hand side of the gun)
360  *    g_tuba.md3 - pickup model
361  *
362  * 3. fully animated model, muzzle flash handling on h_ model:
363  *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
364  *      tags:
365  *        shot = muzzle end (shot origin, also used for muzzle flashes)
366  *        shell = casings ejection point (must be on the right hand side of the gun)
367  *        handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
368  *    v_tuba.md3 - third person model
369  *    g_tuba.md3 - pickup model
370  *
371  * 4. fully animated model, muzzle flash handling on v_ model:
372  *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
373  *      tags:
374  *        shot = muzzle end (shot origin)
375  *        shell = casings ejection point (must be on the right hand side of the gun)
376  *    v_tuba.md3 - third person model
377  *      tags:
378  *        shot = muzzle end (for muzzle flashes)
379  *    g_tuba.md3 - pickup model
380  *
381  * writes:
382  *   this.origin, this.angles
383  *   this.weaponchild
384  *   this.movedir, this.view_ofs
385  *   attachment stuff
386  *   anim stuff
387  * to free:
388  *   call again with ""
389  *   remove the ent
390  */
391 void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
392 {
393         if (name == "")
394         {
395                 this.model = "";
396                 if (this.weaponchild) delete(this.weaponchild);
397                 this.weaponchild = NULL;
398                 this.movedir = '0 0 0';
399                 this.spawnorigin = '0 0 0';
400                 this.oldorigin = '0 0 0';
401                 this.anim_fire1  = '0 1 0.01';
402                 this.anim_fire2  = '0 1 0.01';
403                 this.anim_idle   = '0 1 0.01';
404                 this.anim_reload = '0 1 0.01';
405         }
406         else
407         {
408                 // if there is a child entity, hide it until we're sure we use it
409                 if (this.weaponchild) this.weaponchild.model = "";
410                 _setmodel(this, W_Model(strcat("v_", name, ".md3")));
411                 int v_shot_idx;  // used later
412                 (v_shot_idx = gettagindex(this, "shot")) || (v_shot_idx = gettagindex(this, "tag_shot"));
413
414                 _setmodel(this, W_Model(strcat("h_", name, ".iqm")));
415                 // preset some defaults that work great for renamed zym files (which don't need an animinfo)
416                 this.anim_fire1  = animfixfps(this, '0 1 0.01', '0 0 0');
417                 this.anim_fire2  = animfixfps(this, '1 1 0.01', '0 0 0');
418                 this.anim_idle   = animfixfps(this, '2 1 0.01', '0 0 0');
419                 this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0');
420
421                 // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
422                 // if we don't, this is a "real" animated model
423                 string t;
424                 if ((t = "weapon", gettagindex(this, t)) || (t = "tag_weapon", gettagindex(this, t)))
425                 {
426                         if (!this.weaponchild)
427                         {
428                                 this.weaponchild = new(weaponchild);
429 #ifdef CSQC
430                                 this.weaponchild.drawmask = MASK_NORMAL;
431                                 this.weaponchild.renderflags |= RF_VIEWMODEL;
432 #endif
433                         }
434                         _setmodel(this.weaponchild, W_Model(strcat("v_", name, ".md3")));
435                         setsize(this.weaponchild, '0 0 0', '0 0 0');
436                         setattachment(this.weaponchild, this, t);
437                 }
438                 else
439                 {
440                         if (this.weaponchild) delete(this.weaponchild);
441                         this.weaponchild = NULL;
442                 }
443
444                 setsize(this, '0 0 0', '0 0 0');
445                 setorigin(this, '0 0 0');
446                 this.angles = '0 0 0';
447                 this.frame = 0;
448 #ifdef SVQC
449                 this.viewmodelforclient = NULL;
450 #else
451                 this.renderflags &= ~RF_VIEWMODEL;
452 #endif
453                 if (v_shot_idx)  // v_ model attached to invisible h_ model
454                 {
455                         this.movedir = gettaginfo(this.weaponchild, v_shot_idx);
456                 }
457                 else
458                 {
459                         int idx;
460                         if ((idx = gettagindex(this, "shot")) || (idx = gettagindex(this, "tag_shot")))
461                         {
462                                 this.movedir = gettaginfo(this, idx);
463                         }
464                         else
465                         {
466                                 LOG_WARNF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong",
467                                         this.model);
468                                 this.movedir = '0 0 0';
469                         }
470                 }
471                 {
472                         int idx = 0;
473                         // v_ model attached to invisible h_ model
474                         if (this.weaponchild
475                             && ((idx = gettagindex(this.weaponchild, "shell")) || (idx = gettagindex(this.weaponchild, "tag_shell"))))
476                         {
477                                 this.spawnorigin = gettaginfo(this.weaponchild, idx);
478                         }
479                         else if ((idx = gettagindex(this, "shell")) || (idx = gettagindex(this, "tag_shell")))
480                         {
481                                 this.spawnorigin = gettaginfo(this, idx);
482                         }
483                         else
484                         {
485                                 LOG_WARNF("weapon model %s does not support the 'shell' tag, will display casings wrong",
486                                         this.model);
487                                 this.spawnorigin = this.movedir;
488                         }
489                 }
490                 if (v_shot_idx)
491                 {
492                         this.oldorigin = '0 0 0';  // use regular attachment
493                 }
494                 else
495                 {
496                         int idx;
497                         if (this.weaponchild)
498                                 (idx = gettagindex(this, "weapon")) || (idx = gettagindex(this, "tag_weapon"));
499                         else
500                                 (idx = gettagindex(this, "handle")) || (idx = gettagindex(this, "tag_handle"));
501                         if (idx)
502                         {
503                                 this.oldorigin = this.movedir - gettaginfo(this, idx);
504                         }
505                         else
506                         {
507                                 LOG_WARNF(
508                                         "weapon model %s does not support the 'handle' tag "
509                                         "and neither does the v_ model support the 'shot' tag, "
510                                         "will display muzzle flashes TOTALLY wrong\n",
511                                         this.model);
512                                 this.oldorigin = '0 0 0';  // there is no way to recover from this
513                         }
514                 }
515
516 #ifdef SVQC
517                 this.viewmodelforclient = this.owner;
518 #else
519                 this.renderflags |= RF_VIEWMODEL;
520 #endif
521         }
522
523         this.view_ofs = '0 0 0';
524
525         if (this.movedir.x >= 0)
526         {
527                 int algn = STAT(GUNALIGN, this.owner);
528                 vector v = this.movedir;
529                 this.movedir = shotorg_adjust(v, false, false, algn);
530                 this.view_ofs = shotorg_adjust(v, false, true, algn) - v;
531         }
532         int compressed_shotorg = compressShotOrigin(this.movedir);
533         // make them match perfectly
534 #ifdef SVQC
535         this.movedir = decompressShotOrigin(this.owner.stat_shotorg = compressed_shotorg);
536 #else
537         this.movedir = decompressShotOrigin(compressed_shotorg);
538 #endif
539
540         this.spawnorigin += this.view_ofs;  // offset the casings origin by the same amount
541
542         // check if an instant weapon switch occurred
543         setorigin(this, this.view_ofs);
544         if (!_anim) return;
545         // reset animstate now
546         this.wframe = WFRAME_IDLE;
547         setanim(this, this.anim_idle, true, false, true);
548 }
549 #endif
550
551 #ifndef MENUQC
552
553 REGISTER_NET_TEMP(wframe)
554 #ifdef CSQC
555 NET_HANDLE(wframe, bool isNew)
556 {
557         vector a;
558         a.x = ReadCoord();
559     a.y = ReadCoord();
560     a.z = ReadCoord();
561         bool restartanim = ReadByte();
562         anim_set(viewmodel, a, !restartanim, restartanim, restartanim);
563         viewmodel.state = ReadByte();
564         viewmodel.weapon_nextthink = ReadFloat();
565         viewmodel.alpha = ReadByte() / 255;
566         switch (viewmodel.state)
567         {
568                 case WS_RAISE:
569                         viewmodel.weapon_switchdelay = activeweapon.switchdelay_raise;
570                         break;
571                 case WS_DROP:
572                         viewmodel.weapon_switchdelay = activeweapon.switchdelay_drop;
573                         break;
574         default:
575             viewmodel.weapon_switchdelay = 0;
576             break;
577         }
578         return true;
579 }
580 #endif
581
582 #ifdef SVQC
583 void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim)
584 {
585         if (!IS_REAL_CLIENT(actor)) return;
586         int channel = MSG_ONE;
587         msg_entity = actor;
588         WriteHeader(channel, wframe);
589         WriteCoord(channel, a.x);
590         WriteCoord(channel, a.y);
591         WriteCoord(channel, a.z);
592         WriteByte(channel, restartanim);
593         WriteByte(channel, weaponentity.state);
594         WriteFloat(channel, weaponentity.weapon_nextthink);
595         WriteByte(channel, weaponentity.m_alpha * 255);
596 }
597 #endif
598
599 REGISTER_NET_C2S(w_whereis)
600 #ifdef SVQC
601 void Weapon_whereis(Weapon this, entity cl);
602 NET_HANDLE(w_whereis, bool)
603 {
604         Weapon wpn = ReadRegistered(Weapons);
605         if (wpn != WEP_Null) Weapon_whereis(wpn, sender);
606         return true;
607 }
608 #else
609 void w_whereis(Weapon this)
610 {
611         int channel = MSG_C2S;
612         WriteHeader(channel, w_whereis);
613         WriteRegistered(Weapons, channel, this);
614 }
615 CLIENT_COMMAND(weapon_find, "Show spawn locations of a weapon")
616 {
617         switch (request)
618         {
619                 case CMD_REQUEST_COMMAND:
620                 {
621                         string s = argv(1);
622                         if (s == "all")
623                         {
624                                 FOREACH(Weapons, it != WEP_Null, w_whereis(it));
625                                 return;
626                         }
627                         if (s == "unowned")
628                         {
629                                 FOREACH(Weapons, it != WEP_Null && !(STAT(WEAPONS) & it.m_wepset), w_whereis(it));
630                                 return;
631                         }
632                         FOREACH(Weapons, it != WEP_Null && it.netname == s,
633                         {
634                                 w_whereis(it);
635                                 return;
636                         });
637                 }
638                 default:
639                 {
640                         LOG_INFOF("Incorrect parameters for ^2%s^7\n", "weapon_find");
641                 }
642                 case CMD_REQUEST_USAGE:
643                 {
644                         LOG_INFO("\nUsage:^3 cl_cmd weapon_find weapon\n");
645                         LOG_INFO("  Where 'weapon' is the lowercase weapon name, 'all' or 'unowned'.\n");
646                         return;
647                 }
648         }
649 }
650 #endif
651
652
653 #endif
654
655 #endif