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;
71 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
74 string STR_PLAYER = "player";
75 string STR_SPECTATOR = "spectator";
76 string STR_OBSERVER = "observer";
79 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
80 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
81 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
82 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
84 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
85 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
86 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
87 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
88 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
91 // copies a string to a tempstring (so one can strunzone it)
92 string strcat1(string s) = #115; // FRIK_FILE
97 void bcenterprint(string s)
99 // TODO replace by MSG_ALL (would show it to spectators too, though)?
101 FOR_EACH_PLAYER(head)
102 if (clienttype(head) == CLIENTTYPE_REAL)
103 centerprint(head, s);
106 void GameLogEcho(string s)
111 if (autocvar_sv_eventlog_files)
116 matches = autocvar_sv_eventlog_files_counter + 1;
117 cvar_set("sv_eventlog_files_counter", ftos(matches));
120 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
121 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
122 logfile = fopen(fn, FILE_APPEND);
123 fputs(logfile, ":logversion:3\n");
127 if (autocvar_sv_eventlog_files_timestamps)
128 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
130 fputs(logfile, strcat(s, "\n"));
133 if (autocvar_sv_eventlog_console)
142 // will be opened later
147 if (logfile_open && logfile >= 0)
154 float spawnpoint_nag;
155 void relocate_spawnpoint()
157 // nudge off the floor
158 setorigin(self, self.origin + '0 0 1');
160 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
161 if (trace_startsolid)
167 if (!move_out_of_solid(self))
168 objerror("could not get out of solid at all!");
169 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
170 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
171 print(" ", ftos(self.origin_y - o_y));
172 print(" ", ftos(self.origin_z - o_z), "'\n");
173 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
176 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
182 self.mins = self.maxs = '0 0 0';
183 objerror("player spawn point in solid, mapper sucks!\n");
188 self.use = spawnpoint_use;
189 self.team_saved = self.team;
193 if (have_team_spawns != 0)
195 have_team_spawns = 1;
196 have_team_spawns_forteam[self.team] = 1;
198 if (autocvar_r_showbboxes)
200 // show where spawnpoints point at too
201 makevectors(self.angles);
204 e.classname = "info_player_foo";
205 setorigin(e, self.origin + v_forward * 24);
206 setsize(e, '-8 -8 -8', '8 8 8');
207 e.solid = SOLID_TRIGGER;
211 #define strstr strstrofs
213 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
214 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
215 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
216 // BE CONSTANT OR strzoneD!
217 float strstr(string haystack, string needle, float offset)
221 len = strlen(needle);
222 endpos = strlen(haystack) - len;
223 while(offset <= endpos)
225 found = substring(haystack, offset, len);
234 float NUM_NEAREST_ENTITIES = 4;
235 entity nearest_entity[NUM_NEAREST_ENTITIES];
236 float nearest_length[NUM_NEAREST_ENTITIES];
237 entity findnearest(vector point, .string field, string value, vector axismod)
248 localhead = find(world, field, value);
251 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
252 dist = localhead.oldorigin;
254 dist = localhead.origin;
256 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
259 for (i = 0; i < num_nearest; ++i)
261 if (len < nearest_length[i])
265 // now i tells us where to insert at
266 // INSERTION SORT! YOU'VE SEEN IT! RUN!
267 if (i < NUM_NEAREST_ENTITIES)
269 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
271 nearest_length[j + 1] = nearest_length[j];
272 nearest_entity[j + 1] = nearest_entity[j];
274 nearest_length[i] = len;
275 nearest_entity[i] = localhead;
276 if (num_nearest < NUM_NEAREST_ENTITIES)
277 num_nearest = num_nearest + 1;
280 localhead = find(localhead, field, value);
283 // now use the first one from our list that we can see
284 for (i = 0; i < num_nearest; ++i)
286 traceline(point, nearest_entity[i].origin, TRUE, world);
287 if (trace_fraction == 1)
291 dprint("Nearest point (");
292 dprint(nearest_entity[0].netname);
293 dprint(") is not visible, using a visible one.\n");
295 return nearest_entity[i];
299 if (num_nearest == 0)
302 dprint("Not seeing any location point, using nearest as fallback.\n");
304 dprint("Candidates were: ");
305 for(j = 0; j < num_nearest; ++j)
309 dprint(nearest_entity[j].netname);
314 return nearest_entity[0];
317 void spawnfunc_target_location()
319 self.classname = "target_location";
320 // location name in netname
321 // eventually support: count, teamgame selectors, line of sight?
324 void spawnfunc_info_location()
326 self.classname = "target_location";
327 self.message = self.netname;
330 string NearestLocation(vector p)
335 loc = findnearest(p, classname, "target_location", '1 1 1');
342 loc = findnearest(p, target, "###item###", '1 1 4');
349 string formatmessage(string msg)
360 WarpZone_crosshair_trace(self);
361 cursor = trace_endpos;
362 cursor_ent = trace_ent;
366 break; // too many replacements
369 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
370 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
383 replacement = substring(msg, p, 2);
384 escape = substring(msg, p + 1, 1);
388 else if (escape == "\\")
390 else if (escape == "n")
392 else if (escape == "a")
393 replacement = ftos(floor(self.armorvalue));
394 else if (escape == "h")
395 replacement = ftos(floor(self.health));
396 else if (escape == "l")
397 replacement = NearestLocation(self.origin);
398 else if (escape == "y")
399 replacement = NearestLocation(cursor);
400 else if (escape == "d")
401 replacement = NearestLocation(self.death_origin);
402 else if (escape == "w") {
406 wep = self.switchweapon;
409 replacement = W_Name(wep);
410 } else if (escape == "W") {
411 if (self.items & IT_SHELLS) replacement = "shells";
412 else if (self.items & IT_NAILS) replacement = "bullets";
413 else if (self.items & IT_ROCKETS) replacement = "rockets";
414 else if (self.items & IT_CELLS) replacement = "cells";
415 else replacement = "batteries"; // ;)
416 } else if (escape == "x") {
417 replacement = cursor_ent.netname;
418 if (!replacement || !cursor_ent)
419 replacement = "nothing";
420 } else if (escape == "s")
421 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
422 else if (escape == "S")
423 replacement = ftos(vlen(self.velocity));
425 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
426 p = p + strlen(replacement);
431 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
432 return (value == 0) ? FALSE : TRUE;
441 >0: receives a cvar from name=argv(f) value=argv(f+1)
443 void GetCvars_handleString(string thisname, float f, .string field, string name)
448 strunzone(self.field);
449 self.field = string_null;
453 if (thisname == name)
456 strunzone(self.field);
457 self.field = strzone(argv(f + 1));
461 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
463 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
465 GetCvars_handleString(thisname, f, field, name);
466 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
467 if (thisname == name)
470 s = func(strcat1(self.field));
473 strunzone(self.field);
474 self.field = strzone(s);
478 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
485 if (thisname == name)
486 self.field = stof(argv(f + 1));
489 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
491 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
498 if (thisname == name)
502 self.field = stof(argv(f + 1));
511 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
514 float w_getbestweapon(entity e);
515 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
518 o = W_FixWeaponOrder_ForceComplete(wo);
519 if(self.weaponorder_byimpulse)
521 strunzone(self.weaponorder_byimpulse);
522 self.weaponorder_byimpulse = string_null;
524 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
527 void GetCvars(float f)
532 s = strcat1(argv(f));
536 MUTATOR_CALLHOOK(GetCvars);
537 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
538 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
539 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
540 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
541 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
542 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
543 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
544 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
545 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
546 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
547 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
548 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
549 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
550 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
551 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
552 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
553 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
554 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
555 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
556 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
557 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
558 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
559 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
561 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
562 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
564 #ifdef ALLOW_FORCEMODELS
565 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
566 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
568 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
569 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
570 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
571 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
572 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
574 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
577 if (s == "cl_weaponpriority")
578 self.switchweapon = w_getbestweapon(self);
579 if (s == "cl_allow_uidtracking")
580 PlayerStats_AddPlayer(self);
584 void backtrace(string msg)
587 dev = autocvar_developer;
588 war = autocvar_prvm_backtraceforwarnings;
589 cvar_set("developer", "1");
590 cvar_set("prvm_backtraceforwarnings", "1");
592 print("--- CUT HERE ---\nWARNING: ");
595 remove(world); // isn't there any better way to cause a backtrace?
596 print("\n--- CUT UNTIL HERE ---\n");
597 cvar_set("developer", ftos(dev));
598 cvar_set("prvm_backtraceforwarnings", ftos(war));
601 string Team_ColorCode(float teamid)
603 if (teamid == COLOR_TEAM1)
605 else if (teamid == COLOR_TEAM2)
607 else if (teamid == COLOR_TEAM3)
609 else if (teamid == COLOR_TEAM4)
615 string Team_ColorName(float t)
617 // fixme: Search for team entities and get their .netname's!
618 if (t == COLOR_TEAM1)
620 if (t == COLOR_TEAM2)
622 if (t == COLOR_TEAM3)
624 if (t == COLOR_TEAM4)
629 string Team_ColorNameLowerCase(float t)
631 // fixme: Search for team entities and get their .netname's!
632 if (t == COLOR_TEAM1)
634 if (t == COLOR_TEAM2)
636 if (t == COLOR_TEAM3)
638 if (t == COLOR_TEAM4)
643 float ColourToNumber(string team_colour)
645 if (team_colour == "red")
648 if (team_colour == "blue")
651 if (team_colour == "yellow")
654 if (team_colour == "pink")
657 if (team_colour == "auto")
663 float NumberToTeamNumber(float number)
680 // decolorizes and team colors the player name when needed
681 string playername(entity p)
684 if (teamplay && !intermission_running && p.classname == "player")
686 t = Team_ColorCode(p.team);
687 return strcat(t, strdecolorize(p.netname));
693 vector randompos(vector m1, vector m2)
697 v_x = m2_x * random() + m1_x;
698 v_y = m2_y * random() + m1_y;
699 v_z = m2_z * random() + m1_z;
703 //#NO AUTOCVARS START
705 float g_pickup_shells;
706 float g_pickup_shells_max;
707 float g_pickup_nails;
708 float g_pickup_nails_max;
709 float g_pickup_rockets;
710 float g_pickup_rockets_max;
711 float g_pickup_cells;
712 float g_pickup_cells_max;
714 float g_pickup_fuel_jetpack;
715 float g_pickup_fuel_max;
716 float g_pickup_armorsmall;
717 float g_pickup_armorsmall_max;
718 float g_pickup_armorsmall_anyway;
719 float g_pickup_armormedium;
720 float g_pickup_armormedium_max;
721 float g_pickup_armormedium_anyway;
722 float g_pickup_armorbig;
723 float g_pickup_armorbig_max;
724 float g_pickup_armorbig_anyway;
725 float g_pickup_armorlarge;
726 float g_pickup_armorlarge_max;
727 float g_pickup_armorlarge_anyway;
728 float g_pickup_healthsmall;
729 float g_pickup_healthsmall_max;
730 float g_pickup_healthsmall_anyway;
731 float g_pickup_healthmedium;
732 float g_pickup_healthmedium_max;
733 float g_pickup_healthmedium_anyway;
734 float g_pickup_healthlarge;
735 float g_pickup_healthlarge_max;
736 float g_pickup_healthlarge_anyway;
737 float g_pickup_healthmega;
738 float g_pickup_healthmega_max;
739 float g_pickup_healthmega_anyway;
740 float g_pickup_ammo_anyway;
741 float g_pickup_weapons_anyway;
743 WEPSET_DECLARE_A(g_weaponarena_weapons);
744 float g_weaponarena_random;
745 float g_weaponarena_random_with_laser;
746 string g_weaponarena_list;
747 float g_weaponspeedfactor;
748 float g_weaponratefactor;
749 float g_weapondamagefactor;
750 float g_weaponforcefactor;
751 float g_weaponspreadfactor;
753 WEPSET_DECLARE_A(start_weapons);
754 WEPSET_DECLARE_A(start_weapons_default);
755 WEPSET_DECLARE_A(start_weapons_defaultmask);
757 float start_ammo_shells;
758 float start_ammo_nails;
759 float start_ammo_rockets;
760 float start_ammo_cells;
761 float start_ammo_fuel;
763 float start_armorvalue;
764 WEPSET_DECLARE_A(warmup_start_weapons);
765 WEPSET_DECLARE_A(warmup_start_weapons_default);
766 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
767 float warmup_start_ammo_shells;
768 float warmup_start_ammo_nails;
769 float warmup_start_ammo_rockets;
770 float warmup_start_ammo_cells;
771 float warmup_start_ammo_fuel;
772 float warmup_start_health;
773 float warmup_start_armorvalue;
776 entity get_weaponinfo(float w);
778 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
780 var float i = weaponinfo.weapon;
786 if (g_lms || g_ca || allguns)
787 d = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
789 d = (i == WEP_SHOTGUN);
791 d = 0; // weapon is set a few lines later
793 d = (i == WEP_LASER || i == WEP_SHOTGUN);
794 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
795 d |= (i == WEP_HOOK);
796 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
799 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
804 // 4: is set by default?
813 void readplayerstartcvars()
819 // initialize starting values for players
820 WEPSET_CLEAR_A(start_weapons);
821 WEPSET_CLEAR_A(start_weapons_default);
822 WEPSET_CLEAR_A(start_weapons_defaultmask);
824 start_ammo_shells = 0;
825 start_ammo_nails = 0;
826 start_ammo_rockets = 0;
827 start_ammo_cells = 0;
828 start_health = cvar("g_balance_health_start");
829 start_armorvalue = cvar("g_balance_armor_start");
832 WEPSET_CLEAR_A(g_weaponarena_weapons);
834 s = cvar_string("g_weaponarena");
835 if (s == "0" || s == "")
841 if (s == "0" || s == "")
847 // forcibly turn off weaponarena
852 g_weaponarena_list = "All Weapons";
853 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
854 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
855 WEPSET_OR_AW(g_weaponarena_weapons, j);
857 else if (s == "most")
860 g_weaponarena_list = "Most Weapons";
861 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
863 e = get_weaponinfo(j);
864 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
865 if (e.spawnflags & WEP_FLAG_NORMAL)
866 WEPSET_OR_AW(g_weaponarena_weapons, j);
869 else if (s == "none")
872 g_weaponarena_list = "No Weapons";
877 t = tokenize_console(s);
878 g_weaponarena_list = "";
879 for (i = 0; i < t; ++i)
882 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
884 e = get_weaponinfo(j);
887 WEPSET_OR_AW(g_weaponarena_weapons, j);
888 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
894 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
897 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
901 g_weaponarena_random = cvar("g_weaponarena_random");
903 g_weaponarena_random = 0;
904 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
908 g_minstagib = 0; // incompatible
909 g_pinata = 0; // incompatible
910 g_weapon_stay = 0; // incompatible
911 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
913 start_items |= IT_UNLIMITED_AMMO;
915 else if (g_minstagib)
917 g_pinata = 0; // incompatible
918 g_weapon_stay = 0; // incompatible
919 g_bloodloss = 0; // incompatible
921 start_armorvalue = 0;
922 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
923 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
924 start_items |= IT_UNLIMITED_SUPERWEAPONS;
926 if (g_minstagib_invis_alpha <= 0)
927 g_minstagib_invis_alpha = -1;
931 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
933 e = get_weaponinfo(i);
934 float w = want_weapon("g_start_weapon_", e, FALSE);
936 WEPSET_OR_AW(start_weapons, i);
938 WEPSET_OR_AW(start_weapons_default, i);
940 WEPSET_OR_AW(start_weapons_defaultmask, i);
944 if(!cvar("g_use_ammunition"))
945 start_items |= IT_UNLIMITED_AMMO;
947 if(cvar("g_nexball"))
948 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
952 start_ammo_cells = cvar("g_minstagib_ammo_start");
953 start_ammo_fuel = cvar("g_start_ammo_fuel");
955 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
957 start_ammo_rockets = 999;
958 start_ammo_shells = 999;
959 start_ammo_cells = 999;
960 start_ammo_nails = 999;
961 start_ammo_fuel = 999;
967 start_ammo_shells = cvar("g_lms_start_ammo_shells");
968 start_ammo_nails = cvar("g_lms_start_ammo_nails");
969 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
970 start_ammo_cells = cvar("g_lms_start_ammo_cells");
971 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
975 start_ammo_shells = cvar("g_start_ammo_shells");
976 start_ammo_nails = cvar("g_start_ammo_nails");
977 start_ammo_rockets = cvar("g_start_ammo_rockets");
978 start_ammo_cells = cvar("g_start_ammo_cells");
979 start_ammo_fuel = cvar("g_start_ammo_fuel");
985 start_health = cvar("g_lms_start_health");
986 start_armorvalue = cvar("g_lms_start_armor");
991 warmup_start_ammo_shells = start_ammo_shells;
992 warmup_start_ammo_nails = start_ammo_nails;
993 warmup_start_ammo_rockets = start_ammo_rockets;
994 warmup_start_ammo_cells = start_ammo_cells;
995 warmup_start_ammo_fuel = start_ammo_fuel;
996 warmup_start_health = start_health;
997 warmup_start_armorvalue = start_armorvalue;
998 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
999 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
1000 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
1002 if (!g_weaponarena && !g_minstagib && !g_ca)
1004 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1005 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1006 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1007 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1008 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1009 warmup_start_health = cvar("g_warmup_start_health");
1010 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1011 WEPSET_CLEAR_A(warmup_start_weapons);
1012 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1014 e = get_weaponinfo(i);
1015 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
1017 WEPSET_OR_AW(warmup_start_weapons, i);
1019 WEPSET_OR_AW(warmup_start_weapons_default, i);
1021 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
1027 start_items |= IT_JETPACK;
1029 MUTATOR_CALLHOOK(SetStartItems);
1031 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
1033 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1034 start_items |= IT_FUEL_REGEN;
1035 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1036 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1039 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1041 e = get_weaponinfo(i);
1042 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
1043 weapon_action(i, WR_PRECACHE);
1046 start_ammo_shells = max(0, start_ammo_shells);
1047 start_ammo_nails = max(0, start_ammo_nails);
1048 start_ammo_cells = max(0, start_ammo_cells);
1049 start_ammo_rockets = max(0, start_ammo_rockets);
1050 start_ammo_fuel = max(0, start_ammo_fuel);
1052 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1053 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1054 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1055 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1056 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1060 float g_bugrigs_planar_movement;
1061 float g_bugrigs_planar_movement_car_jumping;
1062 float g_bugrigs_reverse_spinning;
1063 float g_bugrigs_reverse_speeding;
1064 float g_bugrigs_reverse_stopping;
1065 float g_bugrigs_air_steering;
1066 float g_bugrigs_angle_smoothing;
1067 float g_bugrigs_friction_floor;
1068 float g_bugrigs_friction_brake;
1069 float g_bugrigs_friction_air;
1070 float g_bugrigs_accel;
1071 float g_bugrigs_speed_ref;
1072 float g_bugrigs_speed_pow;
1073 float g_bugrigs_steer;
1075 float g_touchexplode;
1076 float g_touchexplode_radius;
1077 float g_touchexplode_damage;
1078 float g_touchexplode_edgedamage;
1079 float g_touchexplode_force;
1086 float sv_pitch_fixyaw;
1088 string GetGametype(); // g_world.qc
1089 void readlevelcvars(void)
1091 g_minstagib = cvar("g_minstagib");
1093 // load ALL the mutators
1094 if(cvar("g_dodging"))
1095 MUTATOR_ADD(mutator_dodging);
1096 if(cvar("g_spawn_near_teammate"))
1097 MUTATOR_ADD(mutator_spawn_near_teammate);
1100 if(cvar("g_invincible_projectiles"))
1101 MUTATOR_ADD(mutator_invincibleprojectiles);
1102 if(cvar("g_new_toys"))
1103 MUTATOR_ADD(mutator_new_toys);
1105 MUTATOR_ADD(mutator_nix);
1106 if(cvar("g_rocket_flying"))
1107 MUTATOR_ADD(mutator_rocketflying);
1108 if(cvar("g_vampire"))
1109 MUTATOR_ADD(mutator_vampire);
1112 // is this a mutator? is this a mode?
1113 if(cvar("g_sandbox"))
1114 MUTATOR_ADD(sandbox);
1116 if(cvar("sv_allow_fullbright"))
1117 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1119 g_bugrigs = cvar("g_bugrigs");
1120 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1121 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1122 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1123 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1124 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1125 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1126 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1127 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1128 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1129 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1130 g_bugrigs_accel = cvar("g_bugrigs_accel");
1131 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1132 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1133 g_bugrigs_steer = cvar("g_bugrigs_steer");
1135 g_touchexplode = cvar("g_touchexplode");
1136 g_touchexplode_radius = cvar("g_touchexplode_radius");
1137 g_touchexplode_damage = cvar("g_touchexplode_damage");
1138 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1139 g_touchexplode_force = cvar("g_touchexplode_force");
1141 #ifdef ALLOW_FORCEMODELS
1142 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1145 sv_clones = cvar("sv_clones");
1146 sv_gentle = cvar("sv_gentle");
1147 sv_foginterval = cvar("sv_foginterval");
1148 g_cloaked = cvar("g_cloaked");
1150 g_cloaked = 1; // always enable cloak in CTS
1151 g_jump_grunt = cvar("g_jump_grunt");
1152 g_footsteps = cvar("g_footsteps");
1153 g_grappling_hook = cvar("g_grappling_hook");
1154 g_jetpack = cvar("g_jetpack");
1155 g_midair = cvar("g_midair");
1156 g_norecoil = cvar("g_norecoil");
1157 g_bloodloss = cvar("g_bloodloss");
1158 sv_maxidle = cvar("sv_maxidle");
1159 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1160 g_ctf_reverse = cvar("g_ctf_reverse");
1161 sv_autotaunt = cvar("sv_autotaunt");
1162 sv_taunt = cvar("sv_taunt");
1164 inWarmupStage = cvar("g_warmup");
1165 g_warmup_limit = cvar("g_warmup_limit");
1166 g_warmup_allguns = cvar("g_warmup_allguns");
1167 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1169 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1170 inWarmupStage = 0; // these modes cannot work together, sorry
1172 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1173 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1174 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1175 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1176 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1177 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1178 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1179 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1180 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1181 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1182 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1183 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1184 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1185 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1187 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1188 g_weaponratefactor = cvar("g_weaponratefactor");
1189 g_weapondamagefactor = cvar("g_weapondamagefactor");
1190 g_weaponforcefactor = cvar("g_weaponforcefactor");
1191 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1193 g_pickup_shells = cvar("g_pickup_shells");
1194 g_pickup_shells_max = cvar("g_pickup_shells_max");
1195 g_pickup_nails = cvar("g_pickup_nails");
1196 g_pickup_nails_max = cvar("g_pickup_nails_max");
1197 g_pickup_rockets = cvar("g_pickup_rockets");
1198 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1199 g_pickup_cells = cvar("g_pickup_cells");
1200 g_pickup_cells_max = cvar("g_pickup_cells_max");
1201 g_pickup_fuel = cvar("g_pickup_fuel");
1202 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1203 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1204 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1205 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1206 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1207 g_pickup_armormedium = cvar("g_pickup_armormedium");
1208 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1209 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1210 g_pickup_armorbig = cvar("g_pickup_armorbig");
1211 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1212 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1213 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1214 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1215 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1216 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1217 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1218 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1219 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1220 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1221 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1222 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1223 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1224 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1225 g_pickup_healthmega = cvar("g_pickup_healthmega");
1226 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1227 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1229 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1230 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1232 g_pinata = cvar("g_pinata");
1234 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1236 g_weapon_stay = cvar("g_weapon_stay");
1238 if not(inWarmupStage && !g_ca)
1239 game_starttime = cvar("g_start_delay");
1241 sv_pitch_min = cvar("sv_pitch_min");
1242 sv_pitch_max = cvar("sv_pitch_max");
1243 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1245 readplayerstartcvars();
1251 string precache_sound (string s) = #19;
1252 float precache_sound_index (string s) = #19;
1254 #define SND_VOLUME 1
1255 #define SND_ATTENUATION 2
1256 #define SND_LARGEENTITY 8
1257 #define SND_LARGESOUND 16
1259 float sound_allowed(float dest, entity e)
1261 // sounds from world may always pass
1264 if (e.classname == "body")
1266 else if (e.realowner && e.realowner != e)
1268 else if (e.owner && e.owner != e)
1273 // sounds to self may always pass
1274 if (dest == MSG_ONE)
1275 if (e == msg_entity)
1277 // sounds by players can be removed
1278 if (autocvar_bot_sound_monopoly)
1279 if (clienttype(e) == CLIENTTYPE_REAL)
1281 // anything else may pass
1285 #ifdef COMPAT_XON010_CHANNELS
1286 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1287 void sound(entity e, float chan, string samp, float vol, float atten)
1289 if (!sound_allowed(MSG_BROADCAST, e))
1291 builtin_sound(e, chan, samp, vol, atten);
1295 void sound(entity e, float chan, string samp, float vol, float atten)
1297 if (!sound_allowed(MSG_BROADCAST, e))
1299 sound7(e, chan, samp, vol, atten, 0, 0);
1303 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1307 if (!sound_allowed(dest, e))
1310 entno = num_for_edict(e);
1311 idx = precache_sound_index(samp);
1316 atten = floor(atten * 64);
1317 vol = floor(vol * 255);
1320 sflags |= SND_VOLUME;
1322 sflags |= SND_ATTENUATION;
1323 if (entno >= 8192 || chan < 0 || chan > 7)
1324 sflags |= SND_LARGEENTITY;
1326 sflags |= SND_LARGESOUND;
1328 WriteByte(dest, SVC_SOUND);
1329 WriteByte(dest, sflags);
1330 if (sflags & SND_VOLUME)
1331 WriteByte(dest, vol);
1332 if (sflags & SND_ATTENUATION)
1333 WriteByte(dest, atten);
1334 if (sflags & SND_LARGEENTITY)
1336 WriteShort(dest, entno);
1337 WriteByte(dest, chan);
1341 WriteShort(dest, entno * 8 + chan);
1343 if (sflags & SND_LARGESOUND)
1344 WriteShort(dest, idx);
1346 WriteByte(dest, idx);
1348 WriteCoord(dest, o_x);
1349 WriteCoord(dest, o_y);
1350 WriteCoord(dest, o_z);
1352 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1356 if (!sound_allowed(dest, e))
1359 o = e.origin + 0.5 * (e.mins + e.maxs);
1360 soundtoat(dest, e, o, chan, samp, vol, atten);
1362 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1364 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1366 void stopsoundto(float dest, entity e, float chan)
1370 if (!sound_allowed(dest, e))
1373 entno = num_for_edict(e);
1375 if (entno >= 8192 || chan < 0 || chan > 7)
1378 idx = precache_sound_index("misc/null.wav");
1379 sflags = SND_LARGEENTITY;
1381 sflags |= SND_LARGESOUND;
1382 WriteByte(dest, SVC_SOUND);
1383 WriteByte(dest, sflags);
1384 WriteShort(dest, entno);
1385 WriteByte(dest, chan);
1386 if (sflags & SND_LARGESOUND)
1387 WriteShort(dest, idx);
1389 WriteByte(dest, idx);
1390 WriteCoord(dest, e.origin_x);
1391 WriteCoord(dest, e.origin_y);
1392 WriteCoord(dest, e.origin_z);
1396 WriteByte(dest, SVC_STOPSOUND);
1397 WriteShort(dest, entno * 8 + chan);
1400 void stopsound(entity e, float chan)
1402 if (!sound_allowed(MSG_BROADCAST, e))
1405 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1406 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1409 void play2(entity e, string filename)
1411 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1413 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1416 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1418 float spamsound(entity e, float chan, string samp, float vol, float atten)
1420 if (!sound_allowed(MSG_BROADCAST, e))
1423 if (time > e.spamtime)
1426 sound(e, chan, samp, vol, atten);
1432 void play2team(float t, string filename)
1436 if (autocvar_bot_sound_monopoly)
1439 FOR_EACH_REALPLAYER(head)
1442 play2(head, filename);
1446 void play2all(string samp)
1448 if (autocvar_bot_sound_monopoly)
1451 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1454 void PrecachePlayerSounds(string f);
1455 void precache_playermodel(string m)
1457 float globhandle, i, n;
1460 if(substring(m, -9,5) == "_lod1")
1462 if(substring(m, -9,5) == "_lod2")
1465 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1468 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1472 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1475 n = search_getsize(globhandle);
1476 for (i = 0; i < n; ++i)
1478 //print(search_getfilename(globhandle, i), "\n");
1479 f = search_getfilename(globhandle, i);
1480 PrecachePlayerSounds(f);
1482 search_end(globhandle);
1484 void precache_all_playermodels(string pattern)
1486 float globhandle, i, n;
1489 globhandle = search_begin(pattern, TRUE, FALSE);
1492 n = search_getsize(globhandle);
1493 for (i = 0; i < n; ++i)
1495 //print(search_getfilename(globhandle, i), "\n");
1496 f = search_getfilename(globhandle, i);
1497 precache_playermodel(f);
1499 search_end(globhandle);
1504 // gamemode related things
1505 precache_model ("models/misc/chatbubble.spr");
1508 precache_model ("models/runematch/curse.mdl");
1509 precache_model ("models/runematch/rune.mdl");
1512 #ifdef TTURRETS_ENABLED
1513 if (autocvar_g_turrets)
1517 // Precache all player models if desired
1518 if (autocvar_sv_precacheplayermodels)
1520 PrecachePlayerSounds("sound/player/default.sounds");
1521 precache_all_playermodels("models/player/*.zym");
1522 precache_all_playermodels("models/player/*.dpm");
1523 precache_all_playermodels("models/player/*.md3");
1524 precache_all_playermodels("models/player/*.psk");
1525 precache_all_playermodels("models/player/*.iqm");
1528 if (autocvar_sv_defaultcharacter)
1531 s = autocvar_sv_defaultplayermodel_red;
1533 precache_playermodel(s);
1534 s = autocvar_sv_defaultplayermodel_blue;
1536 precache_playermodel(s);
1537 s = autocvar_sv_defaultplayermodel_yellow;
1539 precache_playermodel(s);
1540 s = autocvar_sv_defaultplayermodel_pink;
1542 precache_playermodel(s);
1543 s = autocvar_sv_defaultplayermodel;
1545 precache_playermodel(s);
1550 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1551 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1554 // gore and miscellaneous sounds
1555 //precache_sound ("misc/h2ohit.wav");
1556 precache_model ("models/hook.md3");
1557 precache_sound ("misc/armorimpact.wav");
1558 precache_sound ("misc/bodyimpact1.wav");
1559 precache_sound ("misc/bodyimpact2.wav");
1560 precache_sound ("misc/gib.wav");
1561 precache_sound ("misc/gib_splat01.wav");
1562 precache_sound ("misc/gib_splat02.wav");
1563 precache_sound ("misc/gib_splat03.wav");
1564 precache_sound ("misc/gib_splat04.wav");
1565 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1566 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1567 precache_sound ("misc/null.wav");
1568 precache_sound ("misc/spawn.wav");
1569 precache_sound ("misc/talk.wav");
1570 precache_sound ("misc/teleport.wav");
1571 precache_sound ("misc/poweroff.wav");
1572 precache_sound ("player/lava.wav");
1573 precache_sound ("player/slime.wav");
1576 precache_sound ("misc/jetpack_fly.wav");
1578 precache_model ("models/sprites/0.spr32");
1579 precache_model ("models/sprites/1.spr32");
1580 precache_model ("models/sprites/2.spr32");
1581 precache_model ("models/sprites/3.spr32");
1582 precache_model ("models/sprites/4.spr32");
1583 precache_model ("models/sprites/5.spr32");
1584 precache_model ("models/sprites/6.spr32");
1585 precache_model ("models/sprites/7.spr32");
1586 precache_model ("models/sprites/8.spr32");
1587 precache_model ("models/sprites/9.spr32");
1588 precache_model ("models/sprites/10.spr32");
1590 // common weapon precaches
1591 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1592 precache_sound ("weapons/weapon_switch.wav");
1593 precache_sound ("weapons/weaponpickup.wav");
1594 precache_sound ("weapons/unavailable.wav");
1595 precache_sound ("weapons/dryfire.wav");
1596 if (g_grappling_hook)
1598 precache_sound ("weapons/hook_fire.wav"); // hook
1599 precache_sound ("weapons/hook_impact.wav"); // hook
1602 if(autocvar_sv_precacheweapons)
1604 //precache weapon models/sounds
1607 while (wep <= WEP_LAST)
1609 weapon_action(wep, WR_PRECACHE);
1614 precache_model("models/elaser.mdl");
1615 precache_model("models/laser.mdl");
1616 precache_model("models/ebomb.mdl");
1619 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1621 if (!self.noise && self.music) // quake 3 uses the music field
1622 self.noise = self.music;
1624 // plays music for the level if there is any
1627 precache_sound (self.noise);
1628 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1633 // sorry, but using \ in macros breaks line numbers
1634 #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
1635 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1636 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1639 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1641 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
1644 WRITESPECTATABLE_MSG_ONE({
1645 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1646 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1647 WriteByte(MSG_ONE, id);
1648 WriteString(MSG_ONE, s);
1649 if (id != 0 && s != "")
1651 WriteByte(MSG_ONE, duration);
1652 WriteByte(MSG_ONE, countdown_num);
1657 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1659 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1661 // WARNING: this kills the trace globals
1662 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1663 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1665 #define INITPRIO_FIRST 0
1666 #define INITPRIO_GAMETYPE 0
1667 #define INITPRIO_GAMETYPE_FALLBACK 1
1668 #define INITPRIO_FINDTARGET 10
1669 #define INITPRIO_DROPTOFLOOR 20
1670 #define INITPRIO_SETLOCATION 90
1671 #define INITPRIO_LINKDOORS 91
1672 #define INITPRIO_LAST 99
1674 .void(void) initialize_entity;
1675 .float initialize_entity_order;
1676 .entity initialize_entity_next;
1677 entity initialize_entity_first;
1679 void make_safe_for_remove(entity e)
1681 if (e.initialize_entity)
1684 for (ent = initialize_entity_first; ent; )
1686 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1688 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1689 // skip it in linked list
1692 prev.initialize_entity_next = ent.initialize_entity_next;
1693 ent = prev.initialize_entity_next;
1697 initialize_entity_first = ent.initialize_entity_next;
1698 ent = initialize_entity_first;
1704 ent = ent.initialize_entity_next;
1710 void objerror(string s)
1712 make_safe_for_remove(self);
1713 builtin_objerror(s);
1716 .float remove_except_protected_forbidden;
1717 void remove_except_protected(entity e)
1719 if(e.remove_except_protected_forbidden)
1720 error("not allowed to remove this at this point");
1724 void remove_unsafely(entity e)
1726 if(e.classname == "spike")
1727 error("Removing spikes is forbidden (crylink bug), please report");
1731 void remove_safely(entity e)
1733 make_safe_for_remove(e);
1737 void InitializeEntity(entity e, void(void) func, float order)
1741 if (!e || e.initialize_entity)
1743 // make a proxy initializer entity
1747 e.classname = "initialize_entity";
1751 e.initialize_entity = func;
1752 e.initialize_entity_order = order;
1754 cur = initialize_entity_first;
1757 if (!cur || cur.initialize_entity_order > order)
1759 // insert between prev and cur
1761 prev.initialize_entity_next = e;
1763 initialize_entity_first = e;
1764 e.initialize_entity_next = cur;
1768 cur = cur.initialize_entity_next;
1771 void InitializeEntitiesRun()
1774 startoflist = initialize_entity_first;
1775 initialize_entity_first = world;
1776 remove = remove_except_protected;
1777 for (self = startoflist; self; self = self.initialize_entity_next)
1779 self.remove_except_protected_forbidden = 1;
1781 for (self = startoflist; self; )
1784 var void(void) func;
1785 e = self.initialize_entity_next;
1786 func = self.initialize_entity;
1787 self.initialize_entity_order = 0;
1788 self.initialize_entity = func_null;
1789 self.initialize_entity_next = world;
1790 self.remove_except_protected_forbidden = 0;
1791 if (self.classname == "initialize_entity")
1795 builtin_remove(self);
1798 //dprint("Delayed initialization: ", self.classname, "\n");
1799 if(func != func_null)
1804 backtrace(strcat("Null function in: ", self.classname, "\n"));
1808 remove = remove_unsafely;
1811 .float uncustomizeentityforclient_set;
1812 .void(void) uncustomizeentityforclient;
1813 void(void) SUB_Nullpointer = #0;
1814 void UncustomizeEntitiesRun()
1818 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1819 self.uncustomizeentityforclient();
1822 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1824 e.customizeentityforclient = customizer;
1825 e.uncustomizeentityforclient = uncustomizer;
1826 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1830 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1833 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1837 if (e.classname == "")
1838 e.classname = "net_linked";
1840 if (e.model == "" || self.modelindex == 0)
1844 setmodel(e, "null");
1848 e.SendEntity = sendfunc;
1849 e.SendFlags = 0xFFFFFF;
1852 e.effects |= EF_NODEPTHTEST;
1856 e.nextthink = time + dt;
1857 e.think = SUB_Remove;
1861 void adaptor_think2touch()
1870 void adaptor_think2use()
1882 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1884 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
1885 self.projectiledeathtype |= HITTYPE_SPLASH;
1886 adaptor_think2use();
1889 // deferred dropping
1890 void DropToFloor_Handler()
1892 builtin_droptofloor();
1893 self.dropped_origin = self.origin;
1898 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1903 float trace_hits_box_a0, trace_hits_box_a1;
1905 float trace_hits_box_1d(float end, float thmi, float thma)
1909 // just check if x is in range
1917 // do the trace with respect to x
1918 // 0 -> end has to stay in thmi -> thma
1919 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1920 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1921 if (trace_hits_box_a0 > trace_hits_box_a1)
1927 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1932 // now it is a trace from 0 to end
1934 trace_hits_box_a0 = 0;
1935 trace_hits_box_a1 = 1;
1937 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1939 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1941 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1947 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1949 return trace_hits_box(start, end, thmi - ma, thma - mi);
1952 float SUB_NoImpactCheck()
1954 // zero hitcontents = this is not the real impact, but either the
1955 // mirror-impact of something hitting the projectile instead of the
1956 // projectile hitting the something, or a touchareagrid one. Neither of
1957 // these stop the projectile from moving, so...
1958 if(trace_dphitcontents == 0)
1960 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1961 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)));
1964 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1966 if (other == world && self.size != '0 0 0')
1969 tic = self.velocity * sys_frametime;
1970 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1971 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1972 if (trace_fraction >= 1)
1974 dprint("Odd... did not hit...?\n");
1976 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1978 dprint("Detected and prevented the sky-grapple bug.\n");
1986 #define SUB_OwnerCheck() (other && (other == self.owner))
1988 void RemoveGrapplingHook(entity pl);
1989 void W_Crylink_Dequeue(entity e);
1990 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1992 if(SUB_OwnerCheck())
1994 if(SUB_NoImpactCheck())
1996 if(self.classname == "grapplinghook")
1997 RemoveGrapplingHook(self.realowner);
1998 else if(self.classname == "spike")
2000 W_Crylink_Dequeue(self);
2007 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2008 UpdateCSQCProjectile(self);
2011 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2013 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
2014 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
2016 void URI_Get_Callback(float id, float status, string data)
2018 if(url_URI_Get_Callback(id, status, data))
2022 else if (id == URI_GET_DISCARD)
2026 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
2029 Curl_URI_Get_Callback(id, status, data);
2031 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2034 OnlineBanList_URI_Get_Callback(id, status, data);
2038 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2042 string uid2name(string myuid) {
2044 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2046 // FIXME remove this later after 0.6 release
2047 // convert old style broken records to correct style
2050 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2053 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2054 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2059 s = "^1Unregistered Player";
2063 float race_readTime(string map, float pos)
2071 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2074 string race_readUID(string map, float pos)
2082 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2085 float race_readPos(string map, float t) {
2087 for (i = 1; i <= RANKINGS_CNT; ++i)
2088 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2091 return 0; // pos is zero if unranked
2094 void race_writeTime(string map, float t, string myuid)
2103 newpos = race_readPos(map, t);
2106 for(i = 1; i <= RANKINGS_CNT; ++i)
2108 if(race_readUID(map, i) == myuid)
2111 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2112 for (i = prevpos; i > newpos; --i) {
2113 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2114 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2116 } else { // player has no ranked record yet
2117 for (i = RANKINGS_CNT; i > newpos; --i) {
2118 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2119 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2123 // store new time itself
2124 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2125 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2128 string race_readName(string map, float pos)
2136 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2139 string race_placeName(float pos) {
2140 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2142 if(mod(pos, 10) == 1)
2143 return strcat(ftos(pos), "st");
2144 else if(mod(pos, 10) == 2)
2145 return strcat(ftos(pos), "nd");
2146 else if(mod(pos, 10) == 3)
2147 return strcat(ftos(pos), "rd");
2149 return strcat(ftos(pos), "th");
2152 return strcat(ftos(pos), "th");
2155 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2158 vector start, org, delta, end, enddown, mstart;
2161 m = e.dphitcontentsmask;
2162 e.dphitcontentsmask = goodcontents | badcontents;
2165 delta = world.maxs - world.mins;
2167 for (i = 0; i < attempts; ++i)
2169 start_x = org_x + random() * delta_x;
2170 start_y = org_y + random() * delta_y;
2171 start_z = org_z + random() * delta_z;
2173 // rule 1: start inside world bounds, and outside
2174 // solid, and don't start from somewhere where you can
2175 // fall down to evil
2176 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2177 if (trace_fraction >= 1)
2179 if (trace_startsolid)
2181 if (trace_dphitcontents & badcontents)
2183 if (trace_dphitq3surfaceflags & badsurfaceflags)
2186 // rule 2: if we are too high, lower the point
2187 if (trace_fraction * delta_z > maxaboveground)
2188 start = trace_endpos + '0 0 1' * maxaboveground;
2189 enddown = trace_endpos;
2191 // rule 3: make sure we aren't outside the map. This only works
2192 // for somewhat well formed maps. A good rule of thumb is that
2193 // the map should have a convex outside hull.
2194 // these can be traceLINES as we already verified the starting box
2195 mstart = start + 0.5 * (e.mins + e.maxs);
2196 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2197 if (trace_fraction >= 1)
2199 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2200 if (trace_fraction >= 1)
2202 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2203 if (trace_fraction >= 1)
2205 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2206 if (trace_fraction >= 1)
2208 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2209 if (trace_fraction >= 1)
2212 // rule 4: we must "see" some spawnpoint
2213 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2214 if(checkpvs(mstart, sp))
2218 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2219 if(checkpvs(mstart, sp))
2225 // find a random vector to "look at"
2226 end_x = org_x + random() * delta_x;
2227 end_y = org_y + random() * delta_y;
2228 end_z = org_z + random() * delta_z;
2229 end = start + normalize(end - start) * vlen(delta);
2231 // rule 4: start TO end must not be too short
2232 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2233 if (trace_startsolid)
2235 if (trace_fraction < minviewdistance / vlen(delta))
2238 // rule 5: don't want to look at sky
2239 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2242 // rule 6: we must not end up in trigger_hurt
2243 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2249 e.dphitcontentsmask = m;
2253 setorigin(e, start);
2254 e.angles = vectoangles(end - start);
2255 dprint("Needed ", ftos(i + 1), " attempts\n");
2262 float zcurveparticles_effectno;
2263 vector zcurveparticles_start;
2264 float zcurveparticles_spd;
2266 void endzcurveparticles()
2268 if(zcurveparticles_effectno)
2271 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2273 zcurveparticles_effectno = 0;
2276 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2278 spd = bound(0, floor(spd / 16), 32767);
2279 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2281 endzcurveparticles();
2282 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2283 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2284 WriteShort(MSG_BROADCAST, effectno);
2285 WriteCoord(MSG_BROADCAST, start_x);
2286 WriteCoord(MSG_BROADCAST, start_y);
2287 WriteCoord(MSG_BROADCAST, start_z);
2288 zcurveparticles_effectno = effectno;
2289 zcurveparticles_start = start;
2292 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2293 WriteCoord(MSG_BROADCAST, end_x);
2294 WriteCoord(MSG_BROADCAST, end_y);
2295 WriteCoord(MSG_BROADCAST, end_z);
2296 WriteCoord(MSG_BROADCAST, end_dz);
2297 zcurveparticles_spd = spd;
2300 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2303 vector vecxy, velxy;
2305 vecxy = end - start;
2310 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2312 endzcurveparticles();
2313 trailparticles(world, effectno, start, end);
2317 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2318 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2321 void write_recordmarker(entity pl, float tstart, float dt)
2323 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2325 // also write a marker into demo files for demotc-race-record-extractor to find
2328 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2329 " ", ftos(tstart), " ", ftos(dt), "\n"));
2332 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2345 if(allowcenter) // 2: allow center handedness
2358 if(allowcenter) // 2: allow center handedness
2374 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2379 if (autocvar_g_shootfromeye)
2392 else if (autocvar_g_shootfromcenter)
2397 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2407 else if (autocvar_g_shootfromclient)
2409 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2414 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2416 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2420 void attach_sameorigin(entity e, entity to, string tag)
2422 vector org, t_forward, t_left, t_up, e_forward, e_up;
2429 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2430 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2431 t_forward = v_forward * tagscale;
2432 t_left = v_right * -tagscale;
2433 t_up = v_up * tagscale;
2435 e.origin_x = org * t_forward;
2436 e.origin_y = org * t_left;
2437 e.origin_z = org * t_up;
2439 // current forward and up directions
2440 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2441 e.angles = AnglesTransform_FromVAngles(e.angles);
2443 e.angles = AnglesTransform_FromAngles(e.angles);
2444 fixedmakevectors(e.angles);
2446 // untransform forward, up!
2447 e_forward_x = v_forward * t_forward;
2448 e_forward_y = v_forward * t_left;
2449 e_forward_z = v_forward * t_up;
2450 e_up_x = v_up * t_forward;
2451 e_up_y = v_up * t_left;
2452 e_up_z = v_up * t_up;
2454 e.angles = fixedvectoangles2(e_forward, e_up);
2455 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2456 e.angles = AnglesTransform_ToVAngles(e.angles);
2458 e.angles = AnglesTransform_ToAngles(e.angles);
2460 setattachment(e, to, tag);
2461 setorigin(e, e.origin);
2464 void detach_sameorigin(entity e)
2467 org = gettaginfo(e, 0);
2468 e.angles = fixedvectoangles2(v_forward, v_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, world, "");
2475 setorigin(e, e.origin);
2478 void follow_sameorigin(entity e, entity to)
2480 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2481 e.aiment = to; // make the hole follow bmodel
2482 e.punchangle = to.angles; // the original angles of bmodel
2483 e.view_ofs = e.origin - to.origin; // relative origin
2484 e.v_angle = e.angles - to.angles; // relative angles
2487 void unfollow_sameorigin(entity e)
2489 e.movetype = MOVETYPE_NONE;
2492 entity gettaginfo_relative_ent;
2493 vector gettaginfo_relative(entity e, float tag)
2495 if (!gettaginfo_relative_ent)
2497 gettaginfo_relative_ent = spawn();
2498 gettaginfo_relative_ent.effects = EF_NODRAW;
2500 gettaginfo_relative_ent.model = e.model;
2501 gettaginfo_relative_ent.modelindex = e.modelindex;
2502 gettaginfo_relative_ent.frame = e.frame;
2503 return gettaginfo(gettaginfo_relative_ent, tag);
2506 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2510 if (pl.soundentity.cnt & p)
2512 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2513 pl.soundentity.cnt |= p;
2516 void SoundEntity_StopSound(entity pl, float chan)
2520 if (pl.soundentity.cnt & p)
2522 stopsoundto(MSG_ALL, pl.soundentity, chan);
2523 pl.soundentity.cnt &~= p;
2527 void SoundEntity_Attach(entity pl)
2529 pl.soundentity = spawn();
2530 pl.soundentity.classname = "soundentity";
2531 pl.soundentity.owner = pl;
2532 setattachment(pl.soundentity, pl, "");
2533 setmodel(pl.soundentity, "null");
2536 void SoundEntity_Detach(entity pl)
2539 for (i = 0; i <= 7; ++i)
2540 SoundEntity_StopSound(pl, i);
2545 float modeleffect_SendEntity(entity to, float sf)
2548 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2551 if(self.velocity != '0 0 0')
2553 if(self.angles != '0 0 0')
2555 if(self.avelocity != '0 0 0')
2558 WriteByte(MSG_ENTITY, f);
2559 WriteShort(MSG_ENTITY, self.modelindex);
2560 WriteByte(MSG_ENTITY, self.skin);
2561 WriteByte(MSG_ENTITY, self.frame);
2562 WriteCoord(MSG_ENTITY, self.origin_x);
2563 WriteCoord(MSG_ENTITY, self.origin_y);
2564 WriteCoord(MSG_ENTITY, self.origin_z);
2567 WriteCoord(MSG_ENTITY, self.velocity_x);
2568 WriteCoord(MSG_ENTITY, self.velocity_y);
2569 WriteCoord(MSG_ENTITY, self.velocity_z);
2573 WriteCoord(MSG_ENTITY, self.angles_x);
2574 WriteCoord(MSG_ENTITY, self.angles_y);
2575 WriteCoord(MSG_ENTITY, self.angles_z);
2579 WriteCoord(MSG_ENTITY, self.avelocity_x);
2580 WriteCoord(MSG_ENTITY, self.avelocity_y);
2581 WriteCoord(MSG_ENTITY, self.avelocity_z);
2583 WriteShort(MSG_ENTITY, self.scale * 256.0);
2584 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2585 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2586 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2587 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2592 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)
2597 e.classname = "modeleffect";
2605 e.teleport_time = t1;
2609 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2613 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2616 sz = max(e.scale, e.scale2);
2617 setsize(e, e.mins * sz, e.maxs * sz);
2618 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2621 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2623 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2626 float randombit(float bits)
2628 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2637 for(f = 1; f <= bits; f *= 2)
2646 r = (r - 1) / (n - 1);
2653 float randombits(float bits, float k, float error_return)
2657 while(k > 0 && bits != r)
2659 r += randombit(bits - r);
2668 void randombit_test(float bits, float iter)
2672 print(ftos(randombit(bits)), "\n");
2677 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2679 if(halflifedist > 0)
2680 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2681 else if(halflifedist < 0)
2682 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2691 #define cvar_string_normal builtin_cvar_string
2692 #define cvar_normal builtin_cvar
2694 string cvar_string_normal(string n)
2696 if not(cvar_type(n) & 1)
2697 backtrace(strcat("Attempt to access undefined cvar: ", n));
2698 return builtin_cvar_string(n);
2701 float cvar_normal(string n)
2703 return stof(cvar_string_normal(n));
2706 #define cvar_set_normal builtin_cvar_set
2714 oself.think = SUB_Remove;
2715 oself.nextthink = time;
2721 Execute func() after time + fdelay.
2722 self when func is executed = self when defer is called
2724 void defer(float fdelay, void() func)
2731 e.think = defer_think;
2732 e.nextthink = time + fdelay;
2735 .string aiment_classname;
2736 .float aiment_deadflag;
2737 void SetMovetypeFollow(entity ent, entity e)
2739 // FIXME this may not be warpzone aware
2740 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2741 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.
2742 ent.aiment = e; // make the hole follow bmodel
2743 ent.punchangle = e.angles; // the original angles of bmodel
2744 ent.view_ofs = ent.origin - e.origin; // relative origin
2745 ent.v_angle = ent.angles - e.angles; // relative angles
2746 ent.aiment_classname = strzone(e.classname);
2747 ent.aiment_deadflag = e.deadflag;
2749 void UnsetMovetypeFollow(entity ent)
2751 ent.movetype = MOVETYPE_FLY;
2752 PROJECTILE_MAKETRIGGER(ent);
2755 float LostMovetypeFollow(entity ent)
2758 if(ent.movetype != MOVETYPE_FOLLOW)
2764 if(ent.aiment.classname != ent.aiment_classname)
2766 if(ent.aiment.deadflag != ent.aiment_deadflag)
2772 float isPushable(entity e)
2779 case "droppedweapon":
2780 case "keepawayball":
2781 case "nexball_basketball":
2782 case "nexball_football":
2784 case "bullet": // antilagged bullets can't hit this either
2787 if (e.projectiledeathtype)