1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
8 string ColoredTeamName(float t);
10 float DistributeEvenly_amount;
11 float DistributeEvenly_totalweight;
12 void DistributeEvenly_Init(float amount, float totalweight)
14 if (DistributeEvenly_amount)
16 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
17 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
20 DistributeEvenly_amount = 0;
22 DistributeEvenly_amount = amount;
23 DistributeEvenly_totalweight = totalweight;
25 float DistributeEvenly_Get(float weight)
30 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
31 DistributeEvenly_totalweight -= weight;
32 DistributeEvenly_amount -= f;
36 void move_out_of_solid_expand(entity e, vector by)
39 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
42 if (trace_fraction < 1)
45 // adjust origin in the other direction...
46 e.origin = e.origin - by * (1 - trace_fraction);
50 float move_out_of_solid(entity e)
55 traceline(o, o, MOVE_WORLDONLY, e);
59 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
60 if (!trace_startsolid)
67 move_out_of_solid_expand(e, '1 0 0' * m0_x);
69 move_out_of_solid_expand(e, '1 0 0' * m1_x);
71 move_out_of_solid_expand(e, '0 1 0' * m0_y);
73 move_out_of_solid_expand(e, '0 1 0' * m1_y);
75 move_out_of_solid_expand(e, '0 0 1' * m0_z);
77 move_out_of_solid_expand(e, '0 0 1' * m1_z);
79 setorigin(e, e.origin);
81 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
91 string STR_PLAYER = "player";
92 string STR_SPECTATOR = "spectator";
93 string STR_OBSERVER = "observer";
96 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
97 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
98 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
101 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
102 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
103 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
104 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
105 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
108 // copies a string to a tempstring (so one can strunzone it)
109 string strcat1(string s) = #115; // FRIK_FILE
114 void bcenterprint(string s)
116 // TODO replace by MSG_ALL (would show it to spectators too, though)?
118 FOR_EACH_PLAYER(head)
119 if (clienttype(head) == CLIENTTYPE_REAL)
120 centerprint(head, s);
123 void GameLogEcho(string s)
128 if (cvar("sv_eventlog_files"))
133 matches = cvar("sv_eventlog_files_counter") + 1;
134 cvar_set("sv_eventlog_files_counter", ftos(matches));
137 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
138 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
139 logfile = fopen(fn, FILE_APPEND);
140 fputs(logfile, ":logversion:3\n");
144 if (cvar("sv_eventlog_files_timestamps"))
145 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
147 fputs(logfile, strcat(s, "\n"));
150 if (cvar("sv_eventlog_console"))
159 // will be opened later
164 if (logfile_open && logfile >= 0)
171 float spawnpoint_nag;
172 void relocate_spawnpoint()
174 // nudge off the floor
175 setorigin(self, self.origin + '0 0 1');
177 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
178 if (trace_startsolid)
184 if (!move_out_of_solid(self))
185 objerror("could not get out of solid at all!");
186 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
187 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
188 print(" ", ftos(self.origin_y - o_y));
189 print(" ", ftos(self.origin_z - o_z), "'\n");
190 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
193 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
199 self.mins = self.maxs = '0 0 0';
200 objerror("player spawn point in solid, mapper sucks!\n");
205 if (cvar("g_spawnpoints_autodrop"))
207 setsize(self, PL_MIN, PL_MAX);
211 self.use = spawnpoint_use;
212 self.team_saved = self.team;
216 if (g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
218 have_team_spawns = 1;
220 if (cvar("r_showbboxes"))
222 // show where spawnpoints point at too
223 makevectors(self.angles);
226 e.classname = "info_player_foo";
227 setorigin(e, self.origin + v_forward * 24);
228 setsize(e, '-8 -8 -8', '8 8 8');
229 e.solid = SOLID_TRIGGER;
233 #define strstr strstrofs
235 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
236 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
237 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
238 // BE CONSTANT OR strzoneD!
239 float strstr(string haystack, string needle, float offset)
243 len = strlen(needle);
244 endpos = strlen(haystack) - len;
245 while(offset <= endpos)
247 found = substring(haystack, offset, len);
256 float NUM_NEAREST_ENTITIES = 4;
257 entity nearest_entity[NUM_NEAREST_ENTITIES];
258 float nearest_length[NUM_NEAREST_ENTITIES];
259 entity findnearest(vector point, .string field, string value, vector axismod)
270 localhead = find(world, field, value);
273 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
274 dist = localhead.oldorigin;
276 dist = localhead.origin;
278 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
281 for (i = 0; i < num_nearest; ++i)
283 if (len < nearest_length[i])
287 // now i tells us where to insert at
288 // INSERTION SORT! YOU'VE SEEN IT! RUN!
289 if (i < NUM_NEAREST_ENTITIES)
291 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
293 nearest_length[j + 1] = nearest_length[j];
294 nearest_entity[j + 1] = nearest_entity[j];
296 nearest_length[i] = len;
297 nearest_entity[i] = localhead;
298 if (num_nearest < NUM_NEAREST_ENTITIES)
299 num_nearest = num_nearest + 1;
302 localhead = find(localhead, field, value);
305 // now use the first one from our list that we can see
306 for (i = 0; i < num_nearest; ++i)
308 traceline(point, nearest_entity[i].origin, TRUE, world);
309 if (trace_fraction == 1)
313 dprint("Nearest point (");
314 dprint(nearest_entity[0].netname);
315 dprint(") is not visible, using a visible one.\n");
317 return nearest_entity[i];
321 if (num_nearest == 0)
324 dprint("Not seeing any location point, using nearest as fallback.\n");
326 dprint("Candidates were: ");
327 for(j = 0; j < num_nearest; ++j)
331 dprint(nearest_entity[j].netname);
336 return nearest_entity[0];
339 void spawnfunc_target_location()
341 self.classname = "target_location";
342 // location name in netname
343 // eventually support: count, teamgame selectors, line of sight?
346 void spawnfunc_info_location()
348 self.classname = "target_location";
349 self.message = self.netname;
352 string NearestLocation(vector p)
357 loc = findnearest(p, classname, "target_location", '1 1 1');
364 loc = findnearest(p, target, "###item###", '1 1 4');
371 string formatmessage(string msg)
382 break; // too many replacements
384 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
385 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
395 replacement = substring(msg, p, 2);
396 escape = substring(msg, p + 1, 1);
399 else if (escape == "\\")
401 else if (escape == "n")
403 else if (escape == "a")
404 replacement = ftos(floor(self.armorvalue));
405 else if (escape == "h")
406 replacement = ftos(floor(self.health));
407 else if (escape == "l")
408 replacement = NearestLocation(self.origin);
409 else if (escape == "y")
410 replacement = NearestLocation(self.cursor_trace_endpos);
411 else if (escape == "d")
412 replacement = NearestLocation(self.death_origin);
413 else if (escape == "w")
418 wep = self.switchweapon;
421 replacement = W_Name(wep);
423 else if (escape == "W")
425 if (self.items & IT_SHELLS) replacement = "shells";
426 else if (self.items & IT_NAILS) replacement = "bullets";
427 else if (self.items & IT_ROCKETS) replacement = "rockets";
428 else if (self.items & IT_CELLS) replacement = "cells";
429 else replacement = "batteries"; // ;)
431 else if (escape == "x")
433 replacement = self.cursor_trace_ent.netname;
434 if (!replacement || !self.cursor_trace_ent)
435 replacement = "nothing";
437 else if (escape == "p")
439 if (self.last_selected_player)
440 replacement = self.last_selected_player.netname;
442 replacement = "(nobody)";
444 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
445 p = p + strlen(replacement);
456 >0: receives a cvar from name=argv(f) value=argv(f+1)
458 void GetCvars_handleString(string thisname, float f, .string field, string name)
463 strunzone(self.field);
464 self.field = string_null;
468 if (thisname == name)
471 strunzone(self.field);
472 self.field = strzone(argv(f + 1));
476 stuffcmd(self, strcat("sendcvar ", name, "\n"));
478 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
480 GetCvars_handleString(thisname, f, field, name);
481 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
482 if (thisname == name)
485 s = func(strcat1(self.field));
488 strunzone(self.field);
489 self.field = strzone(s);
493 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
500 if (thisname == name)
501 self.field = stof(argv(f + 1));
504 stuffcmd(self, strcat("sendcvar ", name, "\n"));
506 string W_FixWeaponOrder_ForceComplete(string s);
507 string W_FixWeaponOrder_AllowIncomplete(string s);
508 float w_getbestweapon(entity e);
509 void GetCvars(float f)
513 s = strcat1(argv(f));
514 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
515 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
516 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
517 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
518 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
519 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
520 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
521 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
522 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
523 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
524 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
525 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
526 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
527 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
528 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
529 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
530 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
531 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
532 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
533 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
534 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
535 #ifdef ALLOW_FORCEMODELS
536 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
537 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
539 GetCvars_handleFloat(s, f, cvar_cl_gunalign, "cl_gunalign");
542 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
545 if (s == "cl_weaponpriority")
546 self.switchweapon = w_getbestweapon(self);
550 float fexists(string f)
553 fh = fopen(f, FILE_READ);
560 void backtrace(string msg)
563 dev = cvar("developer");
564 cvar_set("developer", "1");
566 dprint("--- CUT HERE ---\nWARNING: ");
569 remove(world); // isn't there any better way to cause a backtrace?
570 dprint("\n--- CUT UNTIL HERE ---\n");
571 cvar_set("developer", ftos(dev));
574 string Team_ColorCode(float teamid)
576 if (teamid == COLOR_TEAM1)
578 else if (teamid == COLOR_TEAM2)
580 else if (teamid == COLOR_TEAM3)
582 else if (teamid == COLOR_TEAM4)
587 string Team_ColorName(float t)
589 // fixme: Search for team entities and get their .netname's!
590 if (t == COLOR_TEAM1)
592 if (t == COLOR_TEAM2)
594 if (t == COLOR_TEAM3)
596 if (t == COLOR_TEAM4)
600 string Team_ColorNameLowerCase(float t)
602 // fixme: Search for team entities and get their .netname's!
603 if (t == COLOR_TEAM1)
605 if (t == COLOR_TEAM2)
607 if (t == COLOR_TEAM3)
609 if (t == COLOR_TEAM4)
614 #define CENTERPRIO_POINT 1
615 #define CENTERPRIO_SPAM 2
616 #define CENTERPRIO_VOTE 4
617 #define CENTERPRIO_NORMAL 5
618 #define CENTERPRIO_SHIELDING 7
619 #define CENTERPRIO_MAPVOTE 9
620 #define CENTERPRIO_IDLEKICK 50
621 #define CENTERPRIO_ADMIN 99
622 .float centerprint_priority;
623 .float centerprint_expires;
624 void centerprint_atprio(entity e, float prio, string s)
626 if (intermission_running)
627 if (prio < CENTERPRIO_MAPVOTE)
629 if (time > e.centerprint_expires)
630 e.centerprint_priority = 0;
631 if (prio >= e.centerprint_priority)
633 e.centerprint_priority = prio;
634 if (timeoutStatus == 2)
635 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
637 e.centerprint_expires = time + e.cvar_scr_centertime;
638 centerprint_builtin(e, s);
641 void centerprint_expire(entity e, float prio)
643 if (prio == e.centerprint_priority)
645 e.centerprint_priority = 0;
646 centerprint_builtin(e, "");
649 void centerprint(entity e, string s)
651 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
654 // decolorizes and team colors the player name when needed
655 string playername(entity p)
658 if (teams_matter && !intermission_running && p.classname == "player")
660 t = Team_ColorCode(p.team);
661 return strcat(t, strdecolorize(p.netname));
667 vector randompos(vector m1, vector m2)
671 v_x = m2_x * random() + m1_x;
672 v_y = m2_y * random() + m1_y;
673 v_z = m2_z * random() + m1_z;
677 float g_pickup_shells;
678 float g_pickup_shells_max;
679 float g_pickup_nails;
680 float g_pickup_nails_max;
681 float g_pickup_rockets;
682 float g_pickup_rockets_max;
683 float g_pickup_cells;
684 float g_pickup_cells_max;
686 float g_pickup_fuel_jetpack;
687 float g_pickup_fuel_max;
688 float g_pickup_armorsmall;
689 float g_pickup_armorsmall_max;
690 float g_pickup_armormedium;
691 float g_pickup_armormedium_max;
692 float g_pickup_armorbig;
693 float g_pickup_armorbig_max;
694 float g_pickup_armorlarge;
695 float g_pickup_armorlarge_max;
696 float g_pickup_healthsmall;
697 float g_pickup_healthsmall_max;
698 float g_pickup_healthmedium;
699 float g_pickup_healthmedium_max;
700 float g_pickup_healthlarge;
701 float g_pickup_healthlarge_max;
702 float g_pickup_healthmega;
703 float g_pickup_healthmega_max;
705 string g_weaponarena_list;
706 float g_weaponspeedfactor;
707 float g_weaponratefactor;
708 float g_weapondamagefactor;
709 float g_weaponforcefactor;
713 float start_ammo_shells;
714 float start_ammo_nails;
715 float start_ammo_rockets;
716 float start_ammo_cells;
717 float start_ammo_fuel;
719 float start_armorvalue;
720 float warmup_start_weapons;
721 float warmup_start_ammo_shells;
722 float warmup_start_ammo_nails;
723 float warmup_start_ammo_rockets;
724 float warmup_start_ammo_cells;
725 float warmup_start_ammo_fuel;
726 float warmup_start_health;
727 float warmup_start_armorvalue;
730 entity get_weaponinfo(float w);
732 float NixNex_CanChooseWeapon(float wpn);
733 void readplayerstartcvars()
739 // initialize starting values for players
742 start_ammo_shells = 0;
743 start_ammo_nails = 0;
744 start_ammo_rockets = 0;
745 start_ammo_cells = 0;
746 start_health = cvar("g_balance_health_start");
747 start_armorvalue = cvar("g_balance_armor_start");
750 s = cvar_string("g_weaponarena");
756 g_weaponarena_list = "All Weapons";
757 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
759 e = get_weaponinfo(j);
760 g_weaponarena |= e.weapons;
761 weapon_action(e.weapon, WR_PRECACHE);
764 else if (s == "most")
766 g_weaponarena_list = "Most Weapons";
767 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
769 e = get_weaponinfo(j);
770 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
772 g_weaponarena |= e.weapons;
773 weapon_action(e.weapon, WR_PRECACHE);
777 else if (s == "none")
779 g_weaponarena_list = "No Weapons";
780 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
784 t = tokenize_console(s);
785 g_weaponarena_list = "";
786 for (i = 0; i < t; ++i)
789 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
791 e = get_weaponinfo(j);
794 g_weaponarena |= e.weapons;
795 weapon_action(e.weapon, WR_PRECACHE);
796 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
802 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
805 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
811 // will be done later
812 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
813 if (NixNex_CanChooseWeapon(i))
814 weapon_action(i, WR_PRECACHE);
815 if(!cvar("g_use_ammunition"))
816 start_items |= IT_UNLIMITED_AMMO;
818 else if (g_weaponarena)
820 start_weapons = g_weaponarena;
821 start_ammo_rockets = 999;
822 start_ammo_shells = 999;
823 start_ammo_cells = 999;
824 start_ammo_nails = 999;
825 start_ammo_fuel = 999;
826 start_items |= IT_UNLIMITED_AMMO;
828 else if (g_minstagib)
831 start_armorvalue = 0;
832 start_weapons = WEPBIT_MINSTANEX;
833 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
834 start_ammo_cells = cvar("g_minstagib_ammo_start");
835 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
836 start_ammo_fuel = cvar("g_start_ammo_fuel");
838 if (g_minstagib_invis_alpha <= 0)
839 g_minstagib_invis_alpha = -1;
845 start_ammo_shells = cvar("g_lms_start_ammo_shells");
846 start_ammo_nails = cvar("g_lms_start_ammo_nails");
847 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
848 start_ammo_cells = cvar("g_lms_start_ammo_cells");
849 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
850 start_health = cvar("g_lms_start_health");
851 start_armorvalue = cvar("g_lms_start_armor");
853 else if (cvar("g_use_ammunition"))
855 start_ammo_shells = cvar("g_start_ammo_shells");
856 start_ammo_nails = cvar("g_start_ammo_nails");
857 start_ammo_rockets = cvar("g_start_ammo_rockets");
858 start_ammo_cells = cvar("g_start_ammo_cells");
859 start_ammo_fuel = cvar("g_start_ammo_fuel");
863 start_ammo_shells = cvar("g_pickup_shells_max");
864 start_ammo_nails = cvar("g_pickup_nails_max");
865 start_ammo_rockets = cvar("g_pickup_rockets_max");
866 start_ammo_cells = cvar("g_pickup_cells_max");
867 start_ammo_fuel = cvar("g_pickup_fuel_max");
868 start_items |= IT_UNLIMITED_AMMO;
871 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
873 e = get_weaponinfo(i);
877 t = cvar(strcat("g_start_weapon_", e.netname));
879 if (t < 0) // "default" weapon selection
882 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
883 else if (g_race || g_cts)
884 t = (i == WEP_LASER);
886 t = 0; // weapon is set a few lines later
888 t = (i == WEP_LASER || i == WEP_SHOTGUN);
889 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
890 t += (i == WEP_HOOK);
893 if (g_nexball && i == WEP_PORTO)
898 start_weapons |= e.weapons;
899 weapon_action(e.weapon, WR_PRECACHE);
906 warmup_start_ammo_shells = start_ammo_shells;
907 warmup_start_ammo_nails = start_ammo_nails;
908 warmup_start_ammo_rockets = start_ammo_rockets;
909 warmup_start_ammo_cells = start_ammo_cells;
910 warmup_start_health = start_health;
911 warmup_start_armorvalue = start_armorvalue;
912 warmup_start_weapons = start_weapons;
914 if (!g_weaponarena && !g_nixnex && !g_minstagib)
916 if (cvar("g_use_ammunition"))
918 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
919 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
920 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
921 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
923 warmup_start_health = cvar("g_warmup_start_health");
924 warmup_start_armorvalue = cvar("g_warmup_start_armor");
925 if (cvar("g_warmup_allguns"))
927 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
929 e = get_weaponinfo(i);
932 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
934 warmup_start_weapons |= e.weapons;
935 weapon_action(e.weapon, WR_PRECACHE);
942 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
944 g_grappling_hook = 0; // these two can't coexist, as they use the same button
945 start_items |= IT_FUEL_REGEN;
946 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
950 start_items |= IT_JETPACK;
952 if (g_weapon_stay == 2)
954 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
955 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
956 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
957 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
958 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
959 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
960 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
961 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
962 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
963 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
966 start_ammo_shells = max(0, start_ammo_shells);
967 start_ammo_nails = max(0, start_ammo_nails);
968 start_ammo_cells = max(0, start_ammo_cells);
969 start_ammo_rockets = max(0, start_ammo_rockets);
970 start_ammo_fuel = max(0, start_ammo_fuel);
972 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
973 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
974 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
975 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
976 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
980 float g_bugrigs_planar_movement;
981 float g_bugrigs_planar_movement_car_jumping;
982 float g_bugrigs_reverse_spinning;
983 float g_bugrigs_reverse_speeding;
984 float g_bugrigs_reverse_stopping;
985 float g_bugrigs_air_steering;
986 float g_bugrigs_angle_smoothing;
987 float g_bugrigs_friction_floor;
988 float g_bugrigs_friction_brake;
989 float g_bugrigs_friction_air;
990 float g_bugrigs_accel;
991 float g_bugrigs_speed_ref;
992 float g_bugrigs_speed_pow;
993 float g_bugrigs_steer;
995 float g_touchexplode;
996 float g_touchexplode_radius;
997 float g_touchexplode_damage;
998 float g_touchexplode_edgedamage;
999 float g_touchexplode_force;
1001 void readlevelcvars(void)
1003 g_bugrigs = cvar("g_bugrigs");
1004 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1005 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1006 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1007 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1008 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1009 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1010 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1011 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1012 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1013 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1014 g_bugrigs_accel = cvar("g_bugrigs_accel");
1015 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1016 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1017 g_bugrigs_steer = cvar("g_bugrigs_steer");
1019 g_touchexplode = cvar("g_touchexplode");
1020 g_touchexplode_radius = cvar("g_touchexplode_radius");
1021 g_touchexplode_damage = cvar("g_touchexplode_damage");
1022 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1023 g_touchexplode_force = cvar("g_touchexplode_force");
1025 #ifdef ALLOW_FORCEMODELS
1026 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1028 sv_loddistance1 = cvar("sv_loddistance1");
1029 sv_loddistance2 = cvar("sv_loddistance2");
1030 if(sv_loddistance2 <= sv_loddistance1)
1031 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1032 sv_clones = cvar("sv_clones");
1033 sv_cheats = cvar("sv_cheats");
1034 sv_gentle = cvar("sv_gentle");
1035 sv_foginterval = cvar("sv_foginterval");
1036 g_cloaked = cvar("g_cloaked");
1037 g_jump_grunt = cvar("g_jump_grunt");
1038 g_footsteps = cvar("g_footsteps");
1039 g_grappling_hook = cvar("g_grappling_hook");
1040 g_jetpack = cvar("g_jetpack");
1041 g_laserguided_missile = cvar("g_laserguided_missile");
1042 g_midair = cvar("g_midair");
1043 g_minstagib = cvar("g_minstagib");
1044 g_nixnex = cvar("g_nixnex");
1045 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1046 g_norecoil = cvar("g_norecoil");
1047 g_vampire = cvar("g_vampire");
1048 g_bloodloss = cvar("g_bloodloss");
1049 sv_maxidle = cvar("sv_maxidle");
1050 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1051 sv_pogostick = cvar("sv_pogostick");
1052 sv_doublejump = cvar("sv_doublejump");
1053 g_ctf_reverse = cvar("g_ctf_reverse");
1055 inWarmupStage = cvar("g_warmup");
1056 g_warmup_limit = cvar("g_warmup_limit");
1057 g_warmup_allguns = cvar("g_warmup_allguns");
1058 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1060 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
1061 inWarmupStage = 0; // these modes cannot work together, sorry
1063 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1064 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1065 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1066 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1067 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1068 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1069 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1070 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1071 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1072 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1073 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1074 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1076 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1077 if (g_nixnex) g_weaponarena = 0;
1080 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1081 g_weaponratefactor = cvar("g_weaponratefactor");
1082 g_weapondamagefactor = cvar("g_weapondamagefactor");
1083 g_weaponforcefactor = cvar("g_weaponforcefactor");
1085 g_pickup_shells = cvar("g_pickup_shells");
1086 g_pickup_shells_max = cvar("g_pickup_shells_max");
1087 g_pickup_nails = cvar("g_pickup_nails");
1088 g_pickup_nails_max = cvar("g_pickup_nails_max");
1089 g_pickup_rockets = cvar("g_pickup_rockets");
1090 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1091 g_pickup_cells = cvar("g_pickup_cells");
1092 g_pickup_cells_max = cvar("g_pickup_cells_max");
1093 g_pickup_fuel = cvar("g_pickup_fuel");
1094 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1095 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1096 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1097 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1098 g_pickup_armormedium = cvar("g_pickup_armormedium");
1099 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1100 g_pickup_armorbig = cvar("g_pickup_armorbig");
1101 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1102 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1103 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1104 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1105 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1106 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1107 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1108 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1109 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1110 g_pickup_healthmega = cvar("g_pickup_healthmega");
1111 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1113 g_pinata = cvar("g_pinata");
1115 g_weapon_stay = cvar("g_weapon_stay");
1116 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1119 if not(inWarmupStage)
1120 game_starttime = cvar("g_start_delay");
1122 readplayerstartcvars();
1126 // TODO sound pack system
1129 string precache_sound_builtin (string s) = #19;
1130 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1131 string precache_sound(string s)
1133 return precache_sound_builtin(strcat(soundpack, s));
1135 void play2(entity e, string filename)
1137 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1139 void sound(entity e, float chan, string samp, float vol, float atten)
1141 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1146 string precache_sound (string s) = #19;
1147 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1148 float precache_sound_index (string s) = #19;
1150 #define SND_VOLUME 1
1151 #define SND_ATTENUATION 2
1152 #define SND_LARGEENTITY 8
1153 #define SND_LARGESOUND 16
1155 float sound_allowed(float dest, entity e)
1157 // sounds from world may always pass
1160 if (e.classname == "body")
1162 if (e.owner && e.owner != e)
1167 // sounds to self may always pass
1168 if (dest == MSG_ONE)
1169 if (e == msg_entity)
1171 // sounds by players can be removed
1172 if (cvar("bot_sound_monopoly"))
1173 if (clienttype(e) == CLIENTTYPE_REAL)
1175 // anything else may pass
1179 void sound(entity e, float chan, string samp, float vol, float atten)
1181 if (!sound_allowed(MSG_BROADCAST, e))
1183 sound_builtin(e, chan, samp, vol, atten);
1185 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1189 if (!sound_allowed(dest, e))
1192 entno = num_for_edict(e);
1193 idx = precache_sound_index(samp);
1198 atten = floor(atten * 64);
1199 vol = floor(vol * 255);
1202 sflags |= SND_VOLUME;
1204 sflags |= SND_ATTENUATION;
1206 sflags |= SND_LARGEENTITY;
1208 sflags |= SND_LARGESOUND;
1210 WriteByte(dest, SVC_SOUND);
1211 WriteByte(dest, sflags);
1212 if (sflags & SND_VOLUME)
1213 WriteByte(dest, vol);
1214 if (sflags & SND_ATTENUATION)
1215 WriteByte(dest, atten);
1216 if (sflags & SND_LARGEENTITY)
1218 WriteShort(dest, entno);
1219 WriteByte(dest, chan);
1223 WriteShort(dest, entno * 8 + chan);
1225 if (sflags & SND_LARGESOUND)
1226 WriteShort(dest, idx);
1228 WriteByte(dest, idx);
1230 WriteCoord(dest, o_x);
1231 WriteCoord(dest, o_y);
1232 WriteCoord(dest, o_z);
1234 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1238 if (!sound_allowed(dest, e))
1241 o = e.origin + 0.5 * (e.mins + e.maxs);
1242 soundtoat(dest, e, o, chan, samp, vol, atten);
1244 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1246 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1248 void stopsoundto(float dest, entity e, float chan)
1252 if (!sound_allowed(dest, e))
1255 entno = num_for_edict(e);
1260 idx = precache_sound_index("misc/null.wav");
1261 sflags = SND_LARGEENTITY;
1263 sflags |= SND_LARGESOUND;
1264 WriteByte(dest, SVC_SOUND);
1265 WriteByte(dest, sflags);
1266 WriteShort(dest, entno);
1267 WriteByte(dest, chan);
1268 if (sflags & SND_LARGESOUND)
1269 WriteShort(dest, idx);
1271 WriteByte(dest, idx);
1272 WriteCoord(dest, e.origin_x);
1273 WriteCoord(dest, e.origin_y);
1274 WriteCoord(dest, e.origin_z);
1278 WriteByte(dest, SVC_STOPSOUND);
1279 WriteShort(dest, entno * 8 + chan);
1282 void stopsound(entity e, float chan)
1284 if (!sound_allowed(MSG_BROADCAST, e))
1287 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1288 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1291 void play2(entity e, string filename)
1293 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1295 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1298 .float announcetime;
1299 float announce(entity player, string msg)
1301 if (time > player.announcetime)
1302 if (clienttype(player) == CLIENTTYPE_REAL)
1304 player.announcetime = time + 0.8;
1310 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1311 float spamsound(entity e, float chan, string samp, float vol, float atten)
1313 if (!sound_allowed(MSG_BROADCAST, e))
1316 if (time > e.announcetime)
1318 e.announcetime = time;
1319 sound(e, chan, samp, vol, atten);
1325 void play2team(float t, string filename)
1329 if (cvar("bot_sound_monopoly"))
1332 FOR_EACH_REALPLAYER(head)
1335 play2(head, filename);
1339 void play2all(string samp)
1341 if (cvar("bot_sound_monopoly"))
1344 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1347 void PrecachePlayerSounds(string f);
1348 void precache_all_models(string pattern)
1350 float globhandle, i, n;
1353 globhandle = search_begin(pattern, TRUE, FALSE);
1356 n = search_getsize(globhandle);
1357 for (i = 0; i < n; ++i)
1359 //print(search_getfilename(globhandle, i), "\n");
1360 f = search_getfilename(globhandle, i);
1363 if(substring(f, -9,5) == "_lod1")
1365 if(substring(f, -9,5) == "_lod2")
1367 if(!sv_loddistance1)
1369 PrecachePlayerSounds(strcat(f, ".sounds"));
1371 search_end(globhandle);
1376 // gamemode related things
1377 precache_model ("models/misc/chatbubble.spr");
1378 precache_model ("models/misc/teambubble.spr");
1381 precache_model ("models/runematch/curse.mdl");
1382 precache_model ("models/runematch/rune.mdl");
1385 #ifdef TTURRETS_ENABLED
1386 if (cvar("g_turrets"))
1390 // Precache all player models if desired
1391 if (cvar("sv_precacheplayermodels"))
1393 PrecachePlayerSounds("sound/player/default.sounds");
1394 precache_all_models("models/player/*.zym");
1395 precache_all_models("models/player/*.dpm");
1396 precache_all_models("models/player/*.md3");
1397 precache_all_models("models/player/*.psk");
1398 //precache_model("models/player/carni.zym");
1399 //precache_model("models/player/crash.zym");
1400 //precache_model("models/player/grunt.zym");
1401 //precache_model("models/player/headhunter.zym");
1402 //precache_model("models/player/insurrectionist.zym");
1403 //precache_model("models/player/jeandarc.zym");
1404 //precache_model("models/player/lurk.zym");
1405 //precache_model("models/player/lycanthrope.zym");
1406 //precache_model("models/player/marine.zym");
1407 //precache_model("models/player/nexus.zym");
1408 //precache_model("models/player/pyria.zym");
1409 //precache_model("models/player/shock.zym");
1410 //precache_model("models/player/skadi.zym");
1411 //precache_model("models/player/specop.zym");
1412 //precache_model("models/player/visitant.zym");
1415 if (cvar("sv_defaultcharacter"))
1418 s = cvar_string("sv_defaultplayermodel_red");
1422 PrecachePlayerSounds(strcat(s, ".sounds"));
1424 s = cvar_string("sv_defaultplayermodel_blue");
1428 PrecachePlayerSounds(strcat(s, ".sounds"));
1430 s = cvar_string("sv_defaultplayermodel_yellow");
1434 PrecachePlayerSounds(strcat(s, ".sounds"));
1436 s = cvar_string("sv_defaultplayermodel_pink");
1440 PrecachePlayerSounds(strcat(s, ".sounds"));
1442 s = cvar_string("sv_defaultplayermodel");
1446 PrecachePlayerSounds(strcat(s, ".sounds"));
1452 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1453 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1456 // gore and miscellaneous sounds
1457 //precache_sound ("misc/h2ohit.wav");
1458 precache_model ("models/hook.md3");
1459 precache_sound ("misc/armorimpact.wav");
1460 precache_sound ("misc/bodyimpact1.wav");
1461 precache_sound ("misc/bodyimpact2.wav");
1462 precache_sound ("misc/gib.wav");
1463 precache_sound ("misc/gib_splat01.wav");
1464 precache_sound ("misc/gib_splat02.wav");
1465 precache_sound ("misc/gib_splat03.wav");
1466 precache_sound ("misc/gib_splat04.wav");
1467 precache_sound ("misc/hit.wav");
1468 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1469 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1470 precache_sound ("misc/null.wav");
1471 precache_sound ("misc/spawn.wav");
1472 precache_sound ("misc/talk.wav");
1473 precache_sound ("misc/teleport.wav");
1474 precache_sound ("misc/poweroff.wav");
1475 precache_sound ("player/lava.wav");
1476 precache_sound ("player/slime.wav");
1479 precache_sound ("misc/jetpack_fly.wav");
1481 // announcer sounds - male
1482 precache_sound ("announcer/male/electrobitch.wav");
1483 precache_sound ("announcer/male/airshot.wav");
1484 precache_sound ("announcer/male/03kills.wav");
1485 precache_sound ("announcer/male/05kills.wav");
1486 precache_sound ("announcer/male/10kills.wav");
1487 precache_sound ("announcer/male/15kills.wav");
1488 precache_sound ("announcer/male/20kills.wav");
1489 precache_sound ("announcer/male/25kills.wav");
1490 precache_sound ("announcer/male/30kills.wav");
1491 precache_sound ("announcer/male/botlike.wav");
1492 precache_sound ("announcer/male/yoda.wav");
1493 precache_sound ("announcer/male/amazing.wav");
1494 precache_sound ("announcer/male/awesome.wav");
1495 precache_sound ("announcer/male/headshot.wav");
1496 precache_sound ("announcer/male/impressive.wav");
1498 // announcer sounds - robotic
1499 precache_sound ("announcer/robotic/prepareforbattle.wav");
1500 precache_sound ("announcer/robotic/begin.wav");
1501 precache_sound ("announcer/robotic/timeoutcalled.wav");
1502 precache_sound ("announcer/robotic/1fragleft.wav");
1503 precache_sound ("announcer/robotic/2fragsleft.wav");
1504 precache_sound ("announcer/robotic/3fragsleft.wav");
1507 precache_sound ("announcer/robotic/lastsecond.wav");
1508 precache_sound ("announcer/robotic/narrowly.wav");
1511 precache_model ("models/sprites/0.spr32");
1512 precache_model ("models/sprites/1.spr32");
1513 precache_model ("models/sprites/2.spr32");
1514 precache_model ("models/sprites/3.spr32");
1515 precache_model ("models/sprites/4.spr32");
1516 precache_model ("models/sprites/5.spr32");
1517 precache_model ("models/sprites/6.spr32");
1518 precache_model ("models/sprites/7.spr32");
1519 precache_model ("models/sprites/8.spr32");
1520 precache_model ("models/sprites/9.spr32");
1521 precache_model ("models/sprites/10.spr32");
1522 precache_sound ("announcer/robotic/1.wav");
1523 precache_sound ("announcer/robotic/2.wav");
1524 precache_sound ("announcer/robotic/3.wav");
1525 precache_sound ("announcer/robotic/4.wav");
1526 precache_sound ("announcer/robotic/5.wav");
1527 precache_sound ("announcer/robotic/6.wav");
1528 precache_sound ("announcer/robotic/7.wav");
1529 precache_sound ("announcer/robotic/8.wav");
1530 precache_sound ("announcer/robotic/9.wav");
1531 precache_sound ("announcer/robotic/10.wav");
1533 // common weapon precaches
1534 precache_sound ("weapons/weapon_switch.wav");
1535 precache_sound ("weapons/weaponpickup.wav");
1536 precache_sound ("weapons/unavailable.wav");
1537 if (g_grappling_hook)
1539 precache_sound ("weapons/hook_fire.wav"); // hook
1540 precache_sound ("weapons/hook_impact.wav"); // hook
1543 if (cvar("sv_precacheweapons") || g_nixnex)
1545 //precache weapon models/sounds
1548 while (wep <= WEP_LAST)
1550 weapon_action(wep, WR_PRECACHE);
1555 precache_model("models/elaser.mdl");
1556 precache_model("models/laser.mdl");
1557 precache_model("models/ebomb.mdl");
1560 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1562 if (!self.noise && self.music) // quake 3 uses the music field
1563 self.noise = self.music;
1565 // plays music for the level if there is any
1568 precache_sound (self.noise);
1569 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1574 // sorry, but using \ in macros breaks line numbers
1575 #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
1576 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1577 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1579 vector ExactTriggerHit_mins;
1580 vector ExactTriggerHit_maxs;
1581 float ExactTriggerHit_Recurse()
1587 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1590 if (trace_ent == self)
1595 se.solid = SOLID_NOT;
1596 f = ExactTriggerHit_Recurse();
1602 float ExactTriggerHit()
1606 if not(self.modelindex)
1610 self.solid = SOLID_BSP;
1611 ExactTriggerHit_mins = other.absmin;
1612 ExactTriggerHit_maxs = other.absmax;
1613 f = ExactTriggerHit_Recurse();
1619 // WARNING: this kills the trace globals
1620 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1621 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1623 #define INITPRIO_FIRST 0
1624 #define INITPRIO_GAMETYPE 0
1625 #define INITPRIO_GAMETYPE_FALLBACK 1
1626 #define INITPRIO_CVARS 5
1627 #define INITPRIO_FINDTARGET 10
1628 #define INITPRIO_DROPTOFLOOR 20
1629 #define INITPRIO_SETLOCATION 90
1630 #define INITPRIO_LINKDOORS 91
1631 #define INITPRIO_LAST 99
1633 .void(void) initialize_entity;
1634 .float initialize_entity_order;
1635 .entity initialize_entity_next;
1636 entity initialize_entity_first;
1638 void make_safe_for_remove(entity e)
1640 if (e.initialize_entity)
1643 for (ent = initialize_entity_first; ent; )
1645 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1647 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1648 // skip it in linked list
1651 prev.initialize_entity_next = ent.initialize_entity_next;
1652 ent = prev.initialize_entity_next;
1656 initialize_entity_first = ent.initialize_entity_next;
1657 ent = initialize_entity_first;
1663 ent = ent.initialize_entity_next;
1669 void objerror(string s)
1671 make_safe_for_remove(self);
1672 objerror_builtin(s);
1675 void remove_unsafely(entity e)
1680 void remove_safely(entity e)
1682 make_safe_for_remove(e);
1686 void InitializeEntity(entity e, void(void) func, float order)
1690 if (!e || e.initialize_entity)
1692 // make a proxy initializer entity
1696 e.classname = "initialize_entity";
1700 e.initialize_entity = func;
1701 e.initialize_entity_order = order;
1703 cur = initialize_entity_first;
1706 if (!cur || cur.initialize_entity_order > order)
1708 // insert between prev and cur
1710 prev.initialize_entity_next = e;
1712 initialize_entity_first = e;
1713 e.initialize_entity_next = cur;
1717 cur = cur.initialize_entity_next;
1720 void InitializeEntitiesRun()
1723 startoflist = initialize_entity_first;
1724 initialize_entity_first = world;
1725 for (self = startoflist; self; )
1728 var void(void) func;
1729 e = self.initialize_entity_next;
1730 func = self.initialize_entity;
1731 self.initialize_entity_order = 0;
1732 self.initialize_entity = func_null;
1733 self.initialize_entity_next = world;
1734 if (self.classname == "initialize_entity")
1738 remove_builtin(self);
1741 //dprint("Delayed initialization: ", self.classname, "\n");
1747 .float uncustomizeentityforclient_set;
1748 .void(void) uncustomizeentityforclient;
1749 void(void) SUB_Nullpointer = #0;
1750 void UncustomizeEntitiesRun()
1754 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1755 self.uncustomizeentityforclient();
1758 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1760 e.customizeentityforclient = customizer;
1761 e.uncustomizeentityforclient = uncustomizer;
1762 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1766 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1769 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1773 if (e.classname == "")
1774 e.classname = "net_linked";
1776 if (e.model == "" || self.modelindex == 0)
1780 setmodel(e, "null");
1784 e.SendEntity = sendfunc;
1785 e.SendFlags = 0xFFFFFF;
1788 e.effects |= EF_NODEPTHTEST;
1792 e.nextthink = time + dt;
1793 e.think = SUB_Remove;
1797 void adaptor_think2touch()
1806 void adaptor_think2use()
1818 // deferred dropping
1819 void DropToFloor_Handler()
1821 droptofloor_builtin();
1822 self.dropped_origin = self.origin;
1827 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1832 float trace_hits_box_a0, trace_hits_box_a1;
1834 float trace_hits_box_1d(float end, float thmi, float thma)
1838 // just check if x is in range
1846 // do the trace with respect to x
1847 // 0 -> end has to stay in thmi -> thma
1848 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1849 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1850 if (trace_hits_box_a0 > trace_hits_box_a1)
1856 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1861 // now it is a trace from 0 to end
1863 trace_hits_box_a0 = 0;
1864 trace_hits_box_a1 = 1;
1866 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1868 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1870 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1876 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1878 return trace_hits_box(start, end, thmi - ma, thma - mi);
1881 float SUB_NoImpactCheck()
1883 // zero hitcontents = this is not the real impact, but either the
1884 // mirror-impact of something hitting the projectile instead of the
1885 // projectile hitting the something, or a touchareagrid one. Neither of
1886 // these stop the projectile from moving, so...
1887 if(trace_dphitcontents == 0)
1889 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1892 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1894 if (other == world && self.size != '0 0 0')
1897 tic = self.velocity * sys_ticrate;
1898 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1899 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1900 if (trace_fraction >= 1)
1902 dprint("Odd... did not hit...?\n");
1904 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1906 dprint("Detected and prevented the sky-grapple bug.\n");
1914 #define SUB_OwnerCheck() (other && (other == self.owner))
1916 #define PROJECTILE_TOUCH do { if(SUB_OwnerCheck()) return; if(SUB_NoImpactCheck()) { remove(self); return; } if(trace_ent && trace_ent.solid > SOLID_TRIGGER) UpdateCSQCProjectileNextFrame(self); } while(0)
1918 float MAX_IPBAN_URIS = 16;
1920 float URI_GET_DISCARD = 0;
1921 float URI_GET_IPBAN = 1;
1922 float URI_GET_IPBAN_END = 16;
1924 void URI_Get_Callback(float id, float status, string data)
1926 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1928 dprint("\nEnd of data.\n");
1930 if (id == URI_GET_DISCARD)
1934 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1937 OnlineBanList_URI_Get_Callback(id, status, data);
1941 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1945 void print_to(entity e, string s)
1948 sprint(e, strcat(s, "\n"));
1967 for (i = 0; i < MapInfo_count; ++i)
1969 if (MapInfo_Get_ByID(i))
1971 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1974 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1975 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1983 for (i = 0; i < MapInfo_count; ++i)
1985 if (MapInfo_Get_ByID(i))
1987 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1990 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1991 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1999 for (i = 0; i < MapInfo_count; ++i)
2001 if (MapInfo_Get_ByID(i))
2003 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2006 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2007 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2013 MapInfo_ClearTemps();
2016 return "No records are available on this server.\n";
2018 return strcat("Records on this server:\n", s);
2021 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2024 vector start, org, delta, end, enddown, mstart;
2026 m = e.dphitcontentsmask;
2027 e.dphitcontentsmask = goodcontents | badcontents;
2030 delta = world.maxs - world.mins;
2032 for (i = 0; i < attempts; ++i)
2034 start_x = org_x + random() * delta_x;
2035 start_y = org_y + random() * delta_y;
2036 start_z = org_z + random() * delta_z;
2038 // rule 1: start inside world bounds, and outside
2039 // solid, and don't start from somewhere where you can
2040 // fall down to evil
2041 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2042 if (trace_fraction >= 1)
2044 if (trace_startsolid)
2046 if (trace_dphitcontents & badcontents)
2048 if (trace_dphitq3surfaceflags & badsurfaceflags)
2051 // rule 2: if we are too high, lower the point
2052 if (trace_fraction * delta_z > maxaboveground)
2053 start = trace_endpos + '0 0 1' * maxaboveground;
2054 enddown = trace_endpos;
2056 // rule 3: make sure we aren't outside the map. This only works
2057 // for somewhat well formed maps. A good rule of thumb is that
2058 // the map should have a convex outside hull.
2059 // these can be traceLINES as we already verified the starting box
2060 mstart = start + 0.5 * (e.mins + e.maxs);
2061 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2062 if (trace_fraction >= 1)
2064 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2065 if (trace_fraction >= 1)
2067 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2068 if (trace_fraction >= 1)
2070 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2071 if (trace_fraction >= 1)
2073 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2074 if (trace_fraction >= 1)
2077 // find a random vector to "look at"
2078 end_x = org_x + random() * delta_x;
2079 end_y = org_y + random() * delta_y;
2080 end_z = org_z + random() * delta_z;
2081 end = start + normalize(end - start) * vlen(delta);
2083 // rule 4: start TO end must not be too short
2084 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2085 if (trace_startsolid)
2087 if (trace_fraction < minviewdistance / vlen(delta))
2090 // rule 5: don't want to look at sky
2091 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2094 // rule 6: we must not end up in trigger_hurt
2095 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2097 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2104 e.dphitcontentsmask = m;
2108 setorigin(e, start);
2109 e.angles = vectoangles(end - start);
2110 dprint("Needed ", ftos(i + 1), " attempts\n");
2117 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2119 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2120 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2121 WriteShort(MSG_BROADCAST, effectno);
2122 WriteCoord(MSG_BROADCAST, start_x);
2123 WriteCoord(MSG_BROADCAST, start_y);
2124 WriteCoord(MSG_BROADCAST, start_z);
2125 WriteCoord(MSG_BROADCAST, end_x);
2126 WriteCoord(MSG_BROADCAST, end_y);
2127 WriteCoord(MSG_BROADCAST, end_z);
2128 WriteCoord(MSG_BROADCAST, end_dz);
2129 WriteShort(MSG_BROADCAST, spd / 16);
2132 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2135 vector vecxy, velxy;
2137 vecxy = end - start;
2142 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2144 trailparticles(world, effectno, start, end);
2148 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2149 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2152 string GetGametype(); // g_world.qc
2153 void write_recordmarker(entity pl, float tstart, float dt)
2155 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2157 // also write a marker into demo files for demotc-race-record-extractor to find
2160 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2161 " ", ftos(tstart), " ", ftos(dt), "\n"));
2164 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2169 if (cvar("g_shootfromclient"))
2171 switch(self.owner.cvar_cl_gunalign)
2187 else if (cvar("g_shootfromeye"))
2200 else if (cvar("g_shootfromcenter"))
2205 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2220 void attach_sameorigin(entity e, entity to, string tag)
2222 vector org, t_forward, t_left, t_up, e_forward, e_up;
2229 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2230 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2231 t_forward = v_forward * tagscale;
2232 t_left = v_right * -tagscale;
2233 t_up = v_up * tagscale;
2235 e.origin_x = org * t_forward;
2236 e.origin_y = org * t_left;
2237 e.origin_z = org * t_up;
2239 // current forward and up directions
2240 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2241 e.angles_x = -e.angles_x;
2242 fixedmakevectors(e.angles);
2244 // untransform forward, up!
2245 e_forward_x = v_forward * t_forward;
2246 e_forward_y = v_forward * t_left;
2247 e_forward_z = v_forward * t_up;
2248 e_up_x = v_up * t_forward;
2249 e_up_y = v_up * t_left;
2250 e_up_z = v_up * t_up;
2252 e.angles = fixedvectoangles2(e_forward, e_up);
2253 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2254 e.angles_x = -e.angles_x;
2256 setattachment(e, to, tag);
2257 setorigin(e, e.origin);
2260 void detach_sameorigin(entity e)
2263 org = gettaginfo(e, 0);
2264 e.angles = fixedvectoangles2(v_forward, v_up);
2265 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2266 e.angles_x = -e.angles_x;
2268 setattachment(e, world, "");
2269 setorigin(e, e.origin);
2272 void follow_sameorigin(entity e, entity to)
2274 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2275 e.aiment = to; // make the hole follow bmodel
2276 e.punchangle = to.angles; // the original angles of bmodel
2277 e.view_ofs = e.origin - to.origin; // relative origin
2278 e.v_angle = e.angles - to.angles; // relative angles
2281 void unfollow_sameorigin(entity e)
2283 e.movetype = MOVETYPE_NONE;
2286 entity gettaginfo_relative_ent;
2287 vector gettaginfo_relative(entity e, float tag)
2289 if (!gettaginfo_relative_ent)
2291 gettaginfo_relative_ent = spawn();
2292 gettaginfo_relative_ent.effects = EF_NODRAW;
2294 gettaginfo_relative_ent.model = e.model;
2295 gettaginfo_relative_ent.modelindex = e.modelindex;
2296 gettaginfo_relative_ent.frame = e.frame;
2297 return gettaginfo(gettaginfo_relative_ent, tag);
2300 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2304 if (pl.soundentity.cnt & p)
2306 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2307 pl.soundentity.cnt |= p;
2310 void SoundEntity_StopSound(entity pl, float chan)
2314 if (pl.soundentity.cnt & p)
2316 stopsoundto(MSG_ALL, pl.soundentity, chan);
2317 pl.soundentity.cnt &~= p;
2321 void SoundEntity_Attach(entity pl)
2323 pl.soundentity = spawn();
2324 pl.soundentity.classname = "soundentity";
2325 pl.soundentity.owner = pl;
2326 setattachment(pl.soundentity, pl, "");
2327 setmodel(pl.soundentity, "null");
2330 void SoundEntity_Detach(entity pl)
2333 for (i = 0; i <= 7; ++i)
2334 SoundEntity_StopSound(pl, i);
2338 float ParseCommandPlayerSlotTarget_firsttoken;
2339 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2347 ParseCommandPlayerSlotTarget_firsttoken = -1;
2351 if (substring(argv(idx), 0, 1) == "#")
2353 s = substring(argv(idx), 1, -1);
2361 if (s == ftos(stof(s)))
2363 e = edict_num(stof(s));
2364 if (e.flags & FL_CLIENT)
2366 ParseCommandPlayerSlotTarget_firsttoken = idx;
2373 // it must be a nick name
2378 FOR_EACH_CLIENT(head)
2379 if (head.netname == s)
2386 ParseCommandPlayerSlotTarget_firsttoken = idx;
2390 s = strdecolorize(s);
2392 FOR_EACH_CLIENT(head)
2393 if (strdecolorize(head.netname) == s)
2400 ParseCommandPlayerSlotTarget_firsttoken = idx;