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);
67 move_out_of_solid_expand(e, '1 0 0' * m0_x); e.mins_x = m0_x;
68 move_out_of_solid_expand(e, '1 0 0' * m1_x); e.maxs_x = m1_x;
69 move_out_of_solid_expand(e, '0 1 0' * m0_y); e.mins_y = m0_y;
70 move_out_of_solid_expand(e, '0 1 0' * m1_y); e.maxs_y = m1_y;
71 move_out_of_solid_expand(e, '0 0 1' * m0_z); e.mins_z = m0_z;
72 move_out_of_solid_expand(e, '0 0 1' * m1_z); e.maxs_z = m1_z;
73 setorigin(e, e.origin);
75 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
85 string STR_PLAYER = "player";
86 string STR_SPECTATOR = "spectator";
87 string STR_OBSERVER = "observer";
90 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
91 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
92 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
93 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
95 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
96 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
97 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
98 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
102 // copies a string to a tempstring (so one can strunzone it)
103 string strcat1(string s) = #115; // FRIK_FILE
108 void bcenterprint(string s)
110 // TODO replace by MSG_ALL (would show it to spectators too, though)?
112 FOR_EACH_PLAYER(head)
113 if(clienttype(head) == CLIENTTYPE_REAL)
114 centerprint(head, s);
117 void GameLogEcho(string s)
122 if(cvar("sv_eventlog_files"))
127 matches = cvar("sv_eventlog_files_counter") + 1;
128 cvar_set("sv_eventlog_files_counter", ftos(matches));
131 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
132 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
133 logfile = fopen(fn, FILE_APPEND);
134 fputs(logfile, ":logversion:3\n");
138 if(cvar("sv_eventlog_files_timestamps"))
139 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
141 fputs(logfile, strcat(s, "\n"));
144 if(cvar("sv_eventlog_console"))
153 // will be opened later
158 if(logfile_open && logfile >= 0)
165 float spawnpoint_nag;
166 void relocate_spawnpoint()
168 // nudge off the floor
169 setorigin(self, self.origin + '0 0 1');
171 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
172 if (trace_startsolid)
178 if(!move_out_of_solid(self))
179 objerror("could not get out of solid at all!");
180 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
181 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
182 print(" ", ftos(self.origin_y - o_y));
183 print(" ", ftos(self.origin_z - o_z), "'\n");
184 if(cvar("g_spawnpoints_auto_move_out_of_solid"))
187 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
193 self.mins = self.maxs = '0 0 0';
194 objerror("player spawn point in solid, mapper sucks!\n");
199 if(cvar("g_spawnpoints_autodrop"))
201 setsize(self, PL_MIN, PL_MAX);
205 self.use = spawnpoint_use;
206 self.team_saved = self.team;
210 if(g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
212 have_team_spawns = 1;
214 if(cvar("r_showbboxes"))
216 // show where spawnpoints point at too
217 makevectors(self.angles);
220 e.classname = "info_player_foo";
221 setorigin(e, self.origin + v_forward * 24);
222 setsize(e, '-8 -8 -8', '8 8 8');
223 e.solid = SOLID_TRIGGER;
227 #define strstr strstrofs
229 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
230 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
231 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
232 // BE CONSTANT OR strzoneD!
233 float strstr(string haystack, string needle, float offset)
237 len = strlen(needle);
238 endpos = strlen(haystack) - len;
239 while(offset <= endpos)
241 found = substring(haystack, offset, len);
250 float NUM_NEAREST_ENTITIES = 4;
251 entity nearest_entity[NUM_NEAREST_ENTITIES];
252 float nearest_length[NUM_NEAREST_ENTITIES];
253 entity findnearest(vector point, .string field, string value, vector axismod)
264 localhead = find(world, field, value);
267 if((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
268 dist = localhead.oldorigin;
270 dist = localhead.origin;
272 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
275 for(i = 0; i < num_nearest; ++i)
277 if(len < nearest_length[i])
281 // now i tells us where to insert at
282 // INSERTION SORT! YOU'VE SEEN IT! RUN!
283 if(i < NUM_NEAREST_ENTITIES)
285 for(j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
287 nearest_length[j + 1] = nearest_length[j];
288 nearest_entity[j + 1] = nearest_entity[j];
290 nearest_length[i] = len;
291 nearest_entity[i] = localhead;
292 if(num_nearest < NUM_NEAREST_ENTITIES)
293 num_nearest = num_nearest + 1;
296 localhead = find(localhead, field, value);
299 // now use the first one from our list that we can see
300 for(i = 0; i < num_nearest; ++i)
302 traceline(point, nearest_entity[i].origin, TRUE, world);
303 if(trace_fraction == 1)
307 dprint("Nearest point (");
308 dprint(nearest_entity[0].netname);
309 dprint(") is not visible, using a visible one.\n");
311 return nearest_entity[i];
318 dprint("Not seeing any location point, using nearest as fallback.\n");
320 dprint("Candidates were: ");
321 for(j = 0; j < num_nearest; ++j)
325 dprint(nearest_entity[j].netname);
330 return nearest_entity[0];
333 void spawnfunc_target_location()
335 self.classname = "target_location";
336 // location name in netname
337 // eventually support: count, teamgame selectors, line of sight?
340 void spawnfunc_info_location()
342 self.classname = "target_location";
343 self.message = self.netname;
346 string NearestLocation(vector p)
351 loc = findnearest(p, classname, "target_location", '1 1 1');
358 loc = findnearest(p, target, "###item###", '1 1 4');
365 string formatmessage(string msg)
376 break; // too many replacements
378 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
379 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
389 replacement = substring(msg, p, 2);
390 escape = substring(msg, p + 1, 1);
393 else if(escape == "\\")
395 else if(escape == "n")
397 else if(escape == "a")
398 replacement = ftos(floor(self.armorvalue));
399 else if(escape == "h")
400 replacement = ftos(floor(self.health));
401 else if(escape == "l")
402 replacement = NearestLocation(self.origin);
403 else if(escape == "y")
404 replacement = NearestLocation(self.cursor_trace_endpos);
405 else if(escape == "d")
406 replacement = NearestLocation(self.death_origin);
407 else if(escape == "w")
412 wep = self.switchweapon;
415 replacement = W_Name(wep);
417 else if(escape == "W")
419 if(self.items & IT_SHELLS) replacement = "shells";
420 else if(self.items & IT_NAILS) replacement = "bullets";
421 else if(self.items & IT_ROCKETS) replacement = "rockets";
422 else if(self.items & IT_CELLS) replacement = "cells";
423 else replacement = "batteries"; // ;)
425 else if(escape == "x")
427 replacement = self.cursor_trace_ent.netname;
428 if(!replacement || !self.cursor_trace_ent)
429 replacement = "nothing";
431 else if(escape == "p")
433 if(self.last_selected_player)
434 replacement = self.last_selected_player.netname;
436 replacement = "(nobody)";
438 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
439 p = p + strlen(replacement);
450 >0: receives a cvar from name=argv(f) value=argv(f+1)
452 void GetCvars_handleString(string thisname, float f, .string field, string name)
457 strunzone(self.field);
458 self.field = string_null;
465 strunzone(self.field);
466 self.field = strzone(argv(f + 1));
470 stuffcmd(self, strcat("sendcvar ", name, "\n"));
472 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
474 GetCvars_handleString(thisname, f, field, name);
475 if(f >= 0) // also initialize to the fitting value for "" when sending cvars out
479 s = func(strcat1(self.field));
482 strunzone(self.field);
483 self.field = strzone(s);
487 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
495 self.field = stof(argv(f + 1));
498 stuffcmd(self, strcat("sendcvar ", name, "\n"));
500 string W_FixWeaponOrder_ForceComplete(string s);
501 string W_FixWeaponOrder_AllowIncomplete(string s);
502 float w_getbestweapon(entity e);
503 void GetCvars(float f)
507 s = strcat1(argv(f));
508 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
509 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
510 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
511 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
512 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
513 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
514 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
515 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
516 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
517 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
518 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
519 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
520 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
521 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
522 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
523 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
524 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
525 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
526 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
527 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
528 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
530 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
533 if(s == "cl_weaponpriority")
534 self.switchweapon = w_getbestweapon(self);
538 float fexists(string f)
541 fh = fopen(f, FILE_READ);
548 void backtrace(string msg)
551 dev = cvar("developer");
552 cvar_set("developer", "1");
554 dprint("--- CUT HERE ---\nWARNING: ");
557 remove(world); // isn't there any better way to cause a backtrace?
558 dprint("\n--- CUT UNTIL HERE ---\n");
559 cvar_set("developer", ftos(dev));
562 string Team_ColorCode(float teamid)
564 if(teamid == COLOR_TEAM1)
566 else if(teamid == COLOR_TEAM2)
568 else if(teamid == COLOR_TEAM3)
570 else if(teamid == COLOR_TEAM4)
575 string Team_ColorName(float t)
577 // fixme: Search for team entities and get their .netname's!
588 string Team_ColorNameLowerCase(float t)
590 // fixme: Search for team entities and get their .netname's!
602 #define CENTERPRIO_POINT 1
603 #define CENTERPRIO_SPAM 2
604 #define CENTERPRIO_VOTE 4
605 #define CENTERPRIO_NORMAL 5
606 #define CENTERPRIO_SHIELDING 7
607 #define CENTERPRIO_MAPVOTE 9
608 #define CENTERPRIO_IDLEKICK 50
609 #define CENTERPRIO_ADMIN 99
610 .float centerprint_priority;
611 .float centerprint_expires;
612 void centerprint_atprio(entity e, float prio, string s)
614 if(intermission_running)
615 if(prio < CENTERPRIO_MAPVOTE)
617 if(time > e.centerprint_expires)
618 e.centerprint_priority = 0;
619 if(prio >= e.centerprint_priority)
621 e.centerprint_priority = prio;
622 if(timeoutStatus == 2)
623 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
625 e.centerprint_expires = time + e.cvar_scr_centertime;
626 centerprint_builtin(e, s);
629 void centerprint_expire(entity e, float prio)
631 if(prio == e.centerprint_priority)
633 e.centerprint_priority = 0;
634 centerprint_builtin(e, "");
637 void centerprint(entity e, string s)
639 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
642 // decolorizes and team colors the player name when needed
643 string playername(entity p)
646 if(teams_matter && !intermission_running && p.classname == "player")
648 t = Team_ColorCode(p.team);
649 return strcat(t, strdecolorize(p.netname));
655 vector randompos(vector m1, vector m2)
659 v_x = m2_x * random() + m1_x;
660 v_y = m2_y * random() + m1_y;
661 v_z = m2_z * random() + m1_z;
665 float g_pickup_shells;
666 float g_pickup_shells_max;
667 float g_pickup_nails;
668 float g_pickup_nails_max;
669 float g_pickup_rockets;
670 float g_pickup_rockets_max;
671 float g_pickup_cells;
672 float g_pickup_cells_max;
674 float g_pickup_fuel_jetpack;
675 float g_pickup_fuel_max;
676 float g_pickup_armorsmall;
677 float g_pickup_armorsmall_max;
678 float g_pickup_armormedium;
679 float g_pickup_armormedium_max;
680 float g_pickup_armorbig;
681 float g_pickup_armorbig_max;
682 float g_pickup_armorlarge;
683 float g_pickup_armorlarge_max;
684 float g_pickup_healthsmall;
685 float g_pickup_healthsmall_max;
686 float g_pickup_healthmedium;
687 float g_pickup_healthmedium_max;
688 float g_pickup_healthlarge;
689 float g_pickup_healthlarge_max;
690 float g_pickup_healthmega;
691 float g_pickup_healthmega_max;
693 string g_weaponarena_list;
697 float start_ammo_shells;
698 float start_ammo_nails;
699 float start_ammo_rockets;
700 float start_ammo_cells;
701 float start_ammo_fuel;
703 float start_armorvalue;
704 float warmup_start_weapons;
705 float warmup_start_ammo_shells;
706 float warmup_start_ammo_nails;
707 float warmup_start_ammo_rockets;
708 float warmup_start_ammo_cells;
709 float warmup_start_ammo_fuel;
710 float warmup_start_health;
711 float warmup_start_armorvalue;
714 entity get_weaponinfo(float w);
716 float NixNex_CanChooseWeapon(float wpn);
717 void readplayerstartcvars()
723 // initialize starting values for players
726 start_ammo_shells = 0;
727 start_ammo_nails = 0;
728 start_ammo_rockets = 0;
729 start_ammo_cells = 0;
730 start_health = cvar("g_balance_health_start");
731 start_armorvalue = cvar("g_balance_armor_start");
734 s = cvar_string("g_weaponarena");
740 g_weaponarena_list = "All Weapons";
741 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
743 e = get_weaponinfo(j);
744 g_weaponarena |= e.weapons;
745 weapon_action(e.weapon, WR_PRECACHE);
750 g_weaponarena_list = "Most Weapons";
751 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
753 e = get_weaponinfo(j);
754 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
756 g_weaponarena |= e.weapons;
757 weapon_action(e.weapon, WR_PRECACHE);
763 t = tokenize_console(s);
764 g_weaponarena_list = "";
765 for(i = 0; i < t; ++i)
768 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
770 e = get_weaponinfo(j);
773 g_weaponarena |= e.weapons;
774 weapon_action(e.weapon, WR_PRECACHE);
775 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
781 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
784 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
790 // will be done later
791 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
792 if(NixNex_CanChooseWeapon(i))
793 weapon_action(i, WR_PRECACHE);
795 else if(g_weaponarena)
797 start_weapons = g_weaponarena;
798 start_ammo_rockets = 999;
799 start_ammo_shells = 999;
800 start_ammo_cells = 999;
801 start_ammo_nails = 999;
802 start_ammo_fuel = 999;
803 start_items |= IT_UNLIMITED_AMMO;
808 start_armorvalue = 0;
809 start_weapons = WEPBIT_MINSTANEX;
810 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
811 start_ammo_cells = cvar("g_minstagib_ammo_start");
812 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
813 start_ammo_fuel = cvar("g_start_ammo_fuel");
815 if(g_minstagib_invis_alpha <= 0)
816 g_minstagib_invis_alpha = -1;
822 start_ammo_shells = cvar("g_lms_start_ammo_shells");
823 start_ammo_nails = cvar("g_lms_start_ammo_nails");
824 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
825 start_ammo_cells = cvar("g_lms_start_ammo_cells");
826 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
827 start_health = cvar("g_lms_start_health");
828 start_armorvalue = cvar("g_lms_start_armor");
829 } else if (cvar("g_use_ammunition")) {
830 start_ammo_shells = cvar("g_start_ammo_shells");
831 start_ammo_nails = cvar("g_start_ammo_nails");
832 start_ammo_rockets = cvar("g_start_ammo_rockets");
833 start_ammo_cells = cvar("g_start_ammo_cells");
834 start_ammo_fuel = cvar("g_start_ammo_fuel");
836 start_ammo_shells = cvar("g_pickup_shells_max");
837 start_ammo_nails = cvar("g_pickup_nails_max");
838 start_ammo_rockets = cvar("g_pickup_rockets_max");
839 start_ammo_cells = cvar("g_pickup_cells_max");
840 start_ammo_fuel = cvar("g_pickup_fuel_max");
841 start_items |= IT_UNLIMITED_AMMO;
844 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
846 e = get_weaponinfo(i);
850 t = cvar(strcat("g_start_weapon_", e.netname));
852 if(t < 0) // "default" weapon selection
855 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
857 t = (i == WEP_LASER);
859 t = 0; // weapon is set a few lines later
861 t = (i == WEP_LASER || i == WEP_SHOTGUN);
862 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
863 t += (i == WEP_HOOK);
866 if(g_nexball && i == WEP_PORTO)
871 start_weapons |= e.weapons;
872 weapon_action(e.weapon, WR_PRECACHE);
879 warmup_start_ammo_shells = start_ammo_shells;
880 warmup_start_ammo_nails = start_ammo_nails;
881 warmup_start_ammo_rockets = start_ammo_rockets;
882 warmup_start_ammo_cells = start_ammo_cells;
883 warmup_start_health = start_health;
884 warmup_start_armorvalue = start_armorvalue;
885 warmup_start_weapons = start_weapons;
887 if(!g_weaponarena && !g_nixnex && !g_minstagib)
889 if(cvar("g_use_ammunition"))
891 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
892 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
893 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
894 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
896 warmup_start_health = cvar("g_warmup_start_health");
897 warmup_start_armorvalue = cvar("g_warmup_start_armor");
898 if(cvar("g_warmup_allguns"))
900 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
902 e = get_weaponinfo(i);
905 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
907 warmup_start_weapons |= e.weapons;
908 weapon_action(e.weapon, WR_PRECACHE);
915 if(g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
917 g_grappling_hook = 0; // these two can't coexist, as they use the same button
918 start_items |= IT_FUEL_REGEN;
919 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
923 start_items |= IT_JETPACK;
925 if(g_weapon_stay == 2)
927 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
928 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
929 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
930 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
931 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
932 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
933 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
934 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
935 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
936 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
939 start_ammo_shells = max(0, start_ammo_shells);
940 start_ammo_nails = max(0, start_ammo_nails);
941 start_ammo_cells = max(0, start_ammo_cells);
942 start_ammo_rockets = max(0, start_ammo_rockets);
943 start_ammo_fuel = max(0, start_ammo_fuel);
945 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
946 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
947 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
948 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
949 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
953 float g_bugrigs_planar_movement;
954 float g_bugrigs_planar_movement_car_jumping;
955 float g_bugrigs_reverse_spinning;
956 float g_bugrigs_reverse_speeding;
957 float g_bugrigs_reverse_stopping;
958 float g_bugrigs_air_steering;
959 float g_bugrigs_angle_smoothing;
960 float g_bugrigs_friction_floor;
961 float g_bugrigs_friction_brake;
962 float g_bugrigs_friction_air;
963 float g_bugrigs_accel;
964 float g_bugrigs_speed_ref;
965 float g_bugrigs_speed_pow;
966 float g_bugrigs_steer;
968 float g_touchexplode;
969 float g_touchexplode_radius;
970 float g_touchexplode_damage;
971 float g_touchexplode_edgedamage;
972 float g_touchexplode_force;
974 void readlevelcvars(void)
976 g_bugrigs = cvar("g_bugrigs");
977 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
978 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
979 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
980 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
981 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
982 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
983 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
984 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
985 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
986 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
987 g_bugrigs_accel = cvar("g_bugrigs_accel");
988 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
989 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
990 g_bugrigs_steer = cvar("g_bugrigs_steer");
992 g_touchexplode = cvar("g_touchexplode");
993 g_touchexplode_radius = cvar("g_touchexplode_radius");
994 g_touchexplode_damage = cvar("g_touchexplode_damage");
995 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
996 g_touchexplode_force = cvar("g_touchexplode_force");
998 sv_clones = cvar("sv_clones");
999 sv_cheats = cvar("sv_cheats");
1000 sv_gentle = cvar("sv_gentle");
1001 sv_foginterval = cvar("sv_foginterval");
1002 g_cloaked = cvar("g_cloaked");
1003 g_jump_grunt = cvar("g_jump_grunt");
1004 g_footsteps = cvar("g_footsteps");
1005 g_grappling_hook = cvar("g_grappling_hook");
1006 g_jetpack = cvar("g_jetpack");
1007 g_laserguided_missile = cvar("g_laserguided_missile");
1008 g_midair = cvar("g_midair");
1009 g_minstagib = cvar("g_minstagib");
1010 g_nixnex = cvar("g_nixnex");
1011 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1012 g_norecoil = cvar("g_norecoil");
1013 g_vampire = cvar("g_vampire");
1014 g_bloodloss = cvar("g_bloodloss");
1015 sv_maxidle = cvar("sv_maxidle");
1016 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1017 sv_pogostick = cvar("sv_pogostick");
1018 sv_doublejump = cvar("sv_doublejump");
1019 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1020 g_ctf_reverse = cvar("g_ctf_reverse");
1022 inWarmupStage = cvar("g_warmup");
1023 g_warmup_limit = cvar("g_warmup_limit");
1024 g_warmup_allguns = cvar("g_warmup_allguns");
1025 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1027 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1028 inWarmupStage = 0; // these modes cannot work together, sorry
1030 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1031 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1032 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1033 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1034 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1035 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1036 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1037 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1038 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1039 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1040 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1041 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1043 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1044 if(g_nixnex) g_weaponarena = 0;
1047 g_pickup_shells = cvar("g_pickup_shells");
1048 g_pickup_shells_max = cvar("g_pickup_shells_max");
1049 g_pickup_nails = cvar("g_pickup_nails");
1050 g_pickup_nails_max = cvar("g_pickup_nails_max");
1051 g_pickup_rockets = cvar("g_pickup_rockets");
1052 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1053 g_pickup_cells = cvar("g_pickup_cells");
1054 g_pickup_cells_max = cvar("g_pickup_cells_max");
1055 g_pickup_fuel = cvar("g_pickup_fuel");
1056 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1057 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1058 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1059 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1060 g_pickup_armormedium = cvar("g_pickup_armormedium");
1061 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1062 g_pickup_armorbig = cvar("g_pickup_armorbig");
1063 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1064 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1065 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1066 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1067 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1068 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1069 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1070 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1071 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1072 g_pickup_healthmega = cvar("g_pickup_healthmega");
1073 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1075 g_pinata = cvar("g_pinata");
1077 g_weapon_stay = cvar("g_weapon_stay");
1078 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1081 if not(inWarmupStage)
1082 game_starttime = cvar("g_start_delay");
1084 readplayerstartcvars();
1088 // TODO sound pack system
1091 string precache_sound_builtin (string s) = #19;
1092 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1093 string precache_sound(string s)
1095 return precache_sound_builtin(strcat(soundpack, s));
1097 void play2(entity e, string filename)
1099 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1101 void sound(entity e, float chan, string samp, float vol, float atten)
1103 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1108 string precache_sound (string s) = #19;
1109 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1110 float precache_sound_index (string s) = #19;
1112 #define SND_VOLUME 1
1113 #define SND_ATTENUATION 2
1114 #define SND_LARGEENTITY 8
1115 #define SND_LARGESOUND 16
1117 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1120 entno = num_for_edict(e);
1121 idx = precache_sound_index(samp);
1126 atten = floor(atten * 64);
1127 vol = floor(vol * 255);
1130 sflags |= SND_VOLUME;
1132 sflags |= SND_ATTENUATION;
1134 sflags |= SND_LARGEENTITY;
1136 sflags |= SND_LARGESOUND;
1138 WriteByte(dest, SVC_SOUND);
1139 WriteByte(dest, sflags);
1140 if(sflags & SND_VOLUME)
1141 WriteByte(dest, vol);
1142 if(sflags & SND_ATTENUATION)
1143 WriteByte(dest, atten);
1144 if(sflags & SND_LARGEENTITY)
1146 WriteShort(dest, entno);
1147 WriteByte(dest, chan);
1151 WriteShort(dest, entno * 8 + chan);
1153 if(sflags & SND_LARGESOUND)
1154 WriteShort(dest, idx);
1156 WriteByte(dest, idx);
1158 WriteCoord(dest, o_x);
1159 WriteCoord(dest, o_y);
1160 WriteCoord(dest, o_z);
1162 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1165 o = e.origin + 0.5 * (e.mins + e.maxs);
1166 soundtoat(dest, e, o, chan, samp, vol, atten);
1168 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1170 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1172 void stopsoundto(float dest, entity e, float chan)
1175 entno = num_for_edict(e);
1180 idx = precache_sound_index("misc/null.wav");
1181 sflags = SND_LARGEENTITY;
1183 sflags |= SND_LARGESOUND;
1184 WriteByte(dest, SVC_SOUND);
1185 WriteByte(dest, sflags);
1186 WriteShort(dest, entno);
1187 WriteByte(dest, chan);
1188 if(sflags & SND_LARGESOUND)
1189 WriteShort(dest, idx);
1191 WriteByte(dest, idx);
1192 WriteCoord(dest, e.origin_x);
1193 WriteCoord(dest, e.origin_y);
1194 WriteCoord(dest, e.origin_z);
1198 WriteByte(dest, SVC_STOPSOUND);
1199 WriteShort(dest, entno * 8 + chan);
1202 void stopsound(entity e, float chan)
1204 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1205 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1208 void play2(entity e, string filename)
1210 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1212 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1215 .float announcetime;
1216 float announce(entity player, string msg)
1218 if(time > player.announcetime)
1219 if(clienttype(player) == CLIENTTYPE_REAL)
1221 player.announcetime = time + 0.8;
1227 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1228 float spamsound(entity e, float chan, string samp, float vol, float atten)
1230 if(time > e.announcetime)
1232 e.announcetime = time;
1233 sound(e, chan, samp, vol, atten);
1239 void play2team(float t, string filename)
1242 FOR_EACH_REALPLAYER(head)
1245 play2(head, filename);
1249 void play2all(string samp)
1251 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1254 void PrecachePlayerSounds(string f);
1255 void precache_all_models(string pattern)
1257 float globhandle, i, n;
1260 globhandle = search_begin(pattern, TRUE, FALSE);
1263 n = search_getsize(globhandle);
1264 for(i = 0; i < n; ++i)
1266 //print(search_getfilename(globhandle, i), "\n");
1267 f = search_getfilename(globhandle, i);
1269 PrecachePlayerSounds(strcat(f, ".sounds"));
1271 search_end(globhandle);
1276 // gamemode related things
1277 precache_model ("models/misc/chatbubble.spr");
1278 precache_model ("models/misc/teambubble.spr");
1281 precache_model ("models/runematch/curse.mdl");
1282 precache_model ("models/runematch/rune.mdl");
1285 #ifdef TTURRETS_ENABLED
1286 if(cvar("g_turrets"))
1290 // Precache all player models if desired
1291 if (cvar("sv_precacheplayermodels"))
1293 PrecachePlayerSounds("sound/player/default.sounds");
1294 precache_all_models("models/player/*.zym");
1295 precache_all_models("models/player/*.dpm");
1296 precache_all_models("models/player/*.md3");
1297 precache_all_models("models/player/*.psk");
1298 //precache_model("models/player/carni.zym");
1299 //precache_model("models/player/crash.zym");
1300 //precache_model("models/player/grunt.zym");
1301 //precache_model("models/player/headhunter.zym");
1302 //precache_model("models/player/insurrectionist.zym");
1303 //precache_model("models/player/jeandarc.zym");
1304 //precache_model("models/player/lurk.zym");
1305 //precache_model("models/player/lycanthrope.zym");
1306 //precache_model("models/player/marine.zym");
1307 //precache_model("models/player/nexus.zym");
1308 //precache_model("models/player/pyria.zym");
1309 //precache_model("models/player/shock.zym");
1310 //precache_model("models/player/skadi.zym");
1311 //precache_model("models/player/specop.zym");
1312 //precache_model("models/player/visitant.zym");
1315 if(cvar("sv_defaultcharacter"))
1318 s = cvar_string("sv_defaultplayermodel_red");
1322 PrecachePlayerSounds(strcat(s, ".sounds"));
1324 s = cvar_string("sv_defaultplayermodel_blue");
1328 PrecachePlayerSounds(strcat(s, ".sounds"));
1330 s = cvar_string("sv_defaultplayermodel_yellow");
1334 PrecachePlayerSounds(strcat(s, ".sounds"));
1336 s = cvar_string("sv_defaultplayermodel_pink");
1340 PrecachePlayerSounds(strcat(s, ".sounds"));
1342 s = cvar_string("sv_defaultplayermodel");
1346 PrecachePlayerSounds(strcat(s, ".sounds"));
1352 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1353 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1356 // gore and miscellaneous sounds
1357 //precache_sound ("misc/h2ohit.wav");
1358 precache_model ("models/hook.md3");
1359 precache_sound ("misc/armorimpact.wav");
1360 precache_sound ("misc/bodyimpact1.wav");
1361 precache_sound ("misc/bodyimpact2.wav");
1362 precache_sound ("misc/gib.wav");
1363 precache_sound ("misc/gib_splat01.wav");
1364 precache_sound ("misc/gib_splat02.wav");
1365 precache_sound ("misc/gib_splat03.wav");
1366 precache_sound ("misc/gib_splat04.wav");
1367 precache_sound ("misc/hit.wav");
1368 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1369 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1370 precache_sound ("misc/null.wav");
1371 precache_sound ("misc/spawn.wav");
1372 precache_sound ("misc/talk.wav");
1373 precache_sound ("misc/teleport.wav");
1374 precache_sound ("misc/poweroff.wav");
1375 precache_sound ("player/lava.wav");
1376 precache_sound ("player/slime.wav");
1379 precache_sound ("misc/jetpack_fly.wav");
1381 // announcer sounds - male
1382 precache_sound ("announcer/male/electrobitch.wav");
1383 precache_sound ("announcer/male/airshot.wav");
1384 precache_sound ("announcer/male/03kills.wav");
1385 precache_sound ("announcer/male/05kills.wav");
1386 precache_sound ("announcer/male/10kills.wav");
1387 precache_sound ("announcer/male/15kills.wav");
1388 precache_sound ("announcer/male/20kills.wav");
1389 precache_sound ("announcer/male/25kills.wav");
1390 precache_sound ("announcer/male/30kills.wav");
1391 precache_sound ("announcer/male/botlike.wav");
1392 precache_sound ("announcer/male/yoda.wav");
1393 precache_sound ("announcer/male/headshot.wav");
1394 precache_sound ("announcer/male/impressive.wav");
1396 // announcer sounds - robotic
1397 precache_sound ("announcer/robotic/prepareforbattle.wav");
1398 precache_sound ("announcer/robotic/begin.wav");
1399 precache_sound ("announcer/robotic/timeoutcalled.wav");
1400 precache_sound ("announcer/robotic/1fragleft.wav");
1401 precache_sound ("announcer/robotic/2fragsleft.wav");
1402 precache_sound ("announcer/robotic/3fragsleft.wav");
1405 precache_sound ("announcer/robotic/lastsecond.wav");
1406 precache_sound ("announcer/robotic/narrowly.wav");
1409 precache_model ("models/sprites/1.spr32");
1410 precache_model ("models/sprites/2.spr32");
1411 precache_model ("models/sprites/3.spr32");
1412 precache_model ("models/sprites/4.spr32");
1413 precache_model ("models/sprites/5.spr32");
1414 precache_model ("models/sprites/6.spr32");
1415 precache_model ("models/sprites/7.spr32");
1416 precache_model ("models/sprites/8.spr32");
1417 precache_model ("models/sprites/9.spr32");
1418 precache_model ("models/sprites/10.spr32");
1419 precache_sound ("announcer/robotic/1.wav");
1420 precache_sound ("announcer/robotic/2.wav");
1421 precache_sound ("announcer/robotic/3.wav");
1422 precache_sound ("announcer/robotic/4.wav");
1423 precache_sound ("announcer/robotic/5.wav");
1424 precache_sound ("announcer/robotic/6.wav");
1425 precache_sound ("announcer/robotic/7.wav");
1426 precache_sound ("announcer/robotic/8.wav");
1427 precache_sound ("announcer/robotic/9.wav");
1428 precache_sound ("announcer/robotic/10.wav");
1430 // common weapon precaches
1431 precache_sound ("weapons/weapon_switch.wav");
1432 precache_sound ("weapons/weaponpickup.wav");
1433 if(g_grappling_hook)
1435 precache_sound ("weapons/hook_fire.wav"); // hook
1436 precache_sound ("weapons/hook_impact.wav"); // hook
1439 if (cvar("sv_precacheweapons") || g_nixnex)
1441 //precache weapon models/sounds
1444 while (wep <= WEP_LAST)
1446 weapon_action(wep, WR_PRECACHE);
1451 precache_model("models/elaser.mdl");
1452 precache_model("models/laser.mdl");
1453 precache_model("models/ebomb.mdl");
1456 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1458 if (!self.noise && self.music) // quake 3 uses the music field
1459 self.noise = self.music;
1461 // plays music for the level if there is any
1464 precache_sound (self.noise);
1465 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1470 // sorry, but using \ in macros breaks line numbers
1471 #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
1472 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1473 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1475 vector ExactTriggerHit_mins;
1476 vector ExactTriggerHit_maxs;
1477 float ExactTriggerHit_Recurse()
1483 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1486 if(trace_ent == self)
1491 se.solid = SOLID_NOT;
1492 f = ExactTriggerHit_Recurse();
1498 float ExactTriggerHit()
1502 if not(self.modelindex)
1506 self.solid = SOLID_BSP;
1507 ExactTriggerHit_mins = other.absmin;
1508 ExactTriggerHit_maxs = other.absmax;
1509 f = ExactTriggerHit_Recurse();
1515 // WARNING: this kills the trace globals
1516 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1517 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1519 #define INITPRIO_FIRST 0
1520 #define INITPRIO_GAMETYPE 0
1521 #define INITPRIO_GAMETYPE_FALLBACK 1
1522 #define INITPRIO_CVARS 5
1523 #define INITPRIO_FINDTARGET 10
1524 #define INITPRIO_DROPTOFLOOR 20
1525 #define INITPRIO_SETLOCATION 90
1526 #define INITPRIO_LINKDOORS 91
1527 #define INITPRIO_LAST 99
1529 .void(void) initialize_entity;
1530 .float initialize_entity_order;
1531 .entity initialize_entity_next;
1532 entity initialize_entity_first;
1534 void make_safe_for_remove(entity e)
1536 if(e.initialize_entity)
1539 for(ent = initialize_entity_first; ent; )
1541 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1543 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1544 // skip it in linked list
1547 prev.initialize_entity_next = ent.initialize_entity_next;
1548 ent = prev.initialize_entity_next;
1552 initialize_entity_first = ent.initialize_entity_next;
1553 ent = initialize_entity_first;
1559 ent = ent.initialize_entity_next;
1565 void objerror(string s)
1567 make_safe_for_remove(self);
1568 objerror_builtin(s);
1571 void remove_unsafely(entity e)
1576 void remove_safely(entity e)
1578 make_safe_for_remove(e);
1582 void InitializeEntity(entity e, void(void) func, float order)
1586 if(!e || e.initialize_entity)
1588 // make a proxy initializer entity
1592 e.classname = "initialize_entity";
1596 e.initialize_entity = func;
1597 e.initialize_entity_order = order;
1599 cur = initialize_entity_first;
1602 if(!cur || cur.initialize_entity_order > order)
1604 // insert between prev and cur
1606 prev.initialize_entity_next = e;
1608 initialize_entity_first = e;
1609 e.initialize_entity_next = cur;
1613 cur = cur.initialize_entity_next;
1616 void InitializeEntitiesRun()
1619 startoflist = initialize_entity_first;
1620 initialize_entity_first = world;
1621 for(self = startoflist; self; )
1624 var void(void) func;
1625 e = self.initialize_entity_next;
1626 func = self.initialize_entity;
1627 self.initialize_entity_order = 0;
1628 self.initialize_entity = func_null;
1629 self.initialize_entity_next = world;
1630 if(self.classname == "initialize_entity")
1634 remove_builtin(self);
1637 //dprint("Delayed initialization: ", self.classname, "\n");
1643 .float uncustomizeentityforclient_set;
1644 .void(void) uncustomizeentityforclient;
1645 void(void) SUB_Nullpointer = #0;
1646 void UncustomizeEntitiesRun()
1650 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1651 self.uncustomizeentityforclient();
1654 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1656 e.customizeentityforclient = customizer;
1657 e.uncustomizeentityforclient = uncustomizer;
1658 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1662 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1665 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1669 if(e.classname == "")
1670 e.classname = "net_linked";
1672 if(e.model == "" || self.modelindex == 0)
1676 setmodel(e, "null");
1680 e.SendEntity = sendfunc;
1681 e.SendFlags = 0xFFFFFF;
1684 e.effects |= EF_NODEPTHTEST;
1688 e.nextthink = time + dt;
1689 e.think = SUB_Remove;
1693 void adaptor_think2touch()
1702 void adaptor_think2use()
1714 // deferred dropping
1715 void DropToFloor_Handler()
1717 droptofloor_builtin();
1718 self.dropped_origin = self.origin;
1723 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1728 float trace_hits_box_a0, trace_hits_box_a1;
1730 float trace_hits_box_1d(float end, float thmi, float thma)
1734 // just check if x is in range
1742 // do the trace with respect to x
1743 // 0 -> end has to stay in thmi -> thma
1744 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1745 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1746 if(trace_hits_box_a0 > trace_hits_box_a1)
1752 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1757 // now it is a trace from 0 to end
1759 trace_hits_box_a0 = 0;
1760 trace_hits_box_a1 = 1;
1762 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1764 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1766 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1772 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1774 return trace_hits_box(start, end, thmi - ma, thma - mi);
1777 float SUB_NoImpactCheck()
1779 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1781 if(other == world && self.size != '0 0 0')
1784 tic = self.velocity * sys_ticrate;
1785 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1786 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1787 if(trace_fraction >= 1)
1789 dprint("Odd... did not hit...?\n");
1791 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1793 dprint("Detected and prevented the sky-grapple bug.\n");
1801 #define SUB_OwnerCheck() (other && (other == self.owner))
1803 #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)
1805 float MAX_IPBAN_URIS = 16;
1807 float URI_GET_DISCARD = 0;
1808 float URI_GET_IPBAN = 1;
1809 float URI_GET_IPBAN_END = 16;
1811 void URI_Get_Callback(float id, float status, string data)
1813 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1815 dprint("\nEnd of data.\n");
1817 if(id == URI_GET_DISCARD)
1821 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1824 OnlineBanList_URI_Get_Callback(id, status, data);
1828 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1832 void print_to(entity e, string s)
1835 sprint(e, strcat(s, "\n"));
1854 for(i = 0; i < MapInfo_count; ++i)
1856 if(MapInfo_Get_ByID(i))
1858 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1861 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1862 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1870 for(i = 0; i < MapInfo_count; ++i)
1872 if(MapInfo_Get_ByID(i))
1874 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1877 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1878 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1883 MapInfo_ClearTemps();
1886 return "No records are available on this server.\n";
1888 return strcat("Records on this server:\n", s);
1891 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1894 vector start, org, delta, end, enddown, mstart;
1896 m = e.dphitcontentsmask;
1897 e.dphitcontentsmask = goodcontents | badcontents;
1900 delta = world.maxs - world.mins;
1902 for(i = 0; i < attempts; ++i)
1904 start_x = org_x + random() * delta_x;
1905 start_y = org_y + random() * delta_y;
1906 start_z = org_z + random() * delta_z;
1908 // rule 1: start inside world bounds, and outside
1909 // solid, and don't start from somewhere where you can
1910 // fall down to evil
1911 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1912 if(trace_fraction >= 1)
1914 if(trace_startsolid)
1916 if(trace_dphitcontents & badcontents)
1918 if(trace_dphitq3surfaceflags & badsurfaceflags)
1921 // rule 2: if we are too high, lower the point
1922 if(trace_fraction * delta_z > maxaboveground)
1923 start = trace_endpos + '0 0 1' * maxaboveground;
1924 enddown = trace_endpos;
1926 // rule 3: make sure we aren't outside the map. This only works
1927 // for somewhat well formed maps. A good rule of thumb is that
1928 // the map should have a convex outside hull.
1929 // these can be traceLINES as we already verified the starting box
1930 mstart = start + 0.5 * (e.mins + e.maxs);
1931 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1932 if(trace_fraction >= 1)
1934 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1935 if(trace_fraction >= 1)
1937 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1938 if(trace_fraction >= 1)
1940 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1941 if(trace_fraction >= 1)
1943 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1944 if(trace_fraction >= 1)
1947 // find a random vector to "look at"
1948 end_x = org_x + random() * delta_x;
1949 end_y = org_y + random() * delta_y;
1950 end_z = org_z + random() * delta_z;
1951 end = start + normalize(end - start) * vlen(delta);
1953 // rule 4: start TO end must not be too short
1954 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1955 if(trace_startsolid)
1957 if(trace_fraction < minviewdistance / vlen(delta))
1960 // rule 5: don't want to look at sky
1961 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1964 // rule 6: we must not end up in trigger_hurt
1965 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1967 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
1974 e.dphitcontentsmask = m;
1978 setorigin(e, start);
1979 e.angles = vectoangles(end - start);
1980 dprint("Needed ", ftos(i + 1), " attempts\n");
1987 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
1989 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
1990 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
1991 WriteShort(MSG_BROADCAST, effectno);
1992 WriteCoord(MSG_BROADCAST, start_x);
1993 WriteCoord(MSG_BROADCAST, start_y);
1994 WriteCoord(MSG_BROADCAST, start_z);
1995 WriteCoord(MSG_BROADCAST, end_x);
1996 WriteCoord(MSG_BROADCAST, end_y);
1997 WriteCoord(MSG_BROADCAST, end_z);
1998 WriteCoord(MSG_BROADCAST, end_dz);
1999 WriteShort(MSG_BROADCAST, spd / 16);
2002 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2005 vector vecxy, velxy;
2007 vecxy = end - start; vecxy_z = 0;
2008 velxy = vel; velxy_z = 0;
2010 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2012 trailparticles(world, effectno, start, end);
2016 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2017 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2020 string GetGametype(); // g_world.qc
2021 void write_recordmarker(entity pl, float tstart, float dt)
2023 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2025 // also write a marker into demo files for demotc-race-record-extractor to find
2028 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2029 " ", ftos(tstart), " ", ftos(dt), "\n"));
2032 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2036 if (cvar("g_shootfromeye"))
2049 else if (cvar("g_shootfromcenter"))
2054 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2069 void attach_sameorigin(entity e, entity to, string tag)
2071 vector org, t_forward, t_left, t_up, e_forward, e_up;
2078 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2079 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2080 t_forward = v_forward * tagscale;
2081 t_left = v_right * -tagscale;
2082 t_up = v_up * tagscale;
2084 e.origin_x = org * t_forward;
2085 e.origin_y = org * t_left;
2086 e.origin_z = org * t_up;
2088 // current forward and up directions
2089 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2090 e.angles_x = -e.angles_x;
2091 fixedmakevectors(e.angles);
2093 // untransform forward, up!
2094 e_forward_x = v_forward * t_forward;
2095 e_forward_y = v_forward * t_left;
2096 e_forward_z = v_forward * t_up;
2097 e_up_x = v_up * t_forward;
2098 e_up_y = v_up * t_left;
2099 e_up_z = v_up * t_up;
2101 e.angles = fixedvectoangles2(e_forward, e_up);
2102 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2103 e.angles_x = -e.angles_x;
2105 setattachment(e, to, tag);
2106 setorigin(e, e.origin);
2109 void detach_sameorigin(entity e)
2112 org = gettaginfo(e, 0);
2113 e.angles = fixedvectoangles2(v_forward, v_up);
2114 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2115 e.angles_x = -e.angles_x;
2117 setattachment(e, world, "");
2118 setorigin(e, e.origin);
2121 void follow_sameorigin(entity e, entity to)
2123 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2124 e.aiment = to; // make the hole follow bmodel
2125 e.punchangle = to.angles; // the original angles of bmodel
2126 e.view_ofs = e.origin - to.origin; // relative origin
2127 e.v_angle = e.angles - to.angles; // relative angles
2130 void unfollow_sameorigin(entity e)
2132 e.movetype = MOVETYPE_NONE;
2135 entity gettaginfo_relative_ent;
2136 vector gettaginfo_relative(entity e, float tag)
2138 if(!gettaginfo_relative_ent)
2140 gettaginfo_relative_ent = spawn();
2141 gettaginfo_relative_ent.effects = EF_NODRAW;
2143 gettaginfo_relative_ent.model = e.model;
2144 gettaginfo_relative_ent.modelindex = e.modelindex;
2145 gettaginfo_relative_ent.frame = e.frame;
2146 return gettaginfo(gettaginfo_relative_ent, tag);
2149 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2153 if(pl.soundentity.cnt & p)
2155 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2156 pl.soundentity.cnt |= p;
2159 void SoundEntity_StopSound(entity pl, float chan)
2163 if(pl.soundentity.cnt & p)
2165 stopsoundto(MSG_ALL, pl.soundentity, chan);
2166 pl.soundentity.cnt &~= p;
2170 void SoundEntity_Attach(entity pl)
2172 pl.soundentity = spawn();
2173 pl.soundentity.classname = "soundentity";
2174 setattachment(pl.soundentity, pl, "");
2175 setmodel(pl.soundentity, "null");
2178 void SoundEntity_Detach(entity pl)
2181 for(i = 0; i <= 7; ++i)
2182 SoundEntity_StopSound(pl, i);
2186 float ParseCommandPlayerSlotTarget_firsttoken;
2187 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2195 ParseCommandPlayerSlotTarget_firsttoken = -1;
2199 if(substring(argv(idx), 0, 1) == "#")
2201 s = substring(argv(idx), 1, -1);
2209 if(s == ftos(stof(s)))
2211 e = edict_num(stof(s));
2212 if(e.flags & FL_CLIENT)
2214 ParseCommandPlayerSlotTarget_firsttoken = idx;
2221 // it must be a nick name
2226 FOR_EACH_CLIENT(head)
2227 if(head.netname == s)
2234 ParseCommandPlayerSlotTarget_firsttoken = idx;
2238 s = strdecolorize(s);
2240 FOR_EACH_CLIENT(head)
2241 if(strdecolorize(head.netname) == s)
2248 ParseCommandPlayerSlotTarget_firsttoken = idx;