Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
all.qc
Go to the documentation of this file.
1#include "all.qh"
2#ifndef WEAPONS_ALL_C
3#define WEAPONS_ALL_C
4
5#if defined(CSQC)
6 #include <client/main.qh>
7 #include <common/constants.qh>
10 #include <common/stats.qh>
11 #include <common/util.qh>
17 #include <lib/warpzone/client.qh>
18 #include <lib/warpzone/common.qh>
19#elif defined(MENUQC)
21#elif defined(SVQC)
22 #include <common/constants.qh>
24 #include <common/items/_mod.qh>
25 #include <common/mapinfo.qh>
28 #include <common/stats.qh>
29 #include <common/teams.qh>
30 #include <common/util.qh>
37 #include <lib/warpzone/common.qh>
38 #include <lib/warpzone/server.qh>
40 #include <server/command/_mod.qh>
41 #include <server/hook.qh>
44 #include <server/portals.qh>
48#endif
49
50
51// WEAPON PLUGIN SYSTEM
52
54{
55 a -= WEP_FIRST;
56 if (REGISTRY_MAX(Weapons) > 24)
57 if (a >= 24)
58 {
59 a -= 24;
60 if (REGISTRY_MAX(Weapons) > 48)
61 if (a >= 24)
62 {
63 a -= 24;
64 return '0 0 1' * BIT(a);
65 }
66 return '0 1 0' * BIT(a);
67 }
68 return '1 0 0' * BIT(a);
69}
70#ifdef SVQC
71 void WriteWepSet(float dst, WepSet w)
72 {
73 if (REGISTRY_MAX(Weapons) > 48) WriteInt72_t(dst, w);
74 else if (REGISTRY_MAX(Weapons) > 24) WriteInt48_t(dst, w);
75 else WriteInt24_t(dst, w.x);
76 }
77#endif
78#ifdef CSQC
80 {
81 return STAT(WEAPONS);
82 }
84 {
85 return STAT(WEAPONSINMAP);
86 }
88 {
89 if (REGISTRY_MAX(Weapons) > 48) return ReadInt72_t();
90 if (REGISTRY_MAX(Weapons) > 24) return ReadInt48_t();
91 return ReadInt24_t() * '1 0 0';
92 }
93#endif
94
95string W_FixWeaponOrder(string order, float complete)
96{
98}
100{
101 int i = stof(s);
102 if (s == "0" || i)
103 {
104 entity wi = REGISTRY_GET(Weapons, i);
105 if (wi != WEP_Null) return wi.netname;
106 }
107 return s;
108}
109
110string W_NameWeaponOrder(string order)
111{
113}
115{
116 if (s == "0" || stof(s)) return s;
117 FOREACH(Weapons, it != WEP_Null && (it.netname == s || it.m_deprecated_netname == s), return ftos(i));
118 return s;
119}
120string W_NumberWeaponOrder(string order)
121{
123}
124
135{
137 Weapon e1 = REGISTRY_GET(Weapons, si);
139 Weapon e2 = REGISTRY_GET(Weapons, sj);
140 int d = (e1.impulse + 9) % 10 - (e2.impulse + 9) % 10;
141 if (d != 0) return -d; // high impulse first!
143 return strstrofs(s, sprintf(" %d ", si), 0)
144 - strstrofs(s, sprintf(" %d ", sj), 0); // low char index first!
145}
160
161string W_FixWeaponOrder_AllowIncomplete(entity this, string order)
162{
163 return W_FixWeaponOrder(order, 0);
164}
165
167{
168 if (order == "") order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
169 return W_FixWeaponOrder(order, 1);
170}
171
173{
174 WepSet result = '0 0 0';
175 for (int j = 0; j < n; ++j)
176 {
178 FOREACH(Weapons, it != WEP_Null, {
179 if (remaining & (it.m_wepset))
180 RandomSelection_AddEnt(it, 1, 1);
181 });
184 remaining &= ~WepSet_FromWeapon(w);
185 }
186 return result;
187}
188
190{
191 FOREACH(Items, it.netname == ammotype.netname,
192 {
193 return it;
194 });
195 LOG_WARNF("Invalid ammo type %d ", ammotype.m_id);
196 return NULL;
197}
198
200{
201 FOREACH(Resources, it.netname == ammotype.netname,
202 {
203 return it;
204 });
205 LOG_WARNF("Invalid ammo type %d ", ammotype.m_id);
206 return NULL;
207}
208
209#ifdef CSQC
211{
212 // TODO: handle networking via resources
213 switch (ammotype)
214 {
215 case RES_SHELLS: return STAT_SHELLS;
216 case RES_BULLETS: return STAT_NAILS;
217 case RES_ROCKETS: return STAT_ROCKETS;
218 case RES_CELLS: return STAT_CELLS;
219 case RES_FUEL: return STAT_FUEL.m_id;
220 default: return -1;
221 }
222}
223#endif
224
225string W_Sound(string w_snd)
226{
227 string output = strcat("weapons/", w_snd);
228 MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
229 return M_ARGV(1, string);
230}
231
232string W_Model(string w_mdl)
233{
234 string output = strcat("models/weapons/", w_mdl);
235 MUTATOR_CALLHOOK(WeaponModel, w_mdl, output);
236 return M_ARGV(1, string);
237}
238
239#ifdef GAMEQC
240vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
241{
242 switch (algn)
243 {
244 default:
245 case 3:
246 // right alignment
247 break;
248 case 4:
249 // left
250 vecs.y = -vecs.y;
251 break;
252 case 1:
253 case 2:
254 // center
255 vecs.y = 0;
256 vecs.z -= 2;
257 break;
258 }
259 return vecs;
260}
261
262vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
263{
264 string s;
265 if (visual)
266 {
267 vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
268 }
269 else if (STAT(SHOOTFROMEYE))
270 {
271 vecs.y = vecs.z = 0;
272 }
273 else if (STAT(SHOOTFROMCENTER))
274 {
275 vecs.y = 0;
276 vecs.z -= 2;
277 }
278 else if ((s = G_SHOOTFROMFIXEDORIGIN) != "")
279 {
280 vector v = stov(s);
281 if (y_is_right) v.y = -v.y;
282 if (v.x != 0) vecs.x = v.x;
283 vecs.y = v.y;
284 vecs.z = v.z;
285 }
286 else // just do the same as top
287 {
288 vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
289 }
290
291 return vecs;
292}
293
294
347void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
348{
349 if (name == "")
350 {
351 vector oldmin = this.mins, oldmax = this.maxs;
352 setmodel(this, MDL_Null);
353 setsize(this, oldmin, oldmax);
354 if (this.weaponchild) delete(this.weaponchild);
355 this.weaponchild = NULL;
356 this.movedir = '0 0 0';
357#ifdef SVQC
358 this.spawnorigin = '0 0 0';
359#endif
360 this.oldorigin = '0 0 0';
361 this.anim_fire1 = '0 1 0.01';
362 this.anim_fire2 = '0 1 0.01';
363 this.anim_idle = '0 1 0.01';
364 this.anim_reload = '0 1 0.01';
365 }
366 else
367 {
368 // if there is a child entity, hide it until we're sure we use it
369 if (this.weaponchild) this.weaponchild.model = "";
370 _setmodel(this, W_Model(strcat("v_", name, ".md3")));
371 int v_shot_idx; // used later
372 (v_shot_idx = gettagindex(this, "shot")) || (v_shot_idx = gettagindex(this, "tag_shot"));
373
374 _setmodel(this, W_Model(strcat("h_", name, ".iqm")));
375 // preset some defaults that work great for renamed zym files (which don't need an animinfo)
376 this.anim_fire1 = animfixfps(this, '0 1 0.01', '0 0 0');
377 this.anim_fire2 = animfixfps(this, '1 1 0.01', '0 0 0');
378 this.anim_idle = animfixfps(this, '2 1 0.01', '0 0 0');
379 this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0');
380
381 // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
382 // if we don't, this is a "real" animated model
383 string t;
384 if ((t = "weapon", gettagindex(this, t)) || (t = "tag_weapon", gettagindex(this, t)))
385 {
386 if (!this.weaponchild)
387 {
388 this.weaponchild = new(weaponchild);
389#ifdef CSQC
390 this.weaponchild.drawmask = MASK_NORMAL;
391 this.weaponchild.renderflags |= RF_VIEWMODEL;
392#endif
393 }
394 _setmodel(this.weaponchild, W_Model(strcat("v_", name, ".md3")));
395 setsize(this.weaponchild, '0 0 0', '0 0 0');
396 setattachment(this.weaponchild, this, t);
397 }
398 else
399 {
400 if (this.weaponchild) delete(this.weaponchild);
401 this.weaponchild = NULL;
402 }
403
404 setsize(this, '0 0 0', '0 0 0');
405 setorigin(this, '0 0 0');
406 this.angles = '0 0 0';
407 this.frame = 0;
408#ifdef SVQC
410#else
412#endif
413 if (v_shot_idx) // v_ model attached to invisible h_ model
414 {
415 this.movedir = gettaginfo(this.weaponchild, v_shot_idx);
416 }
417 else
418 {
419 int idx;
420 if ((idx = gettagindex(this, "shot")) || (idx = gettagindex(this, "tag_shot")))
421 {
422 this.movedir = gettaginfo(this, idx);
423 }
424 else
425 {
426 LOG_WARNF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong",
427 this.model);
428 this.movedir = '0 0 0';
429 }
430 }
431#ifdef SVQC
432 {
433 int idx = 0;
434 // v_ model attached to invisible h_ model
435 if (this.weaponchild
436 && ((idx = gettagindex(this.weaponchild, "shell")) || (idx = gettagindex(this.weaponchild, "tag_shell"))))
437 {
438 this.spawnorigin = gettaginfo(this.weaponchild, idx);
439 }
440 else if ((idx = gettagindex(this, "shell")) || (idx = gettagindex(this, "tag_shell")))
441 {
442 this.spawnorigin = gettaginfo(this, idx);
443 }
444 else
445 {
446 LOG_WARNF("weapon model %s does not support the 'shell' tag, will display casings wrong",
447 this.model);
448 this.spawnorigin = this.movedir;
449 }
450 }
451#endif
452 if (v_shot_idx)
453 {
454 this.oldorigin = '0 0 0'; // use regular attachment
455 }
456 else
457 {
458 int idx;
459 if (this.weaponchild)
460 (idx = gettagindex(this, "weapon")) || (idx = gettagindex(this, "tag_weapon"));
461 else
462 (idx = gettagindex(this, "handle")) || (idx = gettagindex(this, "tag_handle"));
463 if (idx)
464 {
465 this.oldorigin = this.movedir - gettaginfo(this, idx);
466 }
467 else
468 {
469 LOG_WARNF(
470 "weapon model %s does not support the 'handle' tag "
471 "and neither does the v_ model support the 'shot' tag, "
472 "will display muzzle flashes TOTALLY wrong\n",
473 this.model);
474 this.oldorigin = '0 0 0'; // there is no way to recover from this
475 }
476 }
477
478#ifdef SVQC
479 this.viewmodelforclient = this.owner;
480#else
482#endif
483 }
484
485 this.view_ofs = '0 0 0';
486 this.movedir_aligned = this.movedir;
487
488 if (this.movedir.x >= 0)
489 {
490 //int algn = STAT(GUNALIGN, this.owner);
491 int algn = W_GunAlign(this, STAT(GUNALIGN, this.owner));
492 #ifdef SVQC
493 this.m_gunalign = algn;
494 #endif
495 vector v = this.movedir;
496 this.movedir = shotorg_adjust(v, false, false, algn);
497 this.movedir_aligned = shotorg_adjust(v, false, true, algn);
498 this.view_ofs = this.movedir_aligned - v;
499 }
500
501 // shotorg_adjust can give negative .x from shootfromfixedorigin
502 // recheck .x here due to it
503 if (this.movedir.x >= 0)
504 {
505 int compressed_shotorg = compressShotOrigin(this.movedir);
506 // make them match perfectly
507 #ifdef SVQC
508 // null during init
509 if (this.owner) STAT(SHOTORG, this.owner) = compressed_shotorg;
510 #endif
511 this.movedir = decompressShotOrigin(compressed_shotorg);
512 }
513 else
514 {
515 // don't support negative x shotorgs
516 this.movedir = '0 0 0';
517
518 // reset _aligned here too even though as a side effect
519 // g_shootfromfixedorigin can make it reset
520 // it'd be only slightly better if it was checked individually
521 this.movedir_aligned = '0 0 0';
522
523 #ifdef SVQC
524 // null during init
525 if (this.owner) STAT(SHOTORG, this.owner) = 0;
526 #endif
527 }
528
529#ifdef SVQC
530 this.spawnorigin += this.view_ofs; // offset the casings origin by the same amount
531#endif
532
533 // check if an instant weapon switch occurred
534 setorigin(this, this.view_ofs);
535 if (!_anim) return;
536 // reset animstate now
537 this.wframe = WFRAME_IDLE;
538 setanim(this, this.anim_idle, true, false, true);
539}
540#endif
541
542#ifdef GAMEQC
543
545#ifdef CSQC
546NET_HANDLE(wframe, bool isNew)
547{
548 WFRAME fr = ReadByte();
549 float t = ReadFloat();
550 int slot = ReadByte();
551 bool restartanim = ReadByte();
552 entity wepent = viewmodels[slot];
553 if(fr == WFRAME_IDLE)
554 wepent.animstate_looping = false; // we don't need to enforce idle animation
555 else
556 {
557 vector a = '0 0 0';
558 switch(fr)
559 {
560 default:
561 case WFRAME_IDLE: a = wepent.anim_idle; break;
562 case WFRAME_FIRE1: a = wepent.anim_fire1; break;
563 case WFRAME_FIRE2: a = wepent.anim_fire2; break;
564 case WFRAME_RELOAD: a = wepent.anim_reload; break;
565 }
566 a.z *= t;
567 anim_set(wepent, a, !restartanim, restartanim, restartanim);
568 }
569 wepent.state = ReadByte();
570 wepent.weapon_nextthink = ReadFloat();
571 switch (wepent.state)
572 {
573 case WS_RAISE:
574 wepent.weapon_switchdelay = wepent.switchweapon.switchdelay_raise;
575 break;
576 case WS_DROP:
577 wepent.weapon_switchdelay = wepent.activeweapon.switchdelay_drop;
578 break;
579 default:
580 wepent.weapon_switchdelay = 0;
581 break;
582 }
583 return true;
584}
585#endif
586
587#ifdef SVQC
588void wframe_send(entity actor, entity weaponentity, int wepframe, float attackrate, bool restartanim)
589{
590 if (!IS_REAL_CLIENT(actor)) return;
591 int channel = MSG_ONE;
592 msg_entity = actor;
593 WriteHeader(channel, wframe);
594 WriteByte(channel, wepframe);
595 WriteFloat(channel, attackrate);
596 WriteByte(channel, weaponslot(weaponentity.weaponentity_fld));
597 WriteByte(channel, restartanim);
598 WriteByte(channel, weaponentity.state);
599 WriteFloat(channel, weaponentity.weapon_nextthink);
600}
601#endif
602
603REGISTER_NET_C2S(w_whereis)
604#ifdef SVQC
605void Weapon_whereis(Weapon this, entity cl);
606NET_HANDLE(w_whereis, bool)
607{
609 if (wpn != WEP_Null) Weapon_whereis(wpn, sender);
610 return true;
611}
612#else
613void w_whereis(Weapon this)
614{
615 int channel = MSG_C2S;
616 WriteHeader(channel, w_whereis);
617 WriteRegistered(Weapons, channel, this);
618}
619CLIENT_COMMAND(weapon_find, "Show spawn locations of a weapon")
620{
621 switch (request)
622 {
624 {
625 string s = argv(1);
626 if (s == "all")
627 {
628 FOREACH(Weapons, it != WEP_Null, w_whereis(it));
629 return;
630 }
631 if (s == "unowned")
632 {
633 FOREACH(Weapons, it != WEP_Null && !(STAT(WEAPONS) & it.m_wepset), w_whereis(it));
634 return;
635 }
636 FOREACH(Weapons, it != WEP_Null && it.netname == s,
637 {
638 w_whereis(it);
639 return;
640 });
641 }
642 default:
643 LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0));
645 {
646 LOG_HELP("Usage:^3 cl_cmd weapon_find <weapon>");
647 LOG_HELP(" Where <weapon> is the lowercase weapon name, 'all' or 'unowned'.");
648 return;
649 }
650 }
651}
652#endif
653
654#ifdef SVQC
655void W_MuzzleFlash_Model_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset)
656{
657 flash.owner = actor;
658 flash.angles_z = random() * 360;
659
660 entity view = actor.(weaponentity);
661 entity exterior = actor.exteriorweaponentity;
662
663 if (view.oldorigin.x > 0)
664 {
665 setattachment(flash, exterior, "");
666 setorigin(flash, view.oldorigin + offset);
667 }
668 else
669 {
670 if (gettagindex(exterior, "shot")) setattachment(flash, exterior, "shot");
671 else setattachment(flash, exterior, "tag_shot");
672 setorigin(flash, offset);
673 }
674}
675#elif defined(CSQC)
677{
678 flash.owner = wepent;
679 flash.angles_z = random() * 360;
680
681 if (gettagindex(wepent, "shot")) setattachment(flash, wepent, "shot");
682 else setattachment(flash, wepent, "tag_shot");
683 setorigin(flash, offset);
684}
685#endif
686
688{
689 this.frame += 2;
690 this.scale *= 0.5;
691 this.alpha -= 0.25;
692 this.nextthink = time + 0.05;
693
694 if(this.alpha <= 0)
695 {
696 setthink(this, SUB_Remove);
697 this.nextthink = time;
698 this.realowner.muzzle_flash = NULL;
699 return;
700 }
701}
702
703void W_MuzzleFlash_Model(entity wepent, entity muzzlemodel)
704{
705 if(wepent.muzzle_flash == NULL)
706 wepent.muzzle_flash = spawn();
707
708 entity flash = wepent.muzzle_flash;
709 setmodel(flash, muzzlemodel); // precision set below
710
711 flash.scale = 0.75;
713 flash.nextthink = time + 0.02;
714 flash.frame = 2;
715 flash.alpha = 0.75;
716 flash.angles_z = random() * 180;
717 flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
718 flash.owner = flash.realowner = wepent;
719
720#ifdef CSQC
721 flash.drawmask = MASK_NORMAL;
722#endif
723}
724
725REGISTER_NET_TEMP(w_muzzleflash)
726
727#ifdef SVQC
728void W_MuzzleFlash(Weapon thiswep, entity actor, .entity weaponentity, vector shotorg, vector shotdir)
729{
730 // don't show an exterior muzzle effect for the off-hand
731 if(weaponslot(weaponentity) == 0)
732 {
733 Send_Effect_Except(thiswep.m_muzzleeffect, shotorg, shotdir * 1000, 1, '0 0 0', '0 0 0', actor);
734
735 if(thiswep.m_muzzlemodel != MDL_Null)
736 {
737 W_MuzzleFlash_Model(actor.exteriorweaponentity, thiswep.m_muzzlemodel);
738 W_MuzzleFlash_Model_AttachToShotorg(actor, weaponentity, actor.exteriorweaponentity.muzzle_flash, '5 0 0');
739 }
740 }
741
742 FOREACH_CLIENT(it == actor || (IS_SPEC(it) && it.enemy == actor),
743 {
744 if(!IS_REAL_CLIENT(it))
745 continue;
746 int channel = MSG_ONE;
747 msg_entity = it;
748 WriteHeader(channel, w_muzzleflash);
749 WriteByte(channel, thiswep.m_id);
750 WriteByte(channel, weaponslot(weaponentity));
751 WriteVector(channel, shotorg);
752 });
753}
754#elif defined(CSQC)
755NET_HANDLE(w_muzzleflash, bool isNew)
756{
757 return = true;
758 int weapon_id = ReadByte();
759 int slot = ReadByte();
760 vector sv_shotorg = ReadVector();
761
762 Weapon thiswep = REGISTRY_GET(Weapons, weapon_id);
763 vector viewangles = getpropertyvec(VF_CL_VIEWANGLES);
764 vector forward, right, up;
765 MAKE_VECTORS(viewangles, forward, right, up);
766
768 {
769 // in third person mode, show the muzzle flash from the server side weapon position
770 // we don't have a view model to reference in this case
771 pointparticles(thiswep.m_muzzleeffect, sv_shotorg, forward * 1000, 1);
772 return;
773 }
774 if(!autocvar_r_drawviewmodel) return;
775
776 entity wepent = viewmodels[slot];
777 // get the local player entity to calculate shot origin
779 if(!rlplayer)
780 rlplayer = csqcplayer; // fall back to the global
781
782 vector md = wepent.movedir_aligned;
783 vector dv = forward * md.x + right * -md.y + up * md.z;
784 vector org = rlplayer.origin + rlplayer.view_ofs + dv;
785
786 pointparticles(thiswep.m_muzzleeffect, org, forward * 1000, 1);
787
788 if(thiswep.m_muzzlemodel != MDL_Null)
789 {
790 W_MuzzleFlash_Model(wepent, thiswep.m_muzzlemodel);
791 W_MuzzleFlash_Model_AttachToShotorg(wepent, wepent.muzzle_flash, '5 0 0');
792 }
793}
794#endif
795
796
797#endif
798
799#endif
800
801#ifdef SVQC
808#endif
809
810#ifdef CSQC
811REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share");
812REPLICATE(cvar_cl_accuracy_data_receive, bool, "cl_accuracy_data_receive");
813#endif
814
815#ifdef GAMEQC
816REPLICATE(cvar_cl_gunalign, int, "cl_gunalign");
817REPLICATE(cvar_cl_weapon_switch_reload, bool, "cl_weapon_switch_reload");
818REPLICATE(cvar_cl_weapon_switch_fallback_to_impulse, bool, "cl_weapon_switch_fallback_to_impulse");
819REPLICATE(cvar_cl_weaponimpulsemode, int, "cl_weaponimpulsemode");
820REPLICATE(cvar_cl_weaponpriority, string, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
821REPLICATE(cvar_cl_weaponpriorities[0], string, "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
822REPLICATE(cvar_cl_weaponpriorities[1], string, "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
823REPLICATE(cvar_cl_weaponpriorities[2], string, "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
824REPLICATE(cvar_cl_weaponpriorities[3], string, "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
825REPLICATE(cvar_cl_weaponpriorities[4], string, "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
826REPLICATE(cvar_cl_weaponpriorities[5], string, "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
827REPLICATE(cvar_cl_weaponpriorities[6], string, "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
828REPLICATE(cvar_cl_weaponpriorities[7], string, "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
829REPLICATE(cvar_cl_weaponpriorities[8], string, "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
830REPLICATE(cvar_cl_weaponpriorities[9], string, "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
831#endif
832
833
834#ifdef MENUQC
835// Finds keybinds for a weapon
837{
838 string command, key, keys = "";
839 int n, j;
840 float k;
841 const bool joy_active = cvar("joy_active");
842
843 for (int cmd_type = 0; cmd_type < 2; ++cmd_type)
844 {
845 command = cmd_type
846 ? strcat("weapon_", wep.netname)
847 : strcat("weapon_group_", itos(wep.impulse));
848 n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings
849
850 for (j = 0; j < n; ++j)
851 {
852 k = stof(argv(j));
853 if (k == -1)
854 continue;
855 key = keynumtostring(k);
856 if (!joy_active && startsWith(key, "JOY"))
857 continue;
858
859 key = translate_key(key);
860 if (keys == "")
861 keys = strcat("^3", key);
862 else
863 keys = strcat(keys, "^7, ^3", key);
864 }
865 }
866
867 if (keys == "")
868 return _("This weapon has not been bound directly."); // code may miss some weird bind configs, so say "directly"
869 return sprintf(_("You have bound this weapon to: %s."), strcat(keys, "^7"));
870}
871
872
873#define COLORED_DAMAGE(dmg) strcat("^9", dmg, "^7")
874
875#define DPS_ONE(mode) sprintf(_("(DPS: %.2f)"), mode##rft <= 0 ? 1337 : stof(mode##dmg_s) / mode##rft)
876#define DPS_MUL(mode) sprintf(_("(DPS: %.2f)"), mode##rft <= 0 ? 1337 : stof(mode##dmg_s) / mode##rft * stof(mode##shots_s))
877
878#define pri_str _("primary")
879#define sec_str _("secondary")
880#define _IT_DOES_MSG() strcat("\n* ", sprintf(_("It does %s damage"), COLORED_DAMAGE(dmg_s)), " ", DPS_ONE())
881#define _IT_SHOOTS_MSG() strcat("\n* ", sprintf(_("It shoots %s shots doing %s damage each"), shots_s, COLORED_DAMAGE(dmg_s)), " ", DPS_MUL())
882#define _DOES_MSG(mode) strcat("\n* ", sprintf(_("The %s does %s damage"), mode##str, COLORED_DAMAGE(mode##dmg_s)), " ", DPS_ONE(mode))
883#define _DOES_PERSEC_MSG(mode) strcat("\n* ", sprintf(_("The %s does %s damage per second"), mode##str, COLORED_DAMAGE(mode##dmg_s)))
884#define _SHOOTS_MSG(mode) strcat("\n* ", sprintf(_("The %s shoots %s shots doing %s damage each"), mode##str, mode##shots_s, COLORED_DAMAGE(mode##dmg_s)), " ", DPS_MUL(mode))
885#define _SHOOTS_UPTO_MSG(mode) strcat("\n* ", sprintf(_("The %s shoots up to %s shots doing %s damage each"), mode##str, mode##shots_s, COLORED_DAMAGE(mode##dmg_s)), " ", DPS_MUL(mode))
886
887// We could use the *_MSG macros directly but we use these because they are more intuitive and readable
888#define IT_DOES() _IT_DOES_MSG()
889#define IT_SHOOTS() _IT_SHOOTS_MSG()
890#define THE_PRIMARY_DOES() _DOES_MSG(pri_)
891#define THE_PRIMARY_SHOOTS() _SHOOTS_MSG(pri_)
892#define THE_PRIMARY_SHOOTS_UPTO() _SHOOTS_UPTO_MSG(pri_)
893#define THE_PRIMARY_DOES_PERSEC() _DOES_PERSEC_MSG(pri_)
894#define THE_SECONDARY_DOES() _DOES_MSG(sec_)
895#define THE_SECONDARY_SHOOTS() _SHOOTS_MSG(sec_)
896#define THE_SECONDARY_SHOOTS_UPTO() _SHOOTS_UPTO_MSG(sec_)
897#define THE_SECONDARY_DOES_PERSEC() _DOES_PERSEC_MSG(sec_)
898
899// One function for each translatable string
900
902{
903 const string wepbal_s = strcat("g_balance_", name);
904 const string dmg_s = cvar_defstring(strcat(wepbal_s, "_damage"));
905 const float rft = stof(cvar_defstring(strcat(wepbal_s, "_refire")));
906 const string dmg2_s = cvar_defstring(strcat(wepbal_s, "_damage2"));
907 const string dir_dps = sprintf(_("(DPS: %.2f)"), rft <= 0 ? 1337 : stof(dmg2_s) / rft);
908 return strcat(_("Default damage stats:"),
909 IT_DOES(),
910 strcat("\n* ", sprintf(_("A direct hit does %s damage"), COLORED_DAMAGE(dmg2_s)), " ", dir_dps)
911 );
912}
914{
915 const string wepbal_s = strcat("g_balance_", name);
916 const string dmg_s = cvar_defstring(strcat(wepbal_s, "_damage"));
917 const float rft = stof(cvar_defstring(strcat(wepbal_s, "_refire")));
918 return strcat(_("Default damage stats:"),
919 IT_DOES()
920 );
921}
922string W_Guide_DPS_onlyOne_andDirectHit(string name, string fire)
923{
925}
926string W_Guide_DPS_onlyOne(string name, string fire)
927{
928 return W_Guide_DPS_onlyOne_unnamed(strcat(name, "_", fire));
929}
930string W_Guide_DPS(string name, string pri, string sec)
931{
932 // TODO: rename all wep primary cvars to g_balance_*_primary_*, vice versa for secondary.
933 // Example exception: g_balance_arc_beam_* (primary)
934 const string wepbal_s = strcat("g_balance_", name, "_");
935 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
936 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
937 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
938 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
939 return strcat(_("Default damage stats:"),
942 );
943}
944string W_Guide_DPS_bothMultishot(string name, string pri, string sec)
945{
946 const string wepbal_s = strcat("g_balance_", name, "_");
947 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
948 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
949 const string pri_shots_s = cvar_defstring(strcat(wepbal_s, pri, "_shots"));
950 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
951 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
952 const string sec_shots_s = cvar_defstring(strcat(wepbal_s, sec, "_shots"));
953 return strcat(_("Default damage stats:"),
956 );
957}
958string W_Guide_DPS_onlySecondary(string name, string sec)
959{
960 const string wepbal_s = strcat("g_balance_", name, "_");
961 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
962 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
963 return strcat(_("Default damage stats:"),
965 );
966}
967string W_Guide_DPS_secondaryMultishot(string name, string pri, string sec, string shots, string refire2, bool sec_variable)
968{
969 const string wepbal_s = strcat("g_balance_", name, "_");
970 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
971 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
972 string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
973 float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
974 const string sec_shots_s = cvar_defstring(strcat(wepbal_s, shots));
975 if (sec_dmg_s == "")
976 sec_dmg_s = pri_dmg_s;
977 const float num_shots = stof(sec_shots_s);
978 if (refire2 != "")
979 sec_rft = (num_shots - 1) * sec_rft + stof(cvar_defstring(strcat(wepbal_s, refire2)));
980 return strcat(_("Default damage stats:"),
983 );
984}
985string W_Guide_DPS_primaryMultishot(string name, string pri, string sec, string shots, string refire2)
986{
987 const string wepbal_s = strcat("g_balance_", name, "_");
988 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
989 float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
990 const string pri_shots_s = cvar_defstring(strcat(wepbal_s, shots));
991 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
992 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
993 const float num_shots = stof(pri_shots_s);
994 if (refire2 != "")
995 pri_rft = (num_shots - 1) * pri_rft + stof(cvar_defstring(strcat(wepbal_s, refire2)));
996 return strcat(_("Default damage stats:"),
999 );
1000}
1001string W_Guide_DPS_onlyOneMultishot(string name, string fire, string shots, string refire2)
1002{
1003 const string wepbal_s = strcat("g_balance_", name, "_");
1004 const string dmg_s = cvar_defstring(strcat(wepbal_s, fire, "_damage"));
1005 float rft = stof(cvar_defstring(strcat(wepbal_s, fire, "_refire")));
1006 const string shots_s = cvar_defstring(strcat(wepbal_s, shots));
1007 const float num_shots = stof(shots_s);
1008 if (refire2 != "")
1009 rft = (num_shots - 1) * rft + stof(cvar_defstring(strcat(wepbal_s, refire2)));
1010 return strcat(_("Default damage stats:"),
1011 IT_SHOOTS()
1012 );
1013}
1014string W_Guide_DPS_secondaryMultishotWithCombo(string name, string pri, string sec, string shots, string refire2, string combo, bool sec_variable)
1015{
1016 const string wepbal_s = strcat("g_balance_", name, "_");
1017 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage"));
1018 const float pri_rft = stof(cvar_defstring(strcat(wepbal_s, pri, "_refire")));
1019 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
1020 float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
1021 const string sec_shots_s = cvar_defstring(strcat(wepbal_s, shots));
1022 const string cmb_dmg_s = cvar_defstring(strcat(wepbal_s, combo, "_damage"));
1023 const float num_shots = stof(sec_shots_s);
1024 float combo_t = max(pri_rft, sec_rft);
1025 if (refire2 != "")
1026 {
1027 const float sec_rft2 = sec_rft;
1028 sec_rft = stof(cvar_defstring(strcat(wepbal_s, refire2)));
1029 combo_t = max(combo_t, num_shots * sec_rft + sec_rft2);
1030 sec_rft = (num_shots - 1) * sec_rft + sec_rft2;
1031 }
1032 const string DPS_combo = sprintf(_("(total DPS: %.2f)"),
1033 combo_t <= 0 ? 1337 : (stof(pri_dmg_s) + stof(sec_dmg_s) * num_shots + stof(cmb_dmg_s)) / combo_t);
1034 return strcat(_("Default damage stats:"),
1036 (sec_variable ? THE_SECONDARY_SHOOTS_UPTO() : THE_SECONDARY_SHOOTS()),
1037 strcat("\n* ", sprintf(_("The combo adds an extra %s damage"), COLORED_DAMAGE(cmb_dmg_s)), " ", DPS_combo)
1038 );
1039}
1040string W_Guide_DPS_primaryDPS(string name, string pri, string sec)
1041{
1042 const string wepbal_s = strcat("g_balance_", name, "_");
1043 const string pri_dmg_s = cvar_defstring(strcat(wepbal_s, pri, "_damage")); // actually damage per second
1044 const string sec_dmg_s = cvar_defstring(strcat(wepbal_s, sec, "_damage"));
1045 const float sec_rft = stof(cvar_defstring(strcat(wepbal_s, sec, "_refire")));
1046 return strcat(_("Default damage stats:"),
1049 );
1050}
1051#endif
vector shotorg
Definition aim.qh:10
vector shotdir
Definition aim.qh:11
void anim_set(entity e, vector anim, bool looping, bool override, bool restart)
Definition anim.qc:6
#define setanim(...)
Definition anim.qh:45
float frame
primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
Definition anim.qh:6
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
int W_GunAlign(entity this, int preferred_align)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define CLIENT_COMMAND(id, description)
Definition cl_cmd.qh:16
entity csqcplayer
Definition cl_player.qh:26
Definition ammo.qh:24
int m_id
Definition item.qh:127
string netname
Definition resources.qh:27
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:44
int impulse
M: impulse : weapon impulse.
Definition weapon.qh:53
string netname
M: refname : reference name name.
Definition weapon.qh:79
entity m_muzzlemodel
M: flash model MDL_id_MUZZLEFLASH.
Definition weapon.qh:66
float alpha
Definition items.qc:13
entity owner
Definition main.qh:87
float renderflags
Definition main.qh:111
const int CMD_REQUEST_COMMAND
Definition command.qh:3
const int CMD_REQUEST_USAGE
Definition command.qh:4
#define setmodel(this, m)
Definition model.qh:26
#define M_ARGV(x, type)
Definition events.qh:17
string fixPriorityList(string order, float from, float to, float subtract, float complete)
Definition util.qc:600
float compressShotOrigin(vector v)
Definition util.qc:1248
string translate_key(string key)
Definition util.qc:1512
string mapPriorityList(string order, string(string) mapfunc)
Definition util.qc:642
vector decompressShotOrigin(int f)
Definition util.qc:1275
const float VF_CL_VIEWANGLES
const float STAT_CELLS
const float STAT_ROCKETS
const float MASK_NORMAL
vector mins
const float STAT_SHELLS
const float EF_ADDITIVE
const float EF_FULLBRIGHT
const float RF_VIEWMODEL
float time
const float STAT_NAILS
vector maxs
float nextthink
float player_localentnum
vector oldorigin
#define spawn
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:13
#define MAKE_VECTORS(angles, forward, right, up)
Same as the makevectors builtin but uses the provided locals instead of the v_* globals.
entity viewmodelforclient
#define strstrofs
#define gettagindex
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
#define pass(name, colormin, colormax)
void Send_Effect_Except(entity eff, vector eff_loc, vector eff_vel, int eff_cnt, vector eff_col_min, vector eff_col_max, entity ignore)
Definition all.qc:105
ent angles
Definition ent_cs.qc:121
model
Definition ent_cs.qc:139
entity CSQCModel_server2csqc(int i)
Definition cl_model.qc:314
Weapons
Definition guide.qh:113
#define itos(i)
Definition int.qh:6
Header file that describes the functions related to game items.
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define ReadFloat()
Definition net.qh:366
const int MSG_C2S
Definition net.qh:199
#define NET_HANDLE(id, param)
Definition net.qh:15
#define ReadVector()
Definition net.qh:367
int ReadInt24_t()
Definition net.qh:355
#define ReadRegistered(r)
Definition net.qh:298
#define ReadInt48_t()
Definition net.qh:361
#define ReadInt72_t()
Definition net.qh:362
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_C2S(id)
Definition net.qh:88
int ReadByte()
#define WriteRegistered(r, to, it)
Definition net.qh:299
#define REGISTER_NET_TEMP(id)
Definition net.qh:28
#define REPLICATE(...)
Replicates a client cvar into a server field.
Definition replicate.qh:23
#define STAT(...)
Definition stats.qh:82
#define LOG_WARNF(...)
Definition log.qh:62
#define LOG_HELP(...)
Definition log.qh:85
#define LOG_INFOF(...)
Definition log.qh:66
vector movedir
Definition viewloc.qh:18
string name
Definition menu.qh:30
float MSG_ONE
Definition menudefs.qc:56
float stof(string val,...)
string substring(string s, float start, float length)
float cvar(string name)
string keynumtostring(float keynum)
vector stov(string s)
float random(void)
string ftos(float f)
void WriteByte(float data, float dest, float desto)
const string cvar_defstring(string name)
float tokenize(string s)
string argv(float n)
float max(float f,...)
vector animfixfps(entity e, vector a, vector b)
Definition util.qc:1808
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
#define gettaginfo
Definition post.qh:32
entity msg_entity
Definition progsdefs.qc:63
vector view_ofs
Definition progsdefs.qc:151
float scale
Definition projectile.qc:14
entity result
Definition promise.qc:43
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
#define RandomSelection_AddEnt(e, weight, priority)
Definition random.qh:14
entity RandomSelection_chosen_ent
Definition random.qh:5
#define REGISTRY_GET(id, i)
Definition registry.qh:43
#define REGISTRY_MAX(id)
Definition registry.qh:17
#define setthink(e, f)
vector
Definition self.qh:92
vector org
Definition self.qh:92
string weaponorder_byimpulse
Definition client.qh:62
ERASEABLE void heapsort(int n, swapfunc_t swap, comparefunc_t cmp, entity pass)
Definition sort.qh:9
#define CS_CVAR(this)
Definition state.qh:51
#define startsWith(haystack, needle)
Definition string.qh:236
#define strcpy(this, s)
Definition string.qh:52
entity realowner
#define IS_SPEC(v)
Definition utils.qh:10
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50
int autocvar_chase_active
Definition view.qh:17
bool autocvar_r_drawviewmodel
Definition view.qh:97
entity viewmodels[MAX_WEAPONSLOTS]
Definition view.qh:108
string W_FixWeaponOrder_ForceComplete(string order)
Definition all.qc:166
string W_FixWeaponOrder_AllowIncomplete(entity this, string order)
Definition all.qc:161
vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
Definition all.qc:240
void wframe_send(entity actor, entity weaponentity, int wepframe, float attackrate, bool restartanim)
Definition all.qc:588
string W_Model(string w_mdl)
Definition all.qc:232
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:836
#define COLORED_DAMAGE(dmg)
Definition all.qc:873
string W_Guide_DPS_primaryMultishot(string name, string pri, string sec, string shots, string refire2)
Definition all.qc:985
void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
supported formats:
Definition all.qc:347
WepSet WepSet_GetFromStat_InMap()
Definition all.qc:83
string W_Guide_DPS_secondaryMultishot(string name, string pri, string sec, string shots, string refire2, bool sec_variable)
Definition all.qc:967
void WriteWepSet(float dst, WepSet w)
Definition all.qc:71
int GetAmmoStat(Resource ammotype)
Definition all.qc:210
void W_FixWeaponOrder_BuildImpulseList_swap(int i, int j, entity pass)
Definition all.qc:127
entity GetAmmoResource(Ammo ammotype)
Definition all.qc:199
float W_FixWeaponOrder_BuildImpulseList_buf[REGISTRY_MAX(Weapons)]
Definition all.qc:125
void W_MuzzleFlash_Model_Think(entity this)
Definition all.qc:687
float W_FixWeaponOrder_BuildImpulseList_cmp(int i, int j, entity pass)
Definition all.qc:134
string W_NumberWeaponOrder(string order)
Definition all.qc:120
string W_FixWeaponOrder_BuildImpulseList(string o)
Definition all.qc:146
string W_Guide_DPS(string name, string pri, string sec)
Definition all.qc:930
#define THE_SECONDARY_DOES()
Definition all.qc:894
string W_NumberWeaponOrder_MapFunc(string s)
Definition all.qc:114
string W_Sound(string w_snd)
Definition all.qc:225
string W_Guide_DPS_bothMultishot(string name, string pri, string sec)
Definition all.qc:944
string W_NameWeaponOrder(string order)
Definition all.qc:110
string W_Guide_DPS_onlySecondary(string name, string sec)
Definition all.qc:958
void Weapon_whereis(Weapon this, entity cl)
Definition selection.qc:26
void W_MuzzleFlash_Model(entity wepent, entity muzzlemodel)
Definition all.qc:703
string W_Guide_DPS_onlyOne_andDirectHit(string name, string fire)
Definition all.qc:922
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:728
#define THE_SECONDARY_SHOOTS_UPTO()
Definition all.qc:896
string W_NameWeaponOrder_MapFunc(string s)
Definition all.qc:99
string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(entity this, string wo)
Definition all.qc:802
string W_Guide_DPS_secondaryMultishotWithCombo(string name, string pri, string sec, string shots, string refire2, string combo, bool sec_variable)
Definition all.qc:1014
#define THE_PRIMARY_SHOOTS()
Definition all.qc:891
#define IT_DOES()
Definition all.qc:888
WepSet WepSet_GetFromStat()
Definition all.qc:79
string W_Guide_DPS_onlyOneMultishot(string name, string fire, string shots, string refire2)
Definition all.qc:1001
WepSet _WepSet_FromWeapon(int a)
Definition all.qc:53
#define THE_PRIMARY_DOES_PERSEC()
Definition all.qc:893
string W_Guide_DPS_onlyOne_unnamed(string name)
Definition all.qc:913
void W_MuzzleFlash_Model_AttachToShotorg(entity actor,.entity weaponentity, entity flash, vector offset)
Definition all.qc:655
string W_FixWeaponOrder(string order, float complete)
Definition all.qc:95
string W_Guide_DPS_onlyOne(string name, string fire)
Definition all.qc:926
string W_Guide_DPS_primaryDPS(string name, string pri, string sec)
Definition all.qc:1040
string W_FixWeaponOrder_BuildImpulseList_order
Definition all.qc:126
#define IT_SHOOTS()
Definition all.qc:889
entity GetAmmoItem(Resource ammotype)
Definition all.qc:189
#define THE_SECONDARY_SHOOTS()
Definition all.qc:895
WepSet ReadWepSet()
Definition all.qc:87
vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
Definition all.qc:262
string W_Guide_DPS_onlyOne_unnamed_andDirectHit(string name)
Definition all.qc:901
WepSet W_RandomWeapons(entity e, WepSet remaining, int n)
Definition all.qc:172
#define THE_PRIMARY_DOES()
Definition all.qc:890
int m_gunalign
Definition all.qh:385
#define G_SHOOTFROMFIXEDORIGIN
Definition all.qh:418
vector movedir_aligned
Definition all.qh:394
vector anim_reload
Definition all.qh:400
#define WEP_LAST
Definition all.qh:327
vector anim_fire2
Definition all.qh:398
vector anim_idle
Definition all.qh:399
const int WEP_FIRST
Definition all.qh:326
entity weaponchild
Definition all.qh:376
vector anim_fire1
Definition all.qh:397
WFRAME wframe
Definition all.qh:414
#define WEP_IMPULSE_BEGIN
Definition all.qh:334
vector spawnorigin
Definition all.qh:391
#define WepSet_FromWeapon(it)
Definition all.qh:46
const int WS_RAISE
raise frame
Definition weapon.qh:35
vector WepSet
Definition weapon.qh:14
int weaponslot(.entity weaponentity)
Definition weapon.qh:19
const int WS_DROP
deselecting frame
Definition weapon.qh:37