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 float g_weaponarena_random;
744 float g_weaponarena_random_with_laser;
745 string g_weaponarena_list;
746 float g_weaponspeedfactor;
747 float g_weaponratefactor;
748 float g_weapondamagefactor;
749 float g_weaponforcefactor;
750 float g_weaponspreadfactor;
754 float start_ammo_shells;
755 float start_ammo_nails;
756 float start_ammo_rockets;
757 float start_ammo_cells;
758 float start_ammo_fuel;
760 float start_armorvalue;
761 float warmup_start_weapons;
762 float warmup_start_ammo_shells;
763 float warmup_start_ammo_nails;
764 float warmup_start_ammo_rockets;
765 float warmup_start_ammo_cells;
766 float warmup_start_ammo_fuel;
767 float warmup_start_health;
768 float warmup_start_armorvalue;
772 entity get_weaponinfo(float w);
774 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
776 var float i = weaponinfo.weapon;
781 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
783 if (t < 0) // "default" weapon selection
785 if (g_lms || g_ca || allguns)
786 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
790 t = (i == WEP_SHOTGUN);
792 t = 0; // weapon is set a few lines later
794 t = (i == WEP_LASER || i == WEP_SHOTGUN);
795 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
796 t |= (i == WEP_HOOK);
802 void readplayerstartcvars()
808 // initialize starting values for players
811 start_ammo_shells = 0;
812 start_ammo_nails = 0;
813 start_ammo_rockets = 0;
814 start_ammo_cells = 0;
815 start_health = cvar("g_balance_health_start");
816 start_armorvalue = cvar("g_balance_armor_start");
819 s = cvar_string("g_weaponarena");
820 if (s == "0" || s == "")
826 if (s == "0" || s == "")
832 // forcibly turn off weaponarena
836 g_weaponarena_list = "All Weapons";
837 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
839 e = get_weaponinfo(j);
840 g_weaponarena |= e.weapons;
841 weapon_action(e.weapon, WR_PRECACHE);
844 else if (s == "most")
846 g_weaponarena_list = "Most Weapons";
847 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
849 e = get_weaponinfo(j);
850 if (e.spawnflags & WEP_FLAG_NORMAL)
852 g_weaponarena |= e.weapons;
853 weapon_action(e.weapon, WR_PRECACHE);
857 else if (s == "none")
859 g_weaponarena_list = "No Weapons";
860 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
864 t = tokenize_console(s);
865 g_weaponarena_list = "";
866 for (i = 0; i < t; ++i)
869 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
871 e = get_weaponinfo(j);
874 g_weaponarena |= e.weapons;
875 weapon_action(e.weapon, WR_PRECACHE);
876 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
882 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
885 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
889 g_weaponarena_random = cvar("g_weaponarena_random");
891 g_weaponarena_random = 0;
892 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
896 start_weapons = g_weaponarena;
898 start_items |= IT_UNLIMITED_AMMO;
900 else if (g_minstagib)
903 start_armorvalue = 0;
904 start_weapons = WEPBIT_MINSTANEX;
905 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
906 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
908 if (g_minstagib_invis_alpha <= 0)
909 g_minstagib_invis_alpha = -1;
913 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
915 e = get_weaponinfo(i);
916 if(want_weapon("g_start_weapon_", e, FALSE))
917 start_weapons |= e.weapons;
921 if(!cvar("g_use_ammunition"))
922 start_items |= IT_UNLIMITED_AMMO;
926 start_ammo_cells = cvar("g_minstagib_ammo_start");
927 start_ammo_fuel = cvar("g_start_ammo_fuel");
929 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
931 start_ammo_rockets = 999;
932 start_ammo_shells = 999;
933 start_ammo_cells = 999;
934 start_ammo_nails = 999;
935 start_ammo_fuel = 999;
941 start_ammo_shells = cvar("g_lms_start_ammo_shells");
942 start_ammo_nails = cvar("g_lms_start_ammo_nails");
943 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
944 start_ammo_cells = cvar("g_lms_start_ammo_cells");
945 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
949 start_ammo_shells = cvar("g_start_ammo_shells");
950 start_ammo_nails = cvar("g_start_ammo_nails");
951 start_ammo_rockets = cvar("g_start_ammo_rockets");
952 start_ammo_cells = cvar("g_start_ammo_cells");
953 start_ammo_fuel = cvar("g_start_ammo_fuel");
959 start_health = cvar("g_lms_start_health");
960 start_armorvalue = cvar("g_lms_start_armor");
965 warmup_start_ammo_shells = start_ammo_shells;
966 warmup_start_ammo_nails = start_ammo_nails;
967 warmup_start_ammo_rockets = start_ammo_rockets;
968 warmup_start_ammo_cells = start_ammo_cells;
969 warmup_start_ammo_fuel = start_ammo_fuel;
970 warmup_start_health = start_health;
971 warmup_start_armorvalue = start_armorvalue;
972 warmup_start_weapons = start_weapons;
974 if (!g_weaponarena && !g_minstagib && !g_ca)
976 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
977 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
978 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
979 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
980 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
981 warmup_start_health = cvar("g_warmup_start_health");
982 warmup_start_armorvalue = cvar("g_warmup_start_armor");
983 warmup_start_weapons = 0;
984 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
986 e = get_weaponinfo(i);
987 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
988 warmup_start_weapons |= e.weapons;
993 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
995 g_grappling_hook = 0; // these two can't coexist, as they use the same button
996 start_items |= IT_FUEL_REGEN;
997 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
998 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1002 start_items |= IT_JETPACK;
1004 MUTATOR_CALLHOOK(SetStartItems);
1006 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1008 e = get_weaponinfo(i);
1009 if(e.weapons & (start_weapons | warmup_start_weapons))
1010 weapon_action(e.weapon, WR_PRECACHE);
1013 start_ammo_shells = max(0, start_ammo_shells);
1014 start_ammo_nails = max(0, start_ammo_nails);
1015 start_ammo_cells = max(0, start_ammo_cells);
1016 start_ammo_rockets = max(0, start_ammo_rockets);
1017 start_ammo_fuel = max(0, start_ammo_fuel);
1019 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1020 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1021 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1022 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1023 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1027 float g_bugrigs_planar_movement;
1028 float g_bugrigs_planar_movement_car_jumping;
1029 float g_bugrigs_reverse_spinning;
1030 float g_bugrigs_reverse_speeding;
1031 float g_bugrigs_reverse_stopping;
1032 float g_bugrigs_air_steering;
1033 float g_bugrigs_angle_smoothing;
1034 float g_bugrigs_friction_floor;
1035 float g_bugrigs_friction_brake;
1036 float g_bugrigs_friction_air;
1037 float g_bugrigs_accel;
1038 float g_bugrigs_speed_ref;
1039 float g_bugrigs_speed_pow;
1040 float g_bugrigs_steer;
1042 float g_touchexplode;
1043 float g_touchexplode_radius;
1044 float g_touchexplode_damage;
1045 float g_touchexplode_edgedamage;
1046 float g_touchexplode_force;
1053 float sv_pitch_fixyaw;
1055 string GetGametype(); // g_world.qc
1056 void readlevelcvars(void)
1058 // first load all the mutators
1059 if(cvar("g_invincible_projectiles"))
1060 MUTATOR_ADD(mutator_invincibleprojectiles);
1062 MUTATOR_ADD(mutator_nix);
1063 if(cvar("g_dodging"))
1064 MUTATOR_ADD(mutator_dodging);
1065 if(cvar("g_rocket_flying"))
1066 MUTATOR_ADD(mutator_rocketflying);
1067 if(cvar("g_vampire"))
1068 MUTATOR_ADD(mutator_vampire);
1069 if(cvar("g_spawn_near_teammate"))
1070 MUTATOR_ADD(mutator_spawn_near_teammate);
1072 // is this a mutator? is this a mode?
1073 if(cvar("g_sandbox"))
1074 MUTATOR_ADD(sandbox);
1076 if(cvar("sv_allow_fullbright"))
1077 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1079 g_bugrigs = cvar("g_bugrigs");
1080 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1081 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1082 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1083 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1084 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1085 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1086 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1087 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1088 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1089 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1090 g_bugrigs_accel = cvar("g_bugrigs_accel");
1091 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1092 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1093 g_bugrigs_steer = cvar("g_bugrigs_steer");
1095 g_touchexplode = cvar("g_touchexplode");
1096 g_touchexplode_radius = cvar("g_touchexplode_radius");
1097 g_touchexplode_damage = cvar("g_touchexplode_damage");
1098 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1099 g_touchexplode_force = cvar("g_touchexplode_force");
1101 #ifdef ALLOW_FORCEMODELS
1102 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1105 sv_clones = cvar("sv_clones");
1106 sv_gentle = cvar("sv_gentle");
1107 sv_foginterval = cvar("sv_foginterval");
1108 g_cloaked = cvar("g_cloaked");
1110 g_cloaked = 1; // always enable cloak in CTS
1111 g_jump_grunt = cvar("g_jump_grunt");
1112 g_footsteps = cvar("g_footsteps");
1113 g_grappling_hook = cvar("g_grappling_hook");
1114 g_jetpack = cvar("g_jetpack");
1115 g_midair = cvar("g_midair");
1116 g_minstagib = cvar("g_minstagib");
1117 g_norecoil = cvar("g_norecoil");
1118 g_bloodloss = cvar("g_bloodloss");
1119 sv_maxidle = cvar("sv_maxidle");
1120 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1121 g_ctf_reverse = cvar("g_ctf_reverse");
1122 sv_autotaunt = cvar("sv_autotaunt");
1123 sv_taunt = cvar("sv_taunt");
1125 inWarmupStage = cvar("g_warmup");
1126 g_warmup_limit = cvar("g_warmup_limit");
1127 g_warmup_allguns = cvar("g_warmup_allguns");
1128 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1130 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1131 inWarmupStage = 0; // these modes cannot work together, sorry
1133 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1134 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1135 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1136 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1137 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1138 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1139 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1140 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1141 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1142 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1143 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1144 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1146 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1147 g_weaponratefactor = cvar("g_weaponratefactor");
1148 g_weapondamagefactor = cvar("g_weapondamagefactor");
1149 g_weaponforcefactor = cvar("g_weaponforcefactor");
1150 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1152 g_pickup_shells = cvar("g_pickup_shells");
1153 g_pickup_shells_max = cvar("g_pickup_shells_max");
1154 g_pickup_nails = cvar("g_pickup_nails");
1155 g_pickup_nails_max = cvar("g_pickup_nails_max");
1156 g_pickup_rockets = cvar("g_pickup_rockets");
1157 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1158 g_pickup_cells = cvar("g_pickup_cells");
1159 g_pickup_cells_max = cvar("g_pickup_cells_max");
1160 g_pickup_fuel = cvar("g_pickup_fuel");
1161 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1162 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1163 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1164 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1165 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1166 g_pickup_armormedium = cvar("g_pickup_armormedium");
1167 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1168 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1169 g_pickup_armorbig = cvar("g_pickup_armorbig");
1170 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1171 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1172 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1173 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1174 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1175 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1176 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1177 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1178 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1179 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1180 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1181 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1182 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1183 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1184 g_pickup_healthmega = cvar("g_pickup_healthmega");
1185 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1186 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1188 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1189 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1191 g_pinata = cvar("g_pinata");
1193 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1195 g_weapon_stay = cvar("g_weapon_stay");
1197 g_ghost_items = cvar("g_ghost_items");
1199 if(g_ghost_items >= 1)
1200 g_ghost_items = 0.25; // default alpha value
1202 if not(inWarmupStage && !g_ca)
1203 game_starttime = cvar("g_start_delay");
1205 sv_pitch_min = cvar("sv_pitch_min");
1206 sv_pitch_max = cvar("sv_pitch_max");
1207 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1209 readplayerstartcvars();
1215 string precache_sound (string s) = #19;
1216 float precache_sound_index (string s) = #19;
1218 #define SND_VOLUME 1
1219 #define SND_ATTENUATION 2
1220 #define SND_LARGEENTITY 8
1221 #define SND_LARGESOUND 16
1223 float sound_allowed(float dest, entity e)
1225 // sounds from world may always pass
1228 if (e.classname == "body")
1230 else if (e.realowner && e.realowner != e)
1232 else if (e.owner && e.owner != e)
1237 // sounds to self may always pass
1238 if (dest == MSG_ONE)
1239 if (e == msg_entity)
1241 // sounds by players can be removed
1242 if (autocvar_bot_sound_monopoly)
1243 if (clienttype(e) == CLIENTTYPE_REAL)
1245 // anything else may pass
1249 #ifdef COMPAT_XON010_CHANNELS
1250 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1251 void sound(entity e, float chan, string samp, float vol, float atten)
1253 if (!sound_allowed(MSG_BROADCAST, e))
1255 builtin_sound(e, chan, samp, vol, atten);
1259 void sound(entity e, float chan, string samp, float vol, float atten)
1261 if (!sound_allowed(MSG_BROADCAST, e))
1263 sound7(e, chan, samp, vol, atten, 0, 0);
1267 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1271 if (!sound_allowed(dest, e))
1274 entno = num_for_edict(e);
1275 idx = precache_sound_index(samp);
1280 atten = floor(atten * 64);
1281 vol = floor(vol * 255);
1284 sflags |= SND_VOLUME;
1286 sflags |= SND_ATTENUATION;
1287 if (entno >= 8192 || chan < 0 || chan > 7)
1288 sflags |= SND_LARGEENTITY;
1290 sflags |= SND_LARGESOUND;
1292 WriteByte(dest, SVC_SOUND);
1293 WriteByte(dest, sflags);
1294 if (sflags & SND_VOLUME)
1295 WriteByte(dest, vol);
1296 if (sflags & SND_ATTENUATION)
1297 WriteByte(dest, atten);
1298 if (sflags & SND_LARGEENTITY)
1300 WriteShort(dest, entno);
1301 WriteByte(dest, chan);
1305 WriteShort(dest, entno * 8 + chan);
1307 if (sflags & SND_LARGESOUND)
1308 WriteShort(dest, idx);
1310 WriteByte(dest, idx);
1312 WriteCoord(dest, o_x);
1313 WriteCoord(dest, o_y);
1314 WriteCoord(dest, o_z);
1316 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1320 if (!sound_allowed(dest, e))
1323 o = e.origin + 0.5 * (e.mins + e.maxs);
1324 soundtoat(dest, e, o, chan, samp, vol, atten);
1326 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1328 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1330 void stopsoundto(float dest, entity e, float chan)
1334 if (!sound_allowed(dest, e))
1337 entno = num_for_edict(e);
1339 if (entno >= 8192 || chan < 0 || chan > 7)
1342 idx = precache_sound_index("misc/null.wav");
1343 sflags = SND_LARGEENTITY;
1345 sflags |= SND_LARGESOUND;
1346 WriteByte(dest, SVC_SOUND);
1347 WriteByte(dest, sflags);
1348 WriteShort(dest, entno);
1349 WriteByte(dest, chan);
1350 if (sflags & SND_LARGESOUND)
1351 WriteShort(dest, idx);
1353 WriteByte(dest, idx);
1354 WriteCoord(dest, e.origin_x);
1355 WriteCoord(dest, e.origin_y);
1356 WriteCoord(dest, e.origin_z);
1360 WriteByte(dest, SVC_STOPSOUND);
1361 WriteShort(dest, entno * 8 + chan);
1364 void stopsound(entity e, float chan)
1366 if (!sound_allowed(MSG_BROADCAST, e))
1369 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1370 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1373 void play2(entity e, string filename)
1375 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1377 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1380 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1382 float spamsound(entity e, float chan, string samp, float vol, float atten)
1384 if (!sound_allowed(MSG_BROADCAST, e))
1387 if (time > e.spamtime)
1390 sound(e, chan, samp, vol, atten);
1396 void play2team(float t, string filename)
1400 if (autocvar_bot_sound_monopoly)
1403 FOR_EACH_REALPLAYER(head)
1406 play2(head, filename);
1410 void play2all(string samp)
1412 if (autocvar_bot_sound_monopoly)
1415 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1418 void PrecachePlayerSounds(string f);
1419 void precache_playermodel(string m)
1421 float globhandle, i, n;
1424 if(substring(m, -9,5) == "_lod1")
1426 if(substring(m, -9,5) == "_lod2")
1429 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1432 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1436 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1439 n = search_getsize(globhandle);
1440 for (i = 0; i < n; ++i)
1442 //print(search_getfilename(globhandle, i), "\n");
1443 f = search_getfilename(globhandle, i);
1444 PrecachePlayerSounds(f);
1446 search_end(globhandle);
1448 void precache_all_playermodels(string pattern)
1450 float globhandle, i, n;
1453 globhandle = search_begin(pattern, TRUE, FALSE);
1456 n = search_getsize(globhandle);
1457 for (i = 0; i < n; ++i)
1459 //print(search_getfilename(globhandle, i), "\n");
1460 f = search_getfilename(globhandle, i);
1461 precache_playermodel(f);
1463 search_end(globhandle);
1468 // gamemode related things
1469 precache_model ("models/misc/chatbubble.spr");
1472 precache_model ("models/runematch/curse.mdl");
1473 precache_model ("models/runematch/rune.mdl");
1476 #ifdef TTURRETS_ENABLED
1477 if (autocvar_g_turrets)
1481 // Precache all player models if desired
1482 if (autocvar_sv_precacheplayermodels)
1484 PrecachePlayerSounds("sound/player/default.sounds");
1485 precache_all_playermodels("models/player/*.zym");
1486 precache_all_playermodels("models/player/*.dpm");
1487 precache_all_playermodels("models/player/*.md3");
1488 precache_all_playermodels("models/player/*.psk");
1489 precache_all_playermodels("models/player/*.iqm");
1492 if (autocvar_sv_defaultcharacter)
1495 s = autocvar_sv_defaultplayermodel_red;
1497 precache_playermodel(s);
1498 s = autocvar_sv_defaultplayermodel_blue;
1500 precache_playermodel(s);
1501 s = autocvar_sv_defaultplayermodel_yellow;
1503 precache_playermodel(s);
1504 s = autocvar_sv_defaultplayermodel_pink;
1506 precache_playermodel(s);
1507 s = autocvar_sv_defaultplayermodel;
1509 precache_playermodel(s);
1514 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1515 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1518 // gore and miscellaneous sounds
1519 //precache_sound ("misc/h2ohit.wav");
1520 precache_model ("models/hook.md3");
1521 precache_sound ("misc/armorimpact.wav");
1522 precache_sound ("misc/bodyimpact1.wav");
1523 precache_sound ("misc/bodyimpact2.wav");
1524 precache_sound ("misc/gib.wav");
1525 precache_sound ("misc/gib_splat01.wav");
1526 precache_sound ("misc/gib_splat02.wav");
1527 precache_sound ("misc/gib_splat03.wav");
1528 precache_sound ("misc/gib_splat04.wav");
1529 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1530 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1531 precache_sound ("misc/null.wav");
1532 precache_sound ("misc/spawn.wav");
1533 precache_sound ("misc/talk.wav");
1534 precache_sound ("misc/teleport.wav");
1535 precache_sound ("misc/poweroff.wav");
1536 precache_sound ("player/lava.wav");
1537 precache_sound ("player/slime.wav");
1540 precache_sound ("misc/jetpack_fly.wav");
1542 precache_model ("models/sprites/0.spr32");
1543 precache_model ("models/sprites/1.spr32");
1544 precache_model ("models/sprites/2.spr32");
1545 precache_model ("models/sprites/3.spr32");
1546 precache_model ("models/sprites/4.spr32");
1547 precache_model ("models/sprites/5.spr32");
1548 precache_model ("models/sprites/6.spr32");
1549 precache_model ("models/sprites/7.spr32");
1550 precache_model ("models/sprites/8.spr32");
1551 precache_model ("models/sprites/9.spr32");
1552 precache_model ("models/sprites/10.spr32");
1554 // common weapon precaches
1555 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1556 precache_sound ("weapons/weapon_switch.wav");
1557 precache_sound ("weapons/weaponpickup.wav");
1558 precache_sound ("weapons/unavailable.wav");
1559 precache_sound ("weapons/dryfire.wav");
1560 if (g_grappling_hook)
1562 precache_sound ("weapons/hook_fire.wav"); // hook
1563 precache_sound ("weapons/hook_impact.wav"); // hook
1566 if(autocvar_sv_precacheweapons)
1568 //precache weapon models/sounds
1571 while (wep <= WEP_LAST)
1573 weapon_action(wep, WR_PRECACHE);
1578 precache_model("models/elaser.mdl");
1579 precache_model("models/laser.mdl");
1580 precache_model("models/ebomb.mdl");
1583 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1585 if (!self.noise && self.music) // quake 3 uses the music field
1586 self.noise = self.music;
1588 // plays music for the level if there is any
1591 precache_sound (self.noise);
1592 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1597 // sorry, but using \ in macros breaks line numbers
1598 #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
1599 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1600 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1603 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1605 if (clienttype(e) == CLIENTTYPE_REAL)
1608 WRITESPECTATABLE_MSG_ONE({
1609 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1610 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1611 WriteByte(MSG_ONE, id);
1612 WriteString(MSG_ONE, s);
1613 if (id != 0 && s != "")
1615 WriteByte(MSG_ONE, duration);
1616 WriteByte(MSG_ONE, countdown_num);
1621 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1623 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1625 // WARNING: this kills the trace globals
1626 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1627 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1629 #define INITPRIO_FIRST 0
1630 #define INITPRIO_GAMETYPE 0
1631 #define INITPRIO_GAMETYPE_FALLBACK 1
1632 #define INITPRIO_FINDTARGET 10
1633 #define INITPRIO_DROPTOFLOOR 20
1634 #define INITPRIO_SETLOCATION 90
1635 #define INITPRIO_LINKDOORS 91
1636 #define INITPRIO_LAST 99
1638 .void(void) initialize_entity;
1639 .float initialize_entity_order;
1640 .entity initialize_entity_next;
1641 entity initialize_entity_first;
1643 void make_safe_for_remove(entity e)
1645 if (e.initialize_entity)
1648 for (ent = initialize_entity_first; ent; )
1650 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1652 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1653 // skip it in linked list
1656 prev.initialize_entity_next = ent.initialize_entity_next;
1657 ent = prev.initialize_entity_next;
1661 initialize_entity_first = ent.initialize_entity_next;
1662 ent = initialize_entity_first;
1668 ent = ent.initialize_entity_next;
1674 void objerror(string s)
1676 make_safe_for_remove(self);
1677 builtin_objerror(s);
1680 .float remove_except_protected_forbidden;
1681 void remove_except_protected(entity e)
1683 if(e.remove_except_protected_forbidden)
1684 error("not allowed to remove this at this point");
1688 void remove_unsafely(entity e)
1690 if(e.classname == "spike")
1691 error("Removing spikes is forbidden (crylink bug), please report");
1695 void remove_safely(entity e)
1697 make_safe_for_remove(e);
1701 void InitializeEntity(entity e, void(void) func, float order)
1705 if (!e || e.initialize_entity)
1707 // make a proxy initializer entity
1711 e.classname = "initialize_entity";
1715 e.initialize_entity = func;
1716 e.initialize_entity_order = order;
1718 cur = initialize_entity_first;
1721 if (!cur || cur.initialize_entity_order > order)
1723 // insert between prev and cur
1725 prev.initialize_entity_next = e;
1727 initialize_entity_first = e;
1728 e.initialize_entity_next = cur;
1732 cur = cur.initialize_entity_next;
1735 void InitializeEntitiesRun()
1738 startoflist = initialize_entity_first;
1739 initialize_entity_first = world;
1740 remove = remove_except_protected;
1741 for (self = startoflist; self; self = self.initialize_entity_next)
1743 self.remove_except_protected_forbidden = 1;
1745 for (self = startoflist; self; )
1748 var void(void) func;
1749 e = self.initialize_entity_next;
1750 func = self.initialize_entity;
1751 self.initialize_entity_order = 0;
1752 self.initialize_entity = func_null;
1753 self.initialize_entity_next = world;
1754 self.remove_except_protected_forbidden = 0;
1755 if (self.classname == "initialize_entity")
1759 builtin_remove(self);
1762 //dprint("Delayed initialization: ", self.classname, "\n");
1763 if(func != func_null)
1768 backtrace(strcat("Null function in: ", self.classname, "\n"));
1772 remove = remove_unsafely;
1775 .float uncustomizeentityforclient_set;
1776 .void(void) uncustomizeentityforclient;
1777 void(void) SUB_Nullpointer = #0;
1778 void UncustomizeEntitiesRun()
1782 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1783 self.uncustomizeentityforclient();
1786 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1788 e.customizeentityforclient = customizer;
1789 e.uncustomizeentityforclient = uncustomizer;
1790 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1794 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1797 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1801 if (e.classname == "")
1802 e.classname = "net_linked";
1804 if (e.model == "" || self.modelindex == 0)
1808 setmodel(e, "null");
1812 e.SendEntity = sendfunc;
1813 e.SendFlags = 0xFFFFFF;
1816 e.effects |= EF_NODEPTHTEST;
1820 e.nextthink = time + dt;
1821 e.think = SUB_Remove;
1825 void adaptor_think2touch()
1834 void adaptor_think2use()
1846 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1848 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
1849 self.projectiledeathtype |= HITTYPE_SPLASH;
1850 adaptor_think2use();
1853 // deferred dropping
1854 void DropToFloor_Handler()
1856 builtin_droptofloor();
1857 self.dropped_origin = self.origin;
1862 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1867 float trace_hits_box_a0, trace_hits_box_a1;
1869 float trace_hits_box_1d(float end, float thmi, float thma)
1873 // just check if x is in range
1881 // do the trace with respect to x
1882 // 0 -> end has to stay in thmi -> thma
1883 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1884 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1885 if (trace_hits_box_a0 > trace_hits_box_a1)
1891 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1896 // now it is a trace from 0 to end
1898 trace_hits_box_a0 = 0;
1899 trace_hits_box_a1 = 1;
1901 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1903 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1905 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1911 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1913 return trace_hits_box(start, end, thmi - ma, thma - mi);
1916 float SUB_NoImpactCheck()
1918 // zero hitcontents = this is not the real impact, but either the
1919 // mirror-impact of something hitting the projectile instead of the
1920 // projectile hitting the something, or a touchareagrid one. Neither of
1921 // these stop the projectile from moving, so...
1922 if(trace_dphitcontents == 0)
1924 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1925 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)));
1928 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1930 if (other == world && self.size != '0 0 0')
1933 tic = self.velocity * sys_frametime;
1934 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1935 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1936 if (trace_fraction >= 1)
1938 dprint("Odd... did not hit...?\n");
1940 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1942 dprint("Detected and prevented the sky-grapple bug.\n");
1950 #define SUB_OwnerCheck() (other && (other == self.owner))
1952 void RemoveGrapplingHook(entity pl);
1953 void W_Crylink_Dequeue(entity e);
1954 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1956 if(SUB_OwnerCheck())
1958 if(SUB_NoImpactCheck())
1960 if(self.classname == "grapplinghook")
1961 RemoveGrapplingHook(self.realowner);
1962 else if(self.classname == "spike")
1964 W_Crylink_Dequeue(self);
1971 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1972 UpdateCSQCProjectile(self);
1975 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1977 float MAX_IPBAN_URIS = 16;
1979 float URI_GET_DISCARD = 0;
1980 float URI_GET_IPBAN = 1;
1981 float URI_GET_IPBAN_END = 16;
1983 void URI_Get_Callback(float id, float status, string data)
1985 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1987 dprint("\nEnd of data.\n");
1989 if(url_URI_Get_Callback(id, status, data))
1993 else if (id == URI_GET_DISCARD)
1997 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2000 OnlineBanList_URI_Get_Callback(id, status, data);
2004 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2008 void print_to(entity e, string s)
2011 sprint(e, strcat(s, "\n"));
2016 string uid2name(string myuid) {
2018 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2020 // FIXME remove this later after 0.6 release
2021 // convert old style broken records to correct style
2024 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2027 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2028 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2033 s = "^1Unregistered Player";
2037 float race_readTime(string map, float pos)
2045 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2048 string race_readUID(string map, float pos)
2056 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2059 float race_readPos(string map, float t) {
2061 for (i = 1; i <= RANKINGS_CNT; ++i)
2062 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2065 return 0; // pos is zero if unranked
2068 void race_writeTime(string map, float t, string myuid)
2077 newpos = race_readPos(map, t);
2080 for(i = 1; i <= RANKINGS_CNT; ++i)
2082 if(race_readUID(map, i) == myuid)
2085 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2086 for (i = prevpos; i > newpos; --i) {
2087 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2088 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2090 } else { // player has no ranked record yet
2091 for (i = RANKINGS_CNT; i > newpos; --i) {
2092 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2093 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2097 // store new time itself
2098 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2099 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2102 string race_readName(string map, float pos)
2110 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2113 string race_placeName(float pos) {
2114 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2116 if(mod(pos, 10) == 1)
2117 return strcat(ftos(pos), "st");
2118 else if(mod(pos, 10) == 2)
2119 return strcat(ftos(pos), "nd");
2120 else if(mod(pos, 10) == 3)
2121 return strcat(ftos(pos), "rd");
2123 return strcat(ftos(pos), "th");
2126 return strcat(ftos(pos), "th");
2128 string getrecords(float page) // 50 records per page
2142 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2144 if (MapInfo_Get_ByID(i))
2146 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2150 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2151 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2159 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2161 if (MapInfo_Get_ByID(i))
2163 r = race_readTime(MapInfo_Map_bspname, 1);
2166 h = race_readName(MapInfo_Map_bspname, 1);
2167 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2175 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2177 if (MapInfo_Get_ByID(i))
2179 r = race_readTime(MapInfo_Map_bspname, 1);
2182 h = race_readName(MapInfo_Map_bspname, 1);
2183 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2189 MapInfo_ClearTemps();
2191 if (s == "" && page == 0)
2192 return "No records are available on this server.\n";
2197 string getrankings()
2210 for (i = 1; i <= RANKINGS_CNT; ++i)
2212 t = race_readTime(map, i);
2215 n = race_readName(map, i);
2216 p = race_placeName(i);
2217 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2220 MapInfo_ClearTemps();
2223 return strcat("No records are available for the map: ", map, "\n");
2225 return strcat("Records for ", map, ":\n", s);
2228 #define LADDER_FIRSTPOINT 100
2229 #define LADDER_CNT 10
2230 // position X still gives LADDER_FIRSTPOINT/X points
2231 #define LADDER_SIZE 30
2232 // ladder shows the top X players
2233 string top_uids[LADDER_SIZE];
2234 float top_scores[LADDER_SIZE];
2237 float i, j, k, uidcnt;
2251 for (k = 0; k < MapInfo_count; ++k)
2253 if (MapInfo_Get_ByID(k))
2255 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2256 if(i == 0) // speed award
2258 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2261 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2263 else // normal record, if it exists (else break)
2265 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2268 myuid = race_readUID(MapInfo_Map_bspname, i);
2271 // string s contains:
2272 // arg 0 = # of speed recs
2273 // arg 1 = # of 1st place recs
2274 // arg 2 = # of 2nd place recs
2276 // LADDER_CNT+1 = total points
2278 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2281 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2283 for (j = 0; j <= LADDER_CNT + 1; ++j)
2285 if(j != LADDER_CNT + 1)
2286 temp_s = strcat(temp_s, "0 ");
2288 temp_s = strcat(temp_s, "0");
2292 tokenize_console(temp_s);
2295 if(i == 0) // speed award
2296 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2298 if(j == 0) // speed award
2299 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2301 s = strcat(s, " ", argv(j)); // just copy over everything else
2304 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2307 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2308 else if(j == i) // wanted rec!
2309 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2311 s = strcat(s, " ", argv(j)); // just copy over everything else
2314 // total points are (by default) calculated like this:
2315 // speedrec = floor(100 / 10) = 10 points
2316 // 1st place = floor(100 / 1) = 100 points
2317 // 2nd place = floor(100 / 2) = 50 points
2318 // 3rd place = floor(100 / 3) = 33 points
2319 // 4th place = floor(100 / 4) = 25 points
2320 // 5th place = floor(100 / 5) = 20 points
2324 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2326 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2328 db_put(TemporaryDB, strcat("ladder", myuid), s);
2335 for (i = 0; i <= uidcnt; ++i) // for each known uid
2337 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2338 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2339 tokenize_console(temp_s);
2340 thiscnt = stof(argv(LADDER_CNT+1));
2342 if(thiscnt > top_scores[LADDER_SIZE-1])
2343 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2345 if(thiscnt > top_scores[j])
2347 for (k = LADDER_SIZE-1; k >= j; --k)
2349 top_uids[k] = top_uids[k-1];
2350 top_scores[k] = top_scores[k-1];
2352 top_uids[j] = thisuid;
2353 top_scores[j] = thiscnt;
2359 s = "^3-----------------------\n\n";
2361 s = strcat(s, "Pos ^3|");
2362 s = strcat(s, " ^7Total ^3|");
2363 for (i = 1; i <= LADDER_CNT; ++i)
2365 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2367 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2369 s = strcat(s, "\n^3----+--------");
2370 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2372 s = strcat(s, "+-----");
2375 for (i = 1; i <= LADDER_CNT - 9; ++i)
2377 s = strcat(s, "+------");
2381 s = strcat(s, "+--------------+--------------------\n");
2383 for (i = 0; i < LADDER_SIZE; ++i)
2385 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2386 tokenize_console(temp_s);
2387 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2389 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2390 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2391 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2393 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2396 for (j = 10; j <= LADDER_CNT; ++j)
2398 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2402 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2403 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2406 MapInfo_ClearTemps();
2409 return "No ladder on this server!\n";
2411 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2415 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2418 vector start, org, delta, end, enddown, mstart;
2421 m = e.dphitcontentsmask;
2422 e.dphitcontentsmask = goodcontents | badcontents;
2425 delta = world.maxs - world.mins;
2427 for (i = 0; i < attempts; ++i)
2429 start_x = org_x + random() * delta_x;
2430 start_y = org_y + random() * delta_y;
2431 start_z = org_z + random() * delta_z;
2433 // rule 1: start inside world bounds, and outside
2434 // solid, and don't start from somewhere where you can
2435 // fall down to evil
2436 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2437 if (trace_fraction >= 1)
2439 if (trace_startsolid)
2441 if (trace_dphitcontents & badcontents)
2443 if (trace_dphitq3surfaceflags & badsurfaceflags)
2446 // rule 2: if we are too high, lower the point
2447 if (trace_fraction * delta_z > maxaboveground)
2448 start = trace_endpos + '0 0 1' * maxaboveground;
2449 enddown = trace_endpos;
2451 // rule 3: make sure we aren't outside the map. This only works
2452 // for somewhat well formed maps. A good rule of thumb is that
2453 // the map should have a convex outside hull.
2454 // these can be traceLINES as we already verified the starting box
2455 mstart = start + 0.5 * (e.mins + e.maxs);
2456 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2457 if (trace_fraction >= 1)
2459 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2460 if (trace_fraction >= 1)
2462 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2463 if (trace_fraction >= 1)
2465 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2466 if (trace_fraction >= 1)
2468 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2469 if (trace_fraction >= 1)
2472 // rule 4: we must "see" some spawnpoint
2473 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2474 if(checkpvs(mstart, sp))
2478 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2479 if(checkpvs(mstart, sp))
2485 // find a random vector to "look at"
2486 end_x = org_x + random() * delta_x;
2487 end_y = org_y + random() * delta_y;
2488 end_z = org_z + random() * delta_z;
2489 end = start + normalize(end - start) * vlen(delta);
2491 // rule 4: start TO end must not be too short
2492 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2493 if (trace_startsolid)
2495 if (trace_fraction < minviewdistance / vlen(delta))
2498 // rule 5: don't want to look at sky
2499 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2502 // rule 6: we must not end up in trigger_hurt
2503 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2509 e.dphitcontentsmask = m;
2513 setorigin(e, start);
2514 e.angles = vectoangles(end - start);
2515 dprint("Needed ", ftos(i + 1), " attempts\n");
2522 float zcurveparticles_effectno;
2523 vector zcurveparticles_start;
2524 float zcurveparticles_spd;
2526 void endzcurveparticles()
2528 if(zcurveparticles_effectno)
2531 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2533 zcurveparticles_effectno = 0;
2536 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2538 spd = bound(0, floor(spd / 16), 32767);
2539 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2541 endzcurveparticles();
2542 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2543 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2544 WriteShort(MSG_BROADCAST, effectno);
2545 WriteCoord(MSG_BROADCAST, start_x);
2546 WriteCoord(MSG_BROADCAST, start_y);
2547 WriteCoord(MSG_BROADCAST, start_z);
2548 zcurveparticles_effectno = effectno;
2549 zcurveparticles_start = start;
2552 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2553 WriteCoord(MSG_BROADCAST, end_x);
2554 WriteCoord(MSG_BROADCAST, end_y);
2555 WriteCoord(MSG_BROADCAST, end_z);
2556 WriteCoord(MSG_BROADCAST, end_dz);
2557 zcurveparticles_spd = spd;
2560 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2563 vector vecxy, velxy;
2565 vecxy = end - start;
2570 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2572 endzcurveparticles();
2573 trailparticles(world, effectno, start, end);
2577 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2578 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2581 void write_recordmarker(entity pl, float tstart, float dt)
2583 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2585 // also write a marker into demo files for demotc-race-record-extractor to find
2588 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2589 " ", ftos(tstart), " ", ftos(dt), "\n"));
2592 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2605 if(allowcenter) // 2: allow center handedness
2618 if(allowcenter) // 2: allow center handedness
2634 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2639 if (autocvar_g_shootfromeye)
2652 else if (autocvar_g_shootfromcenter)
2657 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2667 else if (autocvar_g_shootfromclient)
2669 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2674 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2676 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2680 void attach_sameorigin(entity e, entity to, string tag)
2682 vector org, t_forward, t_left, t_up, e_forward, e_up;
2689 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2690 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2691 t_forward = v_forward * tagscale;
2692 t_left = v_right * -tagscale;
2693 t_up = v_up * tagscale;
2695 e.origin_x = org * t_forward;
2696 e.origin_y = org * t_left;
2697 e.origin_z = org * t_up;
2699 // current forward and up directions
2700 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2701 e.angles = AnglesTransform_FromVAngles(e.angles);
2703 e.angles = AnglesTransform_FromAngles(e.angles);
2704 fixedmakevectors(e.angles);
2706 // untransform forward, up!
2707 e_forward_x = v_forward * t_forward;
2708 e_forward_y = v_forward * t_left;
2709 e_forward_z = v_forward * t_up;
2710 e_up_x = v_up * t_forward;
2711 e_up_y = v_up * t_left;
2712 e_up_z = v_up * t_up;
2714 e.angles = fixedvectoangles2(e_forward, e_up);
2715 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2716 e.angles = AnglesTransform_ToVAngles(e.angles);
2718 e.angles = AnglesTransform_ToAngles(e.angles);
2720 setattachment(e, to, tag);
2721 setorigin(e, e.origin);
2724 void detach_sameorigin(entity e)
2727 org = gettaginfo(e, 0);
2728 e.angles = fixedvectoangles2(v_forward, v_up);
2729 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2730 e.angles = AnglesTransform_ToVAngles(e.angles);
2732 e.angles = AnglesTransform_ToAngles(e.angles);
2734 setattachment(e, world, "");
2735 setorigin(e, e.origin);
2738 void follow_sameorigin(entity e, entity to)
2740 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2741 e.aiment = to; // make the hole follow bmodel
2742 e.punchangle = to.angles; // the original angles of bmodel
2743 e.view_ofs = e.origin - to.origin; // relative origin
2744 e.v_angle = e.angles - to.angles; // relative angles
2747 void unfollow_sameorigin(entity e)
2749 e.movetype = MOVETYPE_NONE;
2752 entity gettaginfo_relative_ent;
2753 vector gettaginfo_relative(entity e, float tag)
2755 if (!gettaginfo_relative_ent)
2757 gettaginfo_relative_ent = spawn();
2758 gettaginfo_relative_ent.effects = EF_NODRAW;
2760 gettaginfo_relative_ent.model = e.model;
2761 gettaginfo_relative_ent.modelindex = e.modelindex;
2762 gettaginfo_relative_ent.frame = e.frame;
2763 return gettaginfo(gettaginfo_relative_ent, tag);
2766 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2770 if (pl.soundentity.cnt & p)
2772 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2773 pl.soundentity.cnt |= p;
2776 void SoundEntity_StopSound(entity pl, float chan)
2780 if (pl.soundentity.cnt & p)
2782 stopsoundto(MSG_ALL, pl.soundentity, chan);
2783 pl.soundentity.cnt &~= p;
2787 void SoundEntity_Attach(entity pl)
2789 pl.soundentity = spawn();
2790 pl.soundentity.classname = "soundentity";
2791 pl.soundentity.owner = pl;
2792 setattachment(pl.soundentity, pl, "");
2793 setmodel(pl.soundentity, "null");
2796 void SoundEntity_Detach(entity pl)
2799 for (i = 0; i <= 7; ++i)
2800 SoundEntity_StopSound(pl, i);
2804 float ParseCommandPlayerSlotTarget_firsttoken;
2805 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2813 ParseCommandPlayerSlotTarget_firsttoken = -1;
2817 if (substring(argv(idx), 0, 1) == "#")
2819 s = substring(argv(idx), 1, -1);
2821 if (s == "") if (tokens > idx)
2826 ParseCommandPlayerSlotTarget_firsttoken = idx;
2828 if (s == ftos(n) && n > 0 && n <= maxclients)
2831 if (e.flags & FL_CLIENT)
2837 // it must be a nick name
2840 ParseCommandPlayerSlotTarget_firsttoken = idx;
2843 FOR_EACH_CLIENT(head)
2844 if (head.netname == s)
2852 s = strdecolorize(s);
2854 FOR_EACH_CLIENT(head)
2855 if (strdecolorize(head.netname) == s)
2870 float modeleffect_SendEntity(entity to, float sf)
2873 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2876 if(self.velocity != '0 0 0')
2878 if(self.angles != '0 0 0')
2880 if(self.avelocity != '0 0 0')
2883 WriteByte(MSG_ENTITY, f);
2884 WriteShort(MSG_ENTITY, self.modelindex);
2885 WriteByte(MSG_ENTITY, self.skin);
2886 WriteByte(MSG_ENTITY, self.frame);
2887 WriteCoord(MSG_ENTITY, self.origin_x);
2888 WriteCoord(MSG_ENTITY, self.origin_y);
2889 WriteCoord(MSG_ENTITY, self.origin_z);
2892 WriteCoord(MSG_ENTITY, self.velocity_x);
2893 WriteCoord(MSG_ENTITY, self.velocity_y);
2894 WriteCoord(MSG_ENTITY, self.velocity_z);
2898 WriteCoord(MSG_ENTITY, self.angles_x);
2899 WriteCoord(MSG_ENTITY, self.angles_y);
2900 WriteCoord(MSG_ENTITY, self.angles_z);
2904 WriteCoord(MSG_ENTITY, self.avelocity_x);
2905 WriteCoord(MSG_ENTITY, self.avelocity_y);
2906 WriteCoord(MSG_ENTITY, self.avelocity_z);
2908 WriteShort(MSG_ENTITY, self.scale * 256.0);
2909 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2910 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2911 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2912 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2917 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)
2922 e.classname = "modeleffect";
2930 e.teleport_time = t1;
2934 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2938 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2941 sz = max(e.scale, e.scale2);
2942 setsize(e, e.mins * sz, e.maxs * sz);
2943 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2946 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2948 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2951 float randombit(float bits)
2953 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2962 for(f = 1; f <= bits; f *= 2)
2971 r = (r - 1) / (n - 1);
2978 float randombits(float bits, float k, float error_return)
2982 while(k > 0 && bits != r)
2984 r += randombit(bits - r);
2993 void randombit_test(float bits, float iter)
2997 print(ftos(randombit(bits)), "\n");
3002 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3004 if(halflifedist > 0)
3005 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3006 else if(halflifedist < 0)
3007 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3016 #define cvar_string_normal builtin_cvar_string
3017 #define cvar_normal builtin_cvar
3019 string cvar_string_normal(string n)
3021 if not(cvar_type(n) & 1)
3022 backtrace(strcat("Attempt to access undefined cvar: ", n));
3023 return builtin_cvar_string(n);
3026 float cvar_normal(string n)
3028 return stof(cvar_string_normal(n));
3031 #define cvar_set_normal builtin_cvar_set
3039 oself.think = SUB_Remove;
3040 oself.nextthink = time;
3046 Execute func() after time + fdelay.
3047 self when func is executed = self when defer is called
3049 void defer(float fdelay, void() func)
3056 e.think = defer_think;
3057 e.nextthink = time + fdelay;
3060 .string aiment_classname;
3061 .float aiment_deadflag;
3062 void SetMovetypeFollow(entity ent, entity e)
3064 // FIXME this may not be warpzone aware
3065 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3066 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.
3067 ent.aiment = e; // make the hole follow bmodel
3068 ent.punchangle = e.angles; // the original angles of bmodel
3069 ent.view_ofs = ent.origin - e.origin; // relative origin
3070 ent.v_angle = ent.angles - e.angles; // relative angles
3071 ent.aiment_classname = strzone(e.classname);
3072 ent.aiment_deadflag = e.deadflag;
3074 void UnsetMovetypeFollow(entity ent)
3076 ent.movetype = MOVETYPE_FLY;
3077 PROJECTILE_MAKETRIGGER(ent);
3080 float LostMovetypeFollow(entity ent)
3083 if(ent.movetype != MOVETYPE_FOLLOW)
3089 if(ent.aiment.classname != ent.aiment_classname)
3091 if(ent.aiment.deadflag != ent.aiment_deadflag)
3097 float isPushable(entity e)
3104 case "droppedweapon":
3105 case "keepawayball":
3106 case "nexball_basketball":
3107 case "nexball_football":
3109 case "bullet": // antilagged bullets can't hit this either
3112 if (e.projectiledeathtype)