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