1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
9 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
11 void crosshair_trace_plusvisibletriggers(entity pl)
15 first = findchainfloat(solid, SOLID_TRIGGER);
17 for (e = first; e; e = e.chain)
23 for (e = first; e; e = e.chain)
24 e.solid = SOLID_TRIGGER;
26 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
27 void WarpZone_crosshair_trace(entity pl)
29 WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
32 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
33 void() spawnpoint_use;
35 string ColoredTeamName(float t);
37 string admin_name(void)
39 if(autocvar_sv_adminnick != "")
40 return autocvar_sv_adminnick;
42 return "SERVER ADMIN";
45 float DistributeEvenly_amount;
46 float DistributeEvenly_totalweight;
47 void DistributeEvenly_Init(float amount, float totalweight)
49 if (DistributeEvenly_amount)
51 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
52 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
55 DistributeEvenly_amount = 0;
57 DistributeEvenly_amount = amount;
58 DistributeEvenly_totalweight = totalweight;
60 float DistributeEvenly_Get(float weight)
65 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
66 DistributeEvenly_totalweight -= weight;
67 DistributeEvenly_amount -= f;
70 float DistributeEvenly_GetRandomized(float weight)
75 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
76 DistributeEvenly_totalweight -= weight;
77 DistributeEvenly_amount -= f;
81 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
84 string STR_PLAYER = "player";
85 string STR_SPECTATOR = "spectator";
86 string STR_OBSERVER = "observer";
89 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
90 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
91 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
92 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
94 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
95 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
96 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
97 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
98 #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if(v.classname != STR_PLAYER)
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
100 #define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; )
103 #define CENTER_OR_VIEWOFS(ent) (ent.origin + ((ent.classname == STR_PLAYER) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
105 // copies a string to a tempstring (so one can strunzone it)
106 string strcat1(string s) = #115; // FRIK_FILE
111 void bcenterprint(string s)
113 // TODO replace by MSG_ALL (would show it to spectators too, though)?
115 FOR_EACH_PLAYER(head)
116 if (clienttype(head) == CLIENTTYPE_REAL)
117 centerprint(head, s);
120 void GameLogEcho(string s)
125 if (autocvar_sv_eventlog_files)
130 matches = autocvar_sv_eventlog_files_counter + 1;
131 cvar_set("sv_eventlog_files_counter", ftos(matches));
134 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
135 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
136 logfile = fopen(fn, FILE_APPEND);
137 fputs(logfile, ":logversion:3\n");
141 if (autocvar_sv_eventlog_files_timestamps)
142 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
144 fputs(logfile, strcat(s, "\n"));
147 if (autocvar_sv_eventlog_console)
156 // will be opened later
161 if (logfile_open && logfile >= 0)
168 float spawnpoint_nag;
169 void relocate_spawnpoint()
171 // nudge off the floor
172 setorigin(self, self.origin + '0 0 1');
174 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
175 if (trace_startsolid)
181 if (!move_out_of_solid(self))
182 objerror("could not get out of solid at all!");
183 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
184 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
185 print(" ", ftos(self.origin_y - o_y));
186 print(" ", ftos(self.origin_z - o_z), "'\n");
187 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
190 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
196 self.mins = self.maxs = '0 0 0';
197 objerror("player spawn point in solid, mapper sucks!\n");
202 self.use = spawnpoint_use;
203 self.team_saved = self.team;
207 if (have_team_spawns != 0)
209 have_team_spawns = 1;
210 have_team_spawns_forteam[self.team] = 1;
212 if (autocvar_r_showbboxes)
214 // show where spawnpoints point at too
215 makevectors(self.angles);
218 e.classname = "info_player_foo";
219 setorigin(e, self.origin + v_forward * 24);
220 setsize(e, '-8 -8 -8', '8 8 8');
221 e.solid = SOLID_TRIGGER;
225 #define strstr strstrofs
227 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
228 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
229 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
230 // BE CONSTANT OR strzoneD!
231 float strstr(string haystack, string needle, float offset)
235 len = strlen(needle);
236 endpos = strlen(haystack) - len;
237 while(offset <= endpos)
239 found = substring(haystack, offset, len);
248 float NUM_NEAREST_ENTITIES = 4;
249 entity nearest_entity[NUM_NEAREST_ENTITIES];
250 float nearest_length[NUM_NEAREST_ENTITIES];
251 entity findnearest(vector point, .string field, string value, vector axismod)
262 localhead = find(world, field, value);
265 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
266 dist = localhead.oldorigin;
268 dist = localhead.origin;
270 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
273 for (i = 0; i < num_nearest; ++i)
275 if (len < nearest_length[i])
279 // now i tells us where to insert at
280 // INSERTION SORT! YOU'VE SEEN IT! RUN!
281 if (i < NUM_NEAREST_ENTITIES)
283 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
285 nearest_length[j + 1] = nearest_length[j];
286 nearest_entity[j + 1] = nearest_entity[j];
288 nearest_length[i] = len;
289 nearest_entity[i] = localhead;
290 if (num_nearest < NUM_NEAREST_ENTITIES)
291 num_nearest = num_nearest + 1;
294 localhead = find(localhead, field, value);
297 // now use the first one from our list that we can see
298 for (i = 0; i < num_nearest; ++i)
300 traceline(point, nearest_entity[i].origin, TRUE, world);
301 if (trace_fraction == 1)
305 dprint("Nearest point (");
306 dprint(nearest_entity[0].netname);
307 dprint(") is not visible, using a visible one.\n");
309 return nearest_entity[i];
313 if (num_nearest == 0)
316 dprint("Not seeing any location point, using nearest as fallback.\n");
318 dprint("Candidates were: ");
319 for(j = 0; j < num_nearest; ++j)
323 dprint(nearest_entity[j].netname);
328 return nearest_entity[0];
331 void spawnfunc_target_location()
333 self.classname = "target_location";
334 // location name in netname
335 // eventually support: count, teamgame selectors, line of sight?
338 void spawnfunc_info_location()
340 self.classname = "target_location";
341 self.message = self.netname;
344 string NearestLocation(vector p)
349 loc = findnearest(p, classname, "target_location", '1 1 1');
356 loc = findnearest(p, target, "###item###", '1 1 4');
363 string formatmessage(string msg)
374 WarpZone_crosshair_trace(self);
375 cursor = trace_endpos;
376 cursor_ent = trace_ent;
380 break; // too many replacements
383 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
384 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
397 replacement = substring(msg, p, 2);
398 escape = substring(msg, p + 1, 1);
402 else if (escape == "\\")
404 else if (escape == "n")
406 else if (escape == "a")
407 replacement = ftos(floor(self.armorvalue));
408 else if (escape == "h")
409 replacement = ftos(floor(self.health));
410 else if (escape == "l")
411 replacement = NearestLocation(self.origin);
412 else if (escape == "y")
413 replacement = NearestLocation(cursor);
414 else if (escape == "d")
415 replacement = NearestLocation(self.death_origin);
416 else if (escape == "w") {
420 wep = self.switchweapon;
423 replacement = W_Name(wep);
424 } else if (escape == "W") {
425 if (self.items & IT_SHELLS) replacement = "shells";
426 else if (self.items & IT_NAILS) replacement = "bullets";
427 else if (self.items & IT_ROCKETS) replacement = "rockets";
428 else if (self.items & IT_CELLS) replacement = "cells";
429 else replacement = "batteries"; // ;)
430 } else if (escape == "x") {
431 replacement = cursor_ent.netname;
432 if (replacement == "" || !cursor_ent)
433 replacement = "nothing";
434 } else if (escape == "s")
435 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
436 else if (escape == "S")
437 replacement = ftos(vlen(self.velocity));
439 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
440 p = p + strlen(replacement);
445 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
446 return (value == 0) ? FALSE : TRUE;
455 >0: receives a cvar from name=argv(f) value=argv(f+1)
457 void GetCvars_handleString(string thisname, float f, .string field, string name)
462 strunzone(self.field);
463 self.field = string_null;
467 if (thisname == name)
470 strunzone(self.field);
471 self.field = strzone(argv(f + 1));
475 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
477 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
479 GetCvars_handleString(thisname, f, field, name);
480 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
481 if (thisname == name)
484 s = func(strcat1(self.field));
487 strunzone(self.field);
488 self.field = strzone(s);
492 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
499 if (thisname == name)
500 self.field = stof(argv(f + 1));
503 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
505 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
512 if (thisname == name)
516 self.field = stof(argv(f + 1));
525 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
528 float w_getbestweapon(entity e);
529 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
532 o = W_FixWeaponOrder_ForceComplete(wo);
533 if(self.weaponorder_byimpulse)
535 strunzone(self.weaponorder_byimpulse);
536 self.weaponorder_byimpulse = string_null;
538 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
541 void GetCvars(float f)
543 string s = string_null;
546 s = strcat1(argv(f));
550 MUTATOR_CALLHOOK(GetCvars);
551 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
552 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
553 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
554 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
555 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
556 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
557 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
558 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
559 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
560 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
561 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
562 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
563 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
564 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
565 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
566 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
567 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
568 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
569 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
570 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
571 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
572 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
573 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
575 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
576 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
578 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
579 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
580 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
581 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
582 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
584 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
587 if (s == "cl_weaponpriority")
588 self.switchweapon = w_getbestweapon(self);
589 if (s == "cl_allow_uidtracking")
590 PlayerStats_AddPlayer(self);
594 void backtrace(string msg)
597 dev = autocvar_developer;
598 war = autocvar_prvm_backtraceforwarnings;
599 cvar_set("developer", "1");
600 cvar_set("prvm_backtraceforwarnings", "1");
602 print("--- CUT HERE ---\nWARNING: ");
605 remove(world); // isn't there any better way to cause a backtrace?
606 print("\n--- CUT UNTIL HERE ---\n");
607 cvar_set("developer", ftos(dev));
608 cvar_set("prvm_backtraceforwarnings", ftos(war));
611 string Team_ColorCode(float teamid)
613 if (teamid == COLOR_TEAM1)
615 else if (teamid == COLOR_TEAM2)
617 else if (teamid == COLOR_TEAM3)
619 else if (teamid == COLOR_TEAM4)
625 string Team_ColorName(float t)
627 // fixme: Search for team entities and get their .netname's!
628 if (t == COLOR_TEAM1)
630 if (t == COLOR_TEAM2)
632 if (t == COLOR_TEAM3)
634 if (t == COLOR_TEAM4)
639 string Team_ColorNameLowerCase(float t)
641 // fixme: Search for team entities and get their .netname's!
642 if (t == COLOR_TEAM1)
644 if (t == COLOR_TEAM2)
646 if (t == COLOR_TEAM3)
648 if (t == COLOR_TEAM4)
653 float ColourToNumber(string team_colour)
655 if (team_colour == "red")
658 if (team_colour == "blue")
661 if (team_colour == "yellow")
664 if (team_colour == "pink")
667 if (team_colour == "auto")
673 float NumberToTeamNumber(float number)
690 // decolorizes and team colors the player name when needed
691 string playername(entity p)
694 if (teamplay && !intermission_running && p.classname == "player")
696 t = Team_ColorCode(p.team);
697 return strcat(t, strdecolorize(p.netname));
703 vector randompos(vector m1, vector m2)
707 v_x = m2_x * random() + m1_x;
708 v_y = m2_y * random() + m1_y;
709 v_z = m2_z * random() + m1_z;
713 //#NO AUTOCVARS START
715 float g_pickup_shells;
716 float g_pickup_shells_max;
717 float g_pickup_nails;
718 float g_pickup_nails_max;
719 float g_pickup_rockets;
720 float g_pickup_rockets_max;
721 float g_pickup_cells;
722 float g_pickup_cells_max;
724 float g_pickup_fuel_jetpack;
725 float g_pickup_fuel_max;
726 float g_pickup_armorsmall;
727 float g_pickup_armorsmall_max;
728 float g_pickup_armorsmall_anyway;
729 float g_pickup_armormedium;
730 float g_pickup_armormedium_max;
731 float g_pickup_armormedium_anyway;
732 float g_pickup_armorbig;
733 float g_pickup_armorbig_max;
734 float g_pickup_armorbig_anyway;
735 float g_pickup_armorlarge;
736 float g_pickup_armorlarge_max;
737 float g_pickup_armorlarge_anyway;
738 float g_pickup_healthsmall;
739 float g_pickup_healthsmall_max;
740 float g_pickup_healthsmall_anyway;
741 float g_pickup_healthmedium;
742 float g_pickup_healthmedium_max;
743 float g_pickup_healthmedium_anyway;
744 float g_pickup_healthlarge;
745 float g_pickup_healthlarge_max;
746 float g_pickup_healthlarge_anyway;
747 float g_pickup_healthmega;
748 float g_pickup_healthmega_max;
749 float g_pickup_healthmega_anyway;
750 float g_pickup_ammo_anyway;
751 float g_pickup_weapons_anyway;
753 WEPSET_DECLARE_A(g_weaponarena_weapons);
754 float g_weaponarena_random;
755 float g_weaponarena_random_with_laser;
756 string g_weaponarena_list;
757 float g_weaponspeedfactor;
758 float g_weaponratefactor;
759 float g_weapondamagefactor;
760 float g_weaponforcefactor;
761 float g_weaponspreadfactor;
763 WEPSET_DECLARE_A(start_weapons);
764 WEPSET_DECLARE_A(start_weapons_default);
765 WEPSET_DECLARE_A(start_weapons_defaultmask);
767 float start_ammo_shells;
768 float start_ammo_nails;
769 float start_ammo_rockets;
770 float start_ammo_cells;
771 float start_ammo_fuel;
773 float start_armorvalue;
774 WEPSET_DECLARE_A(warmup_start_weapons);
775 WEPSET_DECLARE_A(warmup_start_weapons_default);
776 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
777 float warmup_start_ammo_shells;
778 float warmup_start_ammo_nails;
779 float warmup_start_ammo_rockets;
780 float warmup_start_ammo_cells;
781 float warmup_start_ammo_fuel;
782 float warmup_start_health;
783 float warmup_start_armorvalue;
786 entity get_weaponinfo(float w);
788 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
790 var float i = weaponinfo.weapon;
796 if (g_lms || g_ca || allguns)
798 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
804 d = (i == WEP_SHOTGUN);
806 d = 0; // weapon is set a few lines later
808 d = (i == WEP_LASER || i == WEP_SHOTGUN);
810 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
811 d |= (i == WEP_HOOK);
812 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
815 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
817 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
822 // 4: is set by default?
831 void readplayerstartcvars()
837 // initialize starting values for players
838 WEPSET_CLEAR_A(start_weapons);
839 WEPSET_CLEAR_A(start_weapons_default);
840 WEPSET_CLEAR_A(start_weapons_defaultmask);
842 start_ammo_shells = 0;
843 start_ammo_nails = 0;
844 start_ammo_rockets = 0;
845 start_ammo_cells = 0;
846 start_health = cvar("g_balance_health_start");
847 start_armorvalue = cvar("g_balance_armor_start");
850 WEPSET_CLEAR_A(g_weaponarena_weapons);
852 s = cvar_string("g_weaponarena");
853 if (s == "0" || s == "")
859 if (s == "0" || s == "")
865 // forcibly turn off weaponarena
870 g_weaponarena_list = "All Weapons";
871 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
873 e = get_weaponinfo(j);
874 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
875 WEPSET_OR_AW(g_weaponarena_weapons, j);
878 else if (s == "most")
881 g_weaponarena_list = "Most Weapons";
882 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
884 e = get_weaponinfo(j);
885 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
886 if (e.spawnflags & WEP_FLAG_NORMAL)
887 WEPSET_OR_AW(g_weaponarena_weapons, j);
890 else if (s == "none")
893 g_weaponarena_list = "No Weapons";
898 t = tokenize_console(s);
899 g_weaponarena_list = "";
900 for (i = 0; i < t; ++i)
903 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
905 e = get_weaponinfo(j);
908 WEPSET_OR_AW(g_weaponarena_weapons, j);
909 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
915 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
918 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
922 g_weaponarena_random = cvar("g_weaponarena_random");
924 g_weaponarena_random = 0;
925 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
929 g_minstagib = 0; // incompatible
930 g_pinata = 0; // incompatible
931 g_weapon_stay = 0; // incompatible
932 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
934 start_items |= IT_UNLIMITED_AMMO;
936 else if (g_minstagib)
938 g_pinata = 0; // incompatible
939 g_weapon_stay = 0; // incompatible
940 g_bloodloss = 0; // incompatible
942 start_armorvalue = 0;
943 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
944 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
945 start_items |= IT_UNLIMITED_SUPERWEAPONS;
947 if (g_minstagib_invis_alpha <= 0)
948 g_minstagib_invis_alpha = -1;
952 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
954 e = get_weaponinfo(i);
955 float w = want_weapon("g_start_weapon_", e, FALSE);
957 WEPSET_OR_AW(start_weapons, i);
959 WEPSET_OR_AW(start_weapons_default, i);
961 WEPSET_OR_AW(start_weapons_defaultmask, i);
965 if(!cvar("g_use_ammunition"))
966 start_items |= IT_UNLIMITED_AMMO;
968 if(cvar("g_nexball"))
969 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
973 start_ammo_cells = cvar("g_minstagib_ammo_start");
974 start_ammo_fuel = cvar("g_start_ammo_fuel");
976 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
978 start_ammo_rockets = 999;
979 start_ammo_shells = 999;
980 start_ammo_cells = 999;
981 start_ammo_nails = 999;
982 start_ammo_fuel = 999;
988 start_ammo_shells = cvar("g_lms_start_ammo_shells");
989 start_ammo_nails = cvar("g_lms_start_ammo_nails");
990 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
991 start_ammo_cells = cvar("g_lms_start_ammo_cells");
992 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
996 start_ammo_shells = cvar("g_start_ammo_shells");
997 start_ammo_nails = cvar("g_start_ammo_nails");
998 start_ammo_rockets = cvar("g_start_ammo_rockets");
999 start_ammo_cells = cvar("g_start_ammo_cells");
1000 start_ammo_fuel = cvar("g_start_ammo_fuel");
1006 start_health = cvar("g_lms_start_health");
1007 start_armorvalue = cvar("g_lms_start_armor");
1012 warmup_start_ammo_shells = start_ammo_shells;
1013 warmup_start_ammo_nails = start_ammo_nails;
1014 warmup_start_ammo_rockets = start_ammo_rockets;
1015 warmup_start_ammo_cells = start_ammo_cells;
1016 warmup_start_ammo_fuel = start_ammo_fuel;
1017 warmup_start_health = start_health;
1018 warmup_start_armorvalue = start_armorvalue;
1019 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
1020 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
1021 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
1023 if (!g_weaponarena && !g_minstagib && !g_ca)
1025 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1026 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1027 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1028 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1029 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1030 warmup_start_health = cvar("g_warmup_start_health");
1031 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1032 WEPSET_CLEAR_A(warmup_start_weapons);
1033 WEPSET_CLEAR_A(warmup_start_weapons_default);
1034 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
1035 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1037 e = get_weaponinfo(i);
1038 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
1040 WEPSET_OR_AW(warmup_start_weapons, i);
1042 WEPSET_OR_AW(warmup_start_weapons_default, i);
1044 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
1050 start_items |= IT_JETPACK;
1052 MUTATOR_CALLHOOK(SetStartItems);
1054 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
1056 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1057 start_items |= IT_FUEL_REGEN;
1058 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1059 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1062 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1064 e = get_weaponinfo(i);
1065 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
1066 weapon_action(i, WR_PRECACHE);
1069 start_ammo_shells = max(0, start_ammo_shells);
1070 start_ammo_nails = max(0, start_ammo_nails);
1071 start_ammo_cells = max(0, start_ammo_cells);
1072 start_ammo_rockets = max(0, start_ammo_rockets);
1073 start_ammo_fuel = max(0, start_ammo_fuel);
1075 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1076 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1077 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1078 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1079 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1083 float g_bugrigs_planar_movement;
1084 float g_bugrigs_planar_movement_car_jumping;
1085 float g_bugrigs_reverse_spinning;
1086 float g_bugrigs_reverse_speeding;
1087 float g_bugrigs_reverse_stopping;
1088 float g_bugrigs_air_steering;
1089 float g_bugrigs_angle_smoothing;
1090 float g_bugrigs_friction_floor;
1091 float g_bugrigs_friction_brake;
1092 float g_bugrigs_friction_air;
1093 float g_bugrigs_accel;
1094 float g_bugrigs_speed_ref;
1095 float g_bugrigs_speed_pow;
1096 float g_bugrigs_steer;
1098 float g_touchexplode;
1099 float g_touchexplode_radius;
1100 float g_touchexplode_damage;
1101 float g_touchexplode_edgedamage;
1102 float g_touchexplode_force;
1107 string GetGametype(); // g_world.qc
1108 void readlevelcvars(void)
1110 g_minstagib = cvar("g_minstagib");
1112 monster_skill = cvar("g_monsters_skill");
1114 // load ALL the mutators
1115 if(cvar("g_dodging"))
1116 MUTATOR_ADD(mutator_dodging);
1117 if(cvar("g_spawn_near_teammate"))
1118 MUTATOR_ADD(mutator_spawn_near_teammate);
1119 if(cvar("g_physical_items"))
1120 MUTATOR_ADD(mutator_physical_items);
1123 if(cvar("g_invincible_projectiles"))
1124 MUTATOR_ADD(mutator_invincibleprojectiles);
1125 if(cvar("g_new_toys"))
1126 MUTATOR_ADD(mutator_new_toys);
1128 MUTATOR_ADD(mutator_nix);
1129 if(cvar("g_rocket_flying"))
1130 MUTATOR_ADD(mutator_rocketflying);
1131 if(cvar("g_vampire"))
1132 MUTATOR_ADD(mutator_vampire);
1133 if(cvar("g_superspectate"))
1134 MUTATOR_ADD(mutator_superspec);
1137 // is this a mutator? is this a mode?
1138 if(cvar("g_sandbox"))
1139 MUTATOR_ADD(sandbox);
1142 MUTATOR_ADD(mutator_zombie_apocalypse);
1144 if(cvar("sv_allow_fullbright"))
1145 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1147 g_bugrigs = cvar("g_bugrigs");
1148 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1149 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1150 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1151 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1152 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1153 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1154 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1155 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1156 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1157 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1158 g_bugrigs_accel = cvar("g_bugrigs_accel");
1159 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1160 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1161 g_bugrigs_steer = cvar("g_bugrigs_steer");
1163 g_touchexplode = cvar("g_touchexplode");
1164 g_touchexplode_radius = cvar("g_touchexplode_radius");
1165 g_touchexplode_damage = cvar("g_touchexplode_damage");
1166 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1167 g_touchexplode_force = cvar("g_touchexplode_force");
1169 sv_clones = cvar("sv_clones");
1170 sv_gentle = cvar("sv_gentle");
1171 sv_foginterval = cvar("sv_foginterval");
1172 g_cloaked = cvar("g_cloaked");
1174 g_cloaked = 1; // always enable cloak in CTS
1175 g_jump_grunt = cvar("g_jump_grunt");
1176 g_footsteps = cvar("g_footsteps");
1177 g_grappling_hook = cvar("g_grappling_hook");
1178 g_jetpack = cvar("g_jetpack");
1179 g_midair = cvar("g_midair");
1180 g_norecoil = cvar("g_norecoil");
1181 g_bloodloss = cvar("g_bloodloss");
1182 sv_maxidle = cvar("sv_maxidle");
1183 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1184 sv_autotaunt = cvar("sv_autotaunt");
1185 sv_taunt = cvar("sv_taunt");
1187 inWarmupStage = cvar("g_warmup");
1188 g_warmup_limit = cvar("g_warmup_limit");
1189 g_warmup_allguns = cvar("g_warmup_allguns");
1190 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1192 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1193 inWarmupStage = 0; // these modes cannot work together, sorry
1195 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1196 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1197 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1198 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1199 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1200 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1201 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1202 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1203 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1204 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1205 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1206 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1207 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1208 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1210 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1211 g_weaponratefactor = cvar("g_weaponratefactor");
1212 g_weapondamagefactor = cvar("g_weapondamagefactor");
1213 g_weaponforcefactor = cvar("g_weaponforcefactor");
1214 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1216 g_pickup_shells = cvar("g_pickup_shells");
1217 g_pickup_shells_max = cvar("g_pickup_shells_max");
1218 g_pickup_nails = cvar("g_pickup_nails");
1219 g_pickup_nails_max = cvar("g_pickup_nails_max");
1220 g_pickup_rockets = cvar("g_pickup_rockets");
1221 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1222 g_pickup_cells = cvar("g_pickup_cells");
1223 g_pickup_cells_max = cvar("g_pickup_cells_max");
1224 g_pickup_fuel = cvar("g_pickup_fuel");
1225 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1226 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1227 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1228 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1229 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1230 g_pickup_armormedium = cvar("g_pickup_armormedium");
1231 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1232 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1233 g_pickup_armorbig = cvar("g_pickup_armorbig");
1234 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1235 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1236 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1237 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1238 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1239 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1240 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1241 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1242 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1243 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1244 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1245 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1246 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1247 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1248 g_pickup_healthmega = cvar("g_pickup_healthmega");
1249 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1250 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1252 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1253 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1255 g_pinata = cvar("g_pinata");
1257 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1259 g_weapon_stay = cvar("g_weapon_stay");
1261 if not(inWarmupStage && !g_ca)
1262 game_starttime = cvar("g_start_delay");
1264 readplayerstartcvars();
1270 string precache_sound (string s) = #19;
1271 float precache_sound_index (string s) = #19;
1273 #define SND_VOLUME 1
1274 #define SND_ATTENUATION 2
1275 #define SND_LARGEENTITY 8
1276 #define SND_LARGESOUND 16
1278 float sound_allowed(float dest, entity e)
1280 // sounds from world may always pass
1283 if (e.classname == "body")
1285 else if (e.realowner && e.realowner != e)
1287 else if (e.owner && e.owner != e)
1292 // sounds to self may always pass
1293 if (dest == MSG_ONE)
1294 if (e == msg_entity)
1296 // sounds by players can be removed
1297 if (autocvar_bot_sound_monopoly)
1298 if (clienttype(e) == CLIENTTYPE_REAL)
1300 // anything else may pass
1304 #ifdef COMPAT_XON010_CHANNELS
1305 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1306 void sound(entity e, float chan, string samp, float vol, float atten)
1308 if (!sound_allowed(MSG_BROADCAST, e))
1310 builtin_sound(e, chan, samp, vol, atten);
1314 void sound(entity e, float chan, string samp, float vol, float atten)
1316 if (!sound_allowed(MSG_BROADCAST, e))
1318 sound7(e, chan, samp, vol, atten, 0, 0);
1322 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1326 if (!sound_allowed(dest, e))
1329 entno = num_for_edict(e);
1330 idx = precache_sound_index(samp);
1335 atten = floor(atten * 64);
1336 vol = floor(vol * 255);
1339 sflags |= SND_VOLUME;
1341 sflags |= SND_ATTENUATION;
1342 if (entno >= 8192 || chan < 0 || chan > 7)
1343 sflags |= SND_LARGEENTITY;
1345 sflags |= SND_LARGESOUND;
1347 WriteByte(dest, SVC_SOUND);
1348 WriteByte(dest, sflags);
1349 if (sflags & SND_VOLUME)
1350 WriteByte(dest, vol);
1351 if (sflags & SND_ATTENUATION)
1352 WriteByte(dest, atten);
1353 if (sflags & SND_LARGEENTITY)
1355 WriteShort(dest, entno);
1356 WriteByte(dest, chan);
1360 WriteShort(dest, entno * 8 + chan);
1362 if (sflags & SND_LARGESOUND)
1363 WriteShort(dest, idx);
1365 WriteByte(dest, idx);
1367 WriteCoord(dest, o_x);
1368 WriteCoord(dest, o_y);
1369 WriteCoord(dest, o_z);
1371 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1375 if (!sound_allowed(dest, e))
1378 o = e.origin + 0.5 * (e.mins + e.maxs);
1379 soundtoat(dest, e, o, chan, samp, vol, atten);
1381 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1383 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1385 void stopsoundto(float dest, entity e, float chan)
1389 if (!sound_allowed(dest, e))
1392 entno = num_for_edict(e);
1394 if (entno >= 8192 || chan < 0 || chan > 7)
1397 idx = precache_sound_index("misc/null.wav");
1398 sflags = SND_LARGEENTITY;
1400 sflags |= SND_LARGESOUND;
1401 WriteByte(dest, SVC_SOUND);
1402 WriteByte(dest, sflags);
1403 WriteShort(dest, entno);
1404 WriteByte(dest, chan);
1405 if (sflags & SND_LARGESOUND)
1406 WriteShort(dest, idx);
1408 WriteByte(dest, idx);
1409 WriteCoord(dest, e.origin_x);
1410 WriteCoord(dest, e.origin_y);
1411 WriteCoord(dest, e.origin_z);
1415 WriteByte(dest, SVC_STOPSOUND);
1416 WriteShort(dest, entno * 8 + chan);
1419 void stopsound(entity e, float chan)
1421 if (!sound_allowed(MSG_BROADCAST, e))
1424 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1425 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1428 void play2(entity e, string filename)
1430 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1432 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1435 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1437 float spamsound(entity e, float chan, string samp, float vol, float atten)
1439 if (!sound_allowed(MSG_BROADCAST, e))
1442 if (time > e.spamtime)
1445 sound(e, chan, samp, vol, atten);
1451 void play2team(float t, string filename)
1455 if (autocvar_bot_sound_monopoly)
1458 FOR_EACH_REALPLAYER(head)
1461 play2(head, filename);
1465 void play2all(string samp)
1467 if (autocvar_bot_sound_monopoly)
1470 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1473 void PrecachePlayerSounds(string f);
1474 void precache_playermodel(string m)
1476 float globhandle, i, n;
1479 if(substring(m, -9,5) == "_lod1")
1481 if(substring(m, -9,5) == "_lod2")
1484 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1487 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1491 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1494 n = search_getsize(globhandle);
1495 for (i = 0; i < n; ++i)
1497 //print(search_getfilename(globhandle, i), "\n");
1498 f = search_getfilename(globhandle, i);
1499 PrecachePlayerSounds(f);
1501 search_end(globhandle);
1503 void precache_all_playermodels(string pattern)
1505 float globhandle, i, n;
1508 globhandle = search_begin(pattern, TRUE, FALSE);
1511 n = search_getsize(globhandle);
1512 for (i = 0; i < n; ++i)
1514 //print(search_getfilename(globhandle, i), "\n");
1515 f = search_getfilename(globhandle, i);
1516 precache_playermodel(f);
1518 search_end(globhandle);
1523 // gamemode related things
1524 precache_model ("models/misc/chatbubble.spr");
1527 precache_model ("models/runematch/curse.mdl");
1528 precache_model ("models/runematch/rune.mdl");
1531 #ifdef TTURRETS_ENABLED
1532 if (autocvar_g_turrets)
1536 // Precache all player models if desired
1537 if (autocvar_sv_precacheplayermodels)
1539 PrecachePlayerSounds("sound/player/default.sounds");
1540 precache_all_playermodels("models/player/*.zym");
1541 precache_all_playermodels("models/player/*.dpm");
1542 precache_all_playermodels("models/player/*.md3");
1543 precache_all_playermodels("models/player/*.psk");
1544 precache_all_playermodels("models/player/*.iqm");
1547 if (autocvar_sv_defaultcharacter)
1550 s = autocvar_sv_defaultplayermodel_red;
1552 precache_playermodel(s);
1553 s = autocvar_sv_defaultplayermodel_blue;
1555 precache_playermodel(s);
1556 s = autocvar_sv_defaultplayermodel_yellow;
1558 precache_playermodel(s);
1559 s = autocvar_sv_defaultplayermodel_pink;
1561 precache_playermodel(s);
1562 s = autocvar_sv_defaultplayermodel;
1564 precache_playermodel(s);
1569 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1570 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1573 // gore and miscellaneous sounds
1574 //precache_sound ("misc/h2ohit.wav");
1575 precache_model ("models/hook.md3");
1576 precache_sound ("misc/armorimpact.wav");
1577 precache_sound ("misc/bodyimpact1.wav");
1578 precache_sound ("misc/bodyimpact2.wav");
1579 precache_sound ("misc/gib.wav");
1580 precache_sound ("misc/gib_splat01.wav");
1581 precache_sound ("misc/gib_splat02.wav");
1582 precache_sound ("misc/gib_splat03.wav");
1583 precache_sound ("misc/gib_splat04.wav");
1584 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1585 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1586 precache_sound ("misc/null.wav");
1587 precache_sound ("misc/spawn.wav");
1588 precache_sound ("misc/talk.wav");
1589 precache_sound ("misc/teleport.wav");
1590 precache_sound ("misc/poweroff.wav");
1591 precache_sound ("player/lava.wav");
1592 precache_sound ("player/slime.wav");
1594 precache_model ("models/sprites/0.spr32");
1595 precache_model ("models/sprites/1.spr32");
1596 precache_model ("models/sprites/2.spr32");
1597 precache_model ("models/sprites/3.spr32");
1598 precache_model ("models/sprites/4.spr32");
1599 precache_model ("models/sprites/5.spr32");
1600 precache_model ("models/sprites/6.spr32");
1601 precache_model ("models/sprites/7.spr32");
1602 precache_model ("models/sprites/8.spr32");
1603 precache_model ("models/sprites/9.spr32");
1604 precache_model ("models/sprites/10.spr32");
1606 // common weapon precaches
1607 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1608 precache_sound ("weapons/weapon_switch.wav");
1609 precache_sound ("weapons/weaponpickup.wav");
1610 precache_sound ("weapons/unavailable.wav");
1611 precache_sound ("weapons/dryfire.wav");
1612 if (g_grappling_hook)
1614 precache_sound ("weapons/hook_fire.wav"); // hook
1615 precache_sound ("weapons/hook_impact.wav"); // hook
1618 if(autocvar_sv_precacheweapons)
1620 //precache weapon models/sounds
1623 while (wep <= WEP_LAST)
1625 weapon_action(wep, WR_PRECACHE);
1630 precache_model("models/elaser.mdl");
1631 precache_model("models/laser.mdl");
1632 precache_model("models/ebomb.mdl");
1635 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1637 if (!self.noise && self.music) // quake 3 uses the music field
1638 self.noise = self.music;
1640 // plays music for the level if there is any
1643 precache_sound (self.noise);
1644 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1649 // sorry, but using \ in macros breaks line numbers
1650 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
1651 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1652 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1655 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1657 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
1660 WRITESPECTATABLE_MSG_ONE({
1661 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1662 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1663 WriteByte(MSG_ONE, id);
1664 WriteString(MSG_ONE, s);
1665 if (id != 0 && s != "")
1667 WriteByte(MSG_ONE, duration);
1668 WriteByte(MSG_ONE, countdown_num);
1673 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1675 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1677 // WARNING: this kills the trace globals
1678 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1679 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1681 #define INITPRIO_FIRST 0
1682 #define INITPRIO_GAMETYPE 0
1683 #define INITPRIO_GAMETYPE_FALLBACK 1
1684 #define INITPRIO_FINDTARGET 10
1685 #define INITPRIO_DROPTOFLOOR 20
1686 #define INITPRIO_SETLOCATION 90
1687 #define INITPRIO_LINKDOORS 91
1688 #define INITPRIO_LAST 99
1690 .void(void) initialize_entity;
1691 .float initialize_entity_order;
1692 .entity initialize_entity_next;
1693 entity initialize_entity_first;
1695 void make_safe_for_remove(entity e)
1697 if (e.initialize_entity)
1699 entity ent, prev = world;
1700 for (ent = initialize_entity_first; ent; )
1702 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1704 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1705 // skip it in linked list
1708 prev.initialize_entity_next = ent.initialize_entity_next;
1709 ent = prev.initialize_entity_next;
1713 initialize_entity_first = ent.initialize_entity_next;
1714 ent = initialize_entity_first;
1720 ent = ent.initialize_entity_next;
1726 void objerror(string s)
1728 make_safe_for_remove(self);
1729 builtin_objerror(s);
1732 .float remove_except_protected_forbidden;
1733 void remove_except_protected(entity e)
1735 if(e.remove_except_protected_forbidden)
1736 error("not allowed to remove this at this point");
1740 void remove_unsafely(entity e)
1742 if(e.classname == "spike")
1743 error("Removing spikes is forbidden (crylink bug), please report");
1747 void remove_safely(entity e)
1749 make_safe_for_remove(e);
1753 void InitializeEntity(entity e, void(void) func, float order)
1757 if (!e || e.initialize_entity)
1759 // make a proxy initializer entity
1763 e.classname = "initialize_entity";
1767 e.initialize_entity = func;
1768 e.initialize_entity_order = order;
1770 cur = initialize_entity_first;
1774 if (!cur || cur.initialize_entity_order > order)
1776 // insert between prev and cur
1778 prev.initialize_entity_next = e;
1780 initialize_entity_first = e;
1781 e.initialize_entity_next = cur;
1785 cur = cur.initialize_entity_next;
1788 void InitializeEntitiesRun()
1791 startoflist = initialize_entity_first;
1792 initialize_entity_first = world;
1793 remove = remove_except_protected;
1794 for (self = startoflist; self; self = self.initialize_entity_next)
1796 self.remove_except_protected_forbidden = 1;
1798 for (self = startoflist; self; )
1801 var void(void) func;
1802 e = self.initialize_entity_next;
1803 func = self.initialize_entity;
1804 self.initialize_entity_order = 0;
1805 self.initialize_entity = func_null;
1806 self.initialize_entity_next = world;
1807 self.remove_except_protected_forbidden = 0;
1808 if (self.classname == "initialize_entity")
1812 builtin_remove(self);
1815 //dprint("Delayed initialization: ", self.classname, "\n");
1821 backtrace(strcat("Null function in: ", self.classname, "\n"));
1825 remove = remove_unsafely;
1828 .float uncustomizeentityforclient_set;
1829 .void(void) uncustomizeentityforclient;
1830 void UncustomizeEntitiesRun()
1834 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1835 self.uncustomizeentityforclient();
1838 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1840 e.customizeentityforclient = customizer;
1841 e.uncustomizeentityforclient = uncustomizer;
1842 e.uncustomizeentityforclient_set = !!uncustomizer;
1846 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1849 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1853 if (e.classname == "")
1854 e.classname = "net_linked";
1856 if (e.model == "" || self.modelindex == 0)
1860 setmodel(e, "null");
1864 e.SendEntity = sendfunc;
1865 e.SendFlags = 0xFFFFFF;
1868 e.effects |= EF_NODEPTHTEST;
1872 e.nextthink = time + dt;
1873 e.think = SUB_Remove;
1877 void adaptor_think2touch()
1886 void adaptor_think2use()
1898 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1900 if not(self.flags & FL_ONGROUND) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING
1901 self.projectiledeathtype |= HITTYPE_SPLASH;
1902 adaptor_think2use();
1905 // deferred dropping
1906 void DropToFloor_Handler()
1908 builtin_droptofloor();
1909 self.dropped_origin = self.origin;
1914 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1919 float trace_hits_box_a0, trace_hits_box_a1;
1921 float trace_hits_box_1d(float end, float thmi, float thma)
1925 // just check if x is in range
1933 // do the trace with respect to x
1934 // 0 -> end has to stay in thmi -> thma
1935 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1936 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1937 if (trace_hits_box_a0 > trace_hits_box_a1)
1943 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1948 // now it is a trace from 0 to end
1950 trace_hits_box_a0 = 0;
1951 trace_hits_box_a1 = 1;
1953 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1955 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1957 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1963 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1965 return trace_hits_box(start, end, thmi - ma, thma - mi);
1968 float SUB_NoImpactCheck()
1970 // zero hitcontents = this is not the real impact, but either the
1971 // mirror-impact of something hitting the projectile instead of the
1972 // projectile hitting the something, or a touchareagrid one. Neither of
1973 // these stop the projectile from moving, so...
1974 if(trace_dphitcontents == 0)
1976 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1977 dprint(sprintf(_("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Profectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.origin)));
1980 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1982 if (other == world && self.size != '0 0 0')
1985 tic = self.velocity * sys_frametime;
1986 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1987 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1988 if (trace_fraction >= 1)
1990 dprint("Odd... did not hit...?\n");
1992 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1994 dprint("Detected and prevented the sky-grapple bug.\n");
2002 #define SUB_OwnerCheck() (other && (other == self.owner))
2004 void RemoveGrapplingHook(entity pl);
2005 void W_Crylink_Dequeue(entity e);
2006 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2008 if(SUB_OwnerCheck())
2010 if(SUB_NoImpactCheck())
2012 if(self.classname == "grapplinghook")
2013 RemoveGrapplingHook(self.realowner);
2014 else if(self.classname == "spike")
2016 W_Crylink_Dequeue(self);
2023 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2024 UpdateCSQCProjectile(self);
2027 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2029 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
2030 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
2032 void URI_Get_Callback(float id, float status, string data)
2034 if(url_URI_Get_Callback(id, status, data))
2038 else if (id == URI_GET_DISCARD)
2042 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
2045 Curl_URI_Get_Callback(id, status, data);
2047 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2050 OnlineBanList_URI_Get_Callback(id, status, data);
2054 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2058 string uid2name(string myuid) {
2060 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2062 // FIXME remove this later after 0.6 release
2063 // convert old style broken records to correct style
2066 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2069 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2070 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2075 s = "^1Unregistered Player";
2079 float race_readTime(string map, float pos)
2087 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2090 string race_readUID(string map, float pos)
2098 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2101 float race_readPos(string map, float t) {
2103 for (i = 1; i <= RANKINGS_CNT; ++i)
2104 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2107 return 0; // pos is zero if unranked
2110 void race_writeTime(string map, float t, string myuid)
2119 newpos = race_readPos(map, t);
2121 float i, prevpos = 0;
2122 for(i = 1; i <= RANKINGS_CNT; ++i)
2124 if(race_readUID(map, i) == myuid)
2127 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2128 for (i = prevpos; i > newpos; --i) {
2129 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2130 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2132 } else { // player has no ranked record yet
2133 for (i = RANKINGS_CNT; i > newpos; --i) {
2134 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2135 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2139 // store new time itself
2140 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2141 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2144 string race_readName(string map, float pos)
2152 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2155 string race_placeName(float pos) {
2156 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2158 if(mod(pos, 10) == 1)
2159 return strcat(ftos(pos), "st");
2160 else if(mod(pos, 10) == 2)
2161 return strcat(ftos(pos), "nd");
2162 else if(mod(pos, 10) == 3)
2163 return strcat(ftos(pos), "rd");
2165 return strcat(ftos(pos), "th");
2168 return strcat(ftos(pos), "th");
2171 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2174 vector start, org, delta, end, enddown, mstart;
2177 m = e.dphitcontentsmask;
2178 e.dphitcontentsmask = goodcontents | badcontents;
2181 delta = world.maxs - world.mins;
2185 for (i = 0; i < attempts; ++i)
2187 start_x = org_x + random() * delta_x;
2188 start_y = org_y + random() * delta_y;
2189 start_z = org_z + random() * delta_z;
2191 // rule 1: start inside world bounds, and outside
2192 // solid, and don't start from somewhere where you can
2193 // fall down to evil
2194 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2195 if (trace_fraction >= 1)
2197 if (trace_startsolid)
2199 if (trace_dphitcontents & badcontents)
2201 if (trace_dphitq3surfaceflags & badsurfaceflags)
2204 // rule 2: if we are too high, lower the point
2205 if (trace_fraction * delta_z > maxaboveground)
2206 start = trace_endpos + '0 0 1' * maxaboveground;
2207 enddown = trace_endpos;
2209 // rule 3: make sure we aren't outside the map. This only works
2210 // for somewhat well formed maps. A good rule of thumb is that
2211 // the map should have a convex outside hull.
2212 // these can be traceLINES as we already verified the starting box
2213 mstart = start + 0.5 * (e.mins + e.maxs);
2214 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2215 if (trace_fraction >= 1)
2217 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2218 if (trace_fraction >= 1)
2220 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2221 if (trace_fraction >= 1)
2223 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2224 if (trace_fraction >= 1)
2226 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2227 if (trace_fraction >= 1)
2230 // rule 4: we must "see" some spawnpoint
2231 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2232 if(checkpvs(mstart, sp))
2236 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2237 if(checkpvs(mstart, sp))
2243 // find a random vector to "look at"
2244 end_x = org_x + random() * delta_x;
2245 end_y = org_y + random() * delta_y;
2246 end_z = org_z + random() * delta_z;
2247 end = start + normalize(end - start) * vlen(delta);
2249 // rule 4: start TO end must not be too short
2250 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2251 if (trace_startsolid)
2253 if (trace_fraction < minviewdistance / vlen(delta))
2256 // rule 5: don't want to look at sky
2257 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2260 // rule 6: we must not end up in trigger_hurt
2261 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2267 e.dphitcontentsmask = m;
2271 setorigin(e, start);
2272 e.angles = vectoangles(end - start);
2273 dprint("Needed ", ftos(i + 1), " attempts\n");
2280 float zcurveparticles_effectno;
2281 vector zcurveparticles_start;
2282 float zcurveparticles_spd;
2284 void endzcurveparticles()
2286 if(zcurveparticles_effectno)
2289 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2291 zcurveparticles_effectno = 0;
2294 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2296 spd = bound(0, floor(spd / 16), 32767);
2297 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2299 endzcurveparticles();
2300 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2301 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2302 WriteShort(MSG_BROADCAST, effectno);
2303 WriteCoord(MSG_BROADCAST, start_x);
2304 WriteCoord(MSG_BROADCAST, start_y);
2305 WriteCoord(MSG_BROADCAST, start_z);
2306 zcurveparticles_effectno = effectno;
2307 zcurveparticles_start = start;
2310 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2311 WriteCoord(MSG_BROADCAST, end_x);
2312 WriteCoord(MSG_BROADCAST, end_y);
2313 WriteCoord(MSG_BROADCAST, end_z);
2314 WriteCoord(MSG_BROADCAST, end_dz);
2315 zcurveparticles_spd = spd;
2318 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2321 vector vecxy, velxy;
2323 vecxy = end - start;
2328 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2330 endzcurveparticles();
2331 trailparticles(world, effectno, start, end);
2335 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2336 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2339 void write_recordmarker(entity pl, float tstart, float dt)
2341 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2343 // also write a marker into demo files for demotc-race-record-extractor to find
2346 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2347 " ", ftos(tstart), " ", ftos(dt), "\n"));
2350 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2363 if(allowcenter) // 2: allow center handedness
2376 if(allowcenter) // 2: allow center handedness
2392 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2397 if (autocvar_g_shootfromeye)
2401 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2402 else { vecs_y = 0; vecs_z -= 2; }
2410 else if (autocvar_g_shootfromcenter)
2415 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2425 else if (autocvar_g_shootfromclient)
2427 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2432 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2434 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2438 void attach_sameorigin(entity e, entity to, string tag)
2440 vector org, t_forward, t_left, t_up, e_forward, e_up;
2443 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2444 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2445 t_forward = v_forward * tagscale;
2446 t_left = v_right * -tagscale;
2447 t_up = v_up * tagscale;
2449 e.origin_x = org * t_forward;
2450 e.origin_y = org * t_left;
2451 e.origin_z = org * t_up;
2453 // current forward and up directions
2454 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2455 e.angles = AnglesTransform_FromVAngles(e.angles);
2457 e.angles = AnglesTransform_FromAngles(e.angles);
2458 fixedmakevectors(e.angles);
2460 // untransform forward, up!
2461 e_forward_x = v_forward * t_forward;
2462 e_forward_y = v_forward * t_left;
2463 e_forward_z = v_forward * t_up;
2464 e_up_x = v_up * t_forward;
2465 e_up_y = v_up * t_left;
2466 e_up_z = v_up * t_up;
2468 e.angles = fixedvectoangles2(e_forward, e_up);
2469 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2470 e.angles = AnglesTransform_ToVAngles(e.angles);
2472 e.angles = AnglesTransform_ToAngles(e.angles);
2474 setattachment(e, to, tag);
2475 setorigin(e, e.origin);
2478 void detach_sameorigin(entity e)
2481 org = gettaginfo(e, 0);
2482 e.angles = fixedvectoangles2(v_forward, v_up);
2483 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2484 e.angles = AnglesTransform_ToVAngles(e.angles);
2486 e.angles = AnglesTransform_ToAngles(e.angles);
2488 setattachment(e, world, "");
2489 setorigin(e, e.origin);
2492 void follow_sameorigin(entity e, entity to)
2494 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2495 e.aiment = to; // make the hole follow bmodel
2496 e.punchangle = to.angles; // the original angles of bmodel
2497 e.view_ofs = e.origin - to.origin; // relative origin
2498 e.v_angle = e.angles - to.angles; // relative angles
2501 void unfollow_sameorigin(entity e)
2503 e.movetype = MOVETYPE_NONE;
2506 entity gettaginfo_relative_ent;
2507 vector gettaginfo_relative(entity e, float tag)
2509 if (!gettaginfo_relative_ent)
2511 gettaginfo_relative_ent = spawn();
2512 gettaginfo_relative_ent.effects = EF_NODRAW;
2514 gettaginfo_relative_ent.model = e.model;
2515 gettaginfo_relative_ent.modelindex = e.modelindex;
2516 gettaginfo_relative_ent.frame = e.frame;
2517 return gettaginfo(gettaginfo_relative_ent, tag);
2522 float modeleffect_SendEntity(entity to, float sf)
2525 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2528 if(self.velocity != '0 0 0')
2530 if(self.angles != '0 0 0')
2532 if(self.avelocity != '0 0 0')
2535 WriteByte(MSG_ENTITY, f);
2536 WriteShort(MSG_ENTITY, self.modelindex);
2537 WriteByte(MSG_ENTITY, self.skin);
2538 WriteByte(MSG_ENTITY, self.frame);
2539 WriteCoord(MSG_ENTITY, self.origin_x);
2540 WriteCoord(MSG_ENTITY, self.origin_y);
2541 WriteCoord(MSG_ENTITY, self.origin_z);
2544 WriteCoord(MSG_ENTITY, self.velocity_x);
2545 WriteCoord(MSG_ENTITY, self.velocity_y);
2546 WriteCoord(MSG_ENTITY, self.velocity_z);
2550 WriteCoord(MSG_ENTITY, self.angles_x);
2551 WriteCoord(MSG_ENTITY, self.angles_y);
2552 WriteCoord(MSG_ENTITY, self.angles_z);
2556 WriteCoord(MSG_ENTITY, self.avelocity_x);
2557 WriteCoord(MSG_ENTITY, self.avelocity_y);
2558 WriteCoord(MSG_ENTITY, self.avelocity_z);
2560 WriteShort(MSG_ENTITY, self.scale * 256.0);
2561 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2562 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2563 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2564 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2569 void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
2574 e.classname = "modeleffect";
2582 e.teleport_time = t1;
2586 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2590 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2593 sz = max(e.scale, e.scale2);
2594 setsize(e, e.mins * sz, e.maxs * sz);
2595 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2598 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2600 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2603 float randombit(float bits)
2605 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2614 for(f = 1; f <= bits; f *= 2)
2623 r = (r - 1) / (n - 1);
2630 float randombits(float bits, float k, float error_return)
2634 while(k > 0 && bits != r)
2636 r += randombit(bits - r);
2645 void randombit_test(float bits, float iter)
2649 print(ftos(randombit(bits)), "\n");
2654 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2656 if(halflifedist > 0)
2657 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2658 else if(halflifedist < 0)
2659 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2668 #define cvar_string_normal builtin_cvar_string
2669 #define cvar_normal builtin_cvar
2671 string cvar_string_normal(string n)
2673 if not(cvar_type(n) & 1)
2674 backtrace(strcat("Attempt to access undefined cvar: ", n));
2675 return builtin_cvar_string(n);
2678 float cvar_normal(string n)
2680 return stof(cvar_string_normal(n));
2683 #define cvar_set_normal builtin_cvar_set
2691 oself.think = SUB_Remove;
2692 oself.nextthink = time;
2698 Execute func() after time + fdelay.
2699 self when func is executed = self when defer is called
2701 void defer(float fdelay, void() func)
2708 e.think = defer_think;
2709 e.nextthink = time + fdelay;
2712 .string aiment_classname;
2713 .float aiment_deadflag;
2714 void SetMovetypeFollow(entity ent, entity e)
2716 // FIXME this may not be warpzone aware
2717 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2718 ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
2719 ent.aiment = e; // make the hole follow bmodel
2720 ent.punchangle = e.angles; // the original angles of bmodel
2721 ent.view_ofs = ent.origin - e.origin; // relative origin
2722 ent.v_angle = ent.angles - e.angles; // relative angles
2723 ent.aiment_classname = strzone(e.classname);
2724 ent.aiment_deadflag = e.deadflag;
2726 void UnsetMovetypeFollow(entity ent)
2728 ent.movetype = MOVETYPE_FLY;
2729 PROJECTILE_MAKETRIGGER(ent);
2732 float LostMovetypeFollow(entity ent)
2735 if(ent.movetype != MOVETYPE_FOLLOW)
2741 if(ent.aiment.classname != ent.aiment_classname)
2743 if(ent.aiment.deadflag != ent.aiment_deadflag)
2749 float isPushable(entity e)
2758 case "droppedweapon":
2759 case "keepawayball":
2760 case "nexball_basketball":
2761 case "nexball_football":
2763 case "bullet": // antilagged bullets can't hit this either
2766 if (e.projectiledeathtype)