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;
694 float g_weaponspeedfactor;
695 float g_weapondamagefactor;
699 float start_ammo_shells;
700 float start_ammo_nails;
701 float start_ammo_rockets;
702 float start_ammo_cells;
703 float start_ammo_fuel;
705 float start_armorvalue;
706 float warmup_start_weapons;
707 float warmup_start_ammo_shells;
708 float warmup_start_ammo_nails;
709 float warmup_start_ammo_rockets;
710 float warmup_start_ammo_cells;
711 float warmup_start_ammo_fuel;
712 float warmup_start_health;
713 float warmup_start_armorvalue;
716 entity get_weaponinfo(float w);
718 float NixNex_CanChooseWeapon(float wpn);
719 void readplayerstartcvars()
725 // initialize starting values for players
728 start_ammo_shells = 0;
729 start_ammo_nails = 0;
730 start_ammo_rockets = 0;
731 start_ammo_cells = 0;
732 start_health = cvar("g_balance_health_start");
733 start_armorvalue = cvar("g_balance_armor_start");
736 s = cvar_string("g_weaponarena");
742 g_weaponarena_list = "All Weapons";
743 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
745 e = get_weaponinfo(j);
746 g_weaponarena |= e.weapons;
747 weapon_action(e.weapon, WR_PRECACHE);
752 g_weaponarena_list = "Most Weapons";
753 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
755 e = get_weaponinfo(j);
756 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
758 g_weaponarena |= e.weapons;
759 weapon_action(e.weapon, WR_PRECACHE);
765 t = tokenize_console(s);
766 g_weaponarena_list = "";
767 for(i = 0; i < t; ++i)
770 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
772 e = get_weaponinfo(j);
775 g_weaponarena |= e.weapons;
776 weapon_action(e.weapon, WR_PRECACHE);
777 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
783 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
786 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
792 // will be done later
793 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
794 if(NixNex_CanChooseWeapon(i))
795 weapon_action(i, WR_PRECACHE);
797 else if(g_weaponarena)
799 start_weapons = g_weaponarena;
800 start_ammo_rockets = 999;
801 start_ammo_shells = 999;
802 start_ammo_cells = 999;
803 start_ammo_nails = 999;
804 start_ammo_fuel = 999;
805 start_items |= IT_UNLIMITED_AMMO;
810 start_armorvalue = 0;
811 start_weapons = WEPBIT_MINSTANEX;
812 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
813 start_ammo_cells = cvar("g_minstagib_ammo_start");
814 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
815 start_ammo_fuel = cvar("g_start_ammo_fuel");
817 if(g_minstagib_invis_alpha <= 0)
818 g_minstagib_invis_alpha = -1;
824 start_ammo_shells = cvar("g_lms_start_ammo_shells");
825 start_ammo_nails = cvar("g_lms_start_ammo_nails");
826 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
827 start_ammo_cells = cvar("g_lms_start_ammo_cells");
828 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
829 start_health = cvar("g_lms_start_health");
830 start_armorvalue = cvar("g_lms_start_armor");
831 } else if (cvar("g_use_ammunition")) {
832 start_ammo_shells = cvar("g_start_ammo_shells");
833 start_ammo_nails = cvar("g_start_ammo_nails");
834 start_ammo_rockets = cvar("g_start_ammo_rockets");
835 start_ammo_cells = cvar("g_start_ammo_cells");
836 start_ammo_fuel = cvar("g_start_ammo_fuel");
838 start_ammo_shells = cvar("g_pickup_shells_max");
839 start_ammo_nails = cvar("g_pickup_nails_max");
840 start_ammo_rockets = cvar("g_pickup_rockets_max");
841 start_ammo_cells = cvar("g_pickup_cells_max");
842 start_ammo_fuel = cvar("g_pickup_fuel_max");
843 start_items |= IT_UNLIMITED_AMMO;
846 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
848 e = get_weaponinfo(i);
852 t = cvar(strcat("g_start_weapon_", e.netname));
854 if(t < 0) // "default" weapon selection
857 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
859 t = (i == WEP_LASER);
861 t = 0; // weapon is set a few lines later
863 t = (i == WEP_LASER || i == WEP_SHOTGUN);
864 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
865 t += (i == WEP_HOOK);
868 if(g_nexball && i == WEP_PORTO)
873 start_weapons |= e.weapons;
874 weapon_action(e.weapon, WR_PRECACHE);
881 warmup_start_ammo_shells = start_ammo_shells;
882 warmup_start_ammo_nails = start_ammo_nails;
883 warmup_start_ammo_rockets = start_ammo_rockets;
884 warmup_start_ammo_cells = start_ammo_cells;
885 warmup_start_health = start_health;
886 warmup_start_armorvalue = start_armorvalue;
887 warmup_start_weapons = start_weapons;
889 if(!g_weaponarena && !g_nixnex && !g_minstagib)
891 if(cvar("g_use_ammunition"))
893 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
894 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
895 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
896 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
898 warmup_start_health = cvar("g_warmup_start_health");
899 warmup_start_armorvalue = cvar("g_warmup_start_armor");
900 if(cvar("g_warmup_allguns"))
902 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
904 e = get_weaponinfo(i);
907 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
909 warmup_start_weapons |= e.weapons;
910 weapon_action(e.weapon, WR_PRECACHE);
917 if(g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
919 g_grappling_hook = 0; // these two can't coexist, as they use the same button
920 start_items |= IT_FUEL_REGEN;
921 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
925 start_items |= IT_JETPACK;
927 if(g_weapon_stay == 2)
929 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
930 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
931 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
932 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
933 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
934 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
935 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
936 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
937 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
938 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
941 start_ammo_shells = max(0, start_ammo_shells);
942 start_ammo_nails = max(0, start_ammo_nails);
943 start_ammo_cells = max(0, start_ammo_cells);
944 start_ammo_rockets = max(0, start_ammo_rockets);
945 start_ammo_fuel = max(0, start_ammo_fuel);
947 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
948 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
949 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
950 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
951 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
955 float g_bugrigs_planar_movement;
956 float g_bugrigs_planar_movement_car_jumping;
957 float g_bugrigs_reverse_spinning;
958 float g_bugrigs_reverse_speeding;
959 float g_bugrigs_reverse_stopping;
960 float g_bugrigs_air_steering;
961 float g_bugrigs_angle_smoothing;
962 float g_bugrigs_friction_floor;
963 float g_bugrigs_friction_brake;
964 float g_bugrigs_friction_air;
965 float g_bugrigs_accel;
966 float g_bugrigs_speed_ref;
967 float g_bugrigs_speed_pow;
968 float g_bugrigs_steer;
970 float g_touchexplode;
971 float g_touchexplode_radius;
972 float g_touchexplode_damage;
973 float g_touchexplode_edgedamage;
974 float g_touchexplode_force;
976 void readlevelcvars(void)
978 g_bugrigs = cvar("g_bugrigs");
979 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
980 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
981 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
982 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
983 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
984 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
985 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
986 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
987 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
988 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
989 g_bugrigs_accel = cvar("g_bugrigs_accel");
990 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
991 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
992 g_bugrigs_steer = cvar("g_bugrigs_steer");
994 g_touchexplode = cvar("g_touchexplode");
995 g_touchexplode_radius = cvar("g_touchexplode_radius");
996 g_touchexplode_damage = cvar("g_touchexplode_damage");
997 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
998 g_touchexplode_force = cvar("g_touchexplode_force");
1000 sv_clones = cvar("sv_clones");
1001 sv_cheats = cvar("sv_cheats");
1002 sv_gentle = cvar("sv_gentle");
1003 sv_foginterval = cvar("sv_foginterval");
1004 g_cloaked = cvar("g_cloaked");
1005 g_jump_grunt = cvar("g_jump_grunt");
1006 g_footsteps = cvar("g_footsteps");
1007 g_grappling_hook = cvar("g_grappling_hook");
1008 g_jetpack = cvar("g_jetpack");
1009 g_laserguided_missile = cvar("g_laserguided_missile");
1010 g_midair = cvar("g_midair");
1011 g_minstagib = cvar("g_minstagib");
1012 g_nixnex = cvar("g_nixnex");
1013 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1014 g_norecoil = cvar("g_norecoil");
1015 g_vampire = cvar("g_vampire");
1016 g_bloodloss = cvar("g_bloodloss");
1017 sv_maxidle = cvar("sv_maxidle");
1018 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1019 sv_pogostick = cvar("sv_pogostick");
1020 sv_doublejump = cvar("sv_doublejump");
1021 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1022 g_ctf_reverse = cvar("g_ctf_reverse");
1024 inWarmupStage = cvar("g_warmup");
1025 g_warmup_limit = cvar("g_warmup_limit");
1026 g_warmup_allguns = cvar("g_warmup_allguns");
1027 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1029 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1030 inWarmupStage = 0; // these modes cannot work together, sorry
1032 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1033 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1034 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1035 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1036 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1037 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1038 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1039 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1040 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1041 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1042 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1043 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1045 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1046 if(g_nixnex) g_weaponarena = 0;
1049 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1050 g_weapondamagefactor = cvar("g_weapondamagefactor");
1052 g_pickup_shells = cvar("g_pickup_shells");
1053 g_pickup_shells_max = cvar("g_pickup_shells_max");
1054 g_pickup_nails = cvar("g_pickup_nails");
1055 g_pickup_nails_max = cvar("g_pickup_nails_max");
1056 g_pickup_rockets = cvar("g_pickup_rockets");
1057 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1058 g_pickup_cells = cvar("g_pickup_cells");
1059 g_pickup_cells_max = cvar("g_pickup_cells_max");
1060 g_pickup_fuel = cvar("g_pickup_fuel");
1061 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1062 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1063 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1064 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1065 g_pickup_armormedium = cvar("g_pickup_armormedium");
1066 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1067 g_pickup_armorbig = cvar("g_pickup_armorbig");
1068 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1069 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1070 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1071 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1072 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1073 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1074 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1075 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1076 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1077 g_pickup_healthmega = cvar("g_pickup_healthmega");
1078 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1080 g_pinata = cvar("g_pinata");
1082 g_weapon_stay = cvar("g_weapon_stay");
1083 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1086 if not(inWarmupStage)
1087 game_starttime = cvar("g_start_delay");
1089 readplayerstartcvars();
1093 // TODO sound pack system
1096 string precache_sound_builtin (string s) = #19;
1097 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1098 string precache_sound(string s)
1100 return precache_sound_builtin(strcat(soundpack, s));
1102 void play2(entity e, string filename)
1104 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1106 void sound(entity e, float chan, string samp, float vol, float atten)
1108 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1113 string precache_sound (string s) = #19;
1114 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1115 float precache_sound_index (string s) = #19;
1117 #define SND_VOLUME 1
1118 #define SND_ATTENUATION 2
1119 #define SND_LARGEENTITY 8
1120 #define SND_LARGESOUND 16
1122 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1125 entno = num_for_edict(e);
1126 idx = precache_sound_index(samp);
1131 atten = floor(atten * 64);
1132 vol = floor(vol * 255);
1135 sflags |= SND_VOLUME;
1137 sflags |= SND_ATTENUATION;
1139 sflags |= SND_LARGEENTITY;
1141 sflags |= SND_LARGESOUND;
1143 WriteByte(dest, SVC_SOUND);
1144 WriteByte(dest, sflags);
1145 if(sflags & SND_VOLUME)
1146 WriteByte(dest, vol);
1147 if(sflags & SND_ATTENUATION)
1148 WriteByte(dest, atten);
1149 if(sflags & SND_LARGEENTITY)
1151 WriteShort(dest, entno);
1152 WriteByte(dest, chan);
1156 WriteShort(dest, entno * 8 + chan);
1158 if(sflags & SND_LARGESOUND)
1159 WriteShort(dest, idx);
1161 WriteByte(dest, idx);
1163 WriteCoord(dest, o_x);
1164 WriteCoord(dest, o_y);
1165 WriteCoord(dest, o_z);
1167 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1170 o = e.origin + 0.5 * (e.mins + e.maxs);
1171 soundtoat(dest, e, o, chan, samp, vol, atten);
1173 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1175 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1177 void stopsoundto(float dest, entity e, float chan)
1180 entno = num_for_edict(e);
1185 idx = precache_sound_index("misc/null.wav");
1186 sflags = SND_LARGEENTITY;
1188 sflags |= SND_LARGESOUND;
1189 WriteByte(dest, SVC_SOUND);
1190 WriteByte(dest, sflags);
1191 WriteShort(dest, entno);
1192 WriteByte(dest, chan);
1193 if(sflags & SND_LARGESOUND)
1194 WriteShort(dest, idx);
1196 WriteByte(dest, idx);
1197 WriteCoord(dest, e.origin_x);
1198 WriteCoord(dest, e.origin_y);
1199 WriteCoord(dest, e.origin_z);
1203 WriteByte(dest, SVC_STOPSOUND);
1204 WriteShort(dest, entno * 8 + chan);
1207 void stopsound(entity e, float chan)
1209 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1210 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1213 void play2(entity e, string filename)
1215 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1217 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1220 .float announcetime;
1221 float announce(entity player, string msg)
1223 if(time > player.announcetime)
1224 if(clienttype(player) == CLIENTTYPE_REAL)
1226 player.announcetime = time + 0.8;
1232 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1233 float spamsound(entity e, float chan, string samp, float vol, float atten)
1235 if(time > e.announcetime)
1237 e.announcetime = time;
1238 sound(e, chan, samp, vol, atten);
1244 void play2team(float t, string filename)
1247 FOR_EACH_REALPLAYER(head)
1250 play2(head, filename);
1254 void play2all(string samp)
1256 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1259 void PrecachePlayerSounds(string f);
1260 void precache_all_models(string pattern)
1262 float globhandle, i, n;
1265 globhandle = search_begin(pattern, TRUE, FALSE);
1268 n = search_getsize(globhandle);
1269 for(i = 0; i < n; ++i)
1271 //print(search_getfilename(globhandle, i), "\n");
1272 f = search_getfilename(globhandle, i);
1274 PrecachePlayerSounds(strcat(f, ".sounds"));
1276 search_end(globhandle);
1281 // gamemode related things
1282 precache_model ("models/misc/chatbubble.spr");
1283 precache_model ("models/misc/teambubble.spr");
1286 precache_model ("models/runematch/curse.mdl");
1287 precache_model ("models/runematch/rune.mdl");
1290 #ifdef TTURRETS_ENABLED
1291 if(cvar("g_turrets"))
1295 // Precache all player models if desired
1296 if (cvar("sv_precacheplayermodels"))
1298 PrecachePlayerSounds("sound/player/default.sounds");
1299 precache_all_models("models/player/*.zym");
1300 precache_all_models("models/player/*.dpm");
1301 precache_all_models("models/player/*.md3");
1302 precache_all_models("models/player/*.psk");
1303 //precache_model("models/player/carni.zym");
1304 //precache_model("models/player/crash.zym");
1305 //precache_model("models/player/grunt.zym");
1306 //precache_model("models/player/headhunter.zym");
1307 //precache_model("models/player/insurrectionist.zym");
1308 //precache_model("models/player/jeandarc.zym");
1309 //precache_model("models/player/lurk.zym");
1310 //precache_model("models/player/lycanthrope.zym");
1311 //precache_model("models/player/marine.zym");
1312 //precache_model("models/player/nexus.zym");
1313 //precache_model("models/player/pyria.zym");
1314 //precache_model("models/player/shock.zym");
1315 //precache_model("models/player/skadi.zym");
1316 //precache_model("models/player/specop.zym");
1317 //precache_model("models/player/visitant.zym");
1320 if(cvar("sv_defaultcharacter"))
1323 s = cvar_string("sv_defaultplayermodel_red");
1327 PrecachePlayerSounds(strcat(s, ".sounds"));
1329 s = cvar_string("sv_defaultplayermodel_blue");
1333 PrecachePlayerSounds(strcat(s, ".sounds"));
1335 s = cvar_string("sv_defaultplayermodel_yellow");
1339 PrecachePlayerSounds(strcat(s, ".sounds"));
1341 s = cvar_string("sv_defaultplayermodel_pink");
1345 PrecachePlayerSounds(strcat(s, ".sounds"));
1347 s = cvar_string("sv_defaultplayermodel");
1351 PrecachePlayerSounds(strcat(s, ".sounds"));
1357 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1358 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1361 // gore and miscellaneous sounds
1362 //precache_sound ("misc/h2ohit.wav");
1363 precache_model ("models/hook.md3");
1364 precache_sound ("misc/armorimpact.wav");
1365 precache_sound ("misc/bodyimpact1.wav");
1366 precache_sound ("misc/bodyimpact2.wav");
1367 precache_sound ("misc/gib.wav");
1368 precache_sound ("misc/gib_splat01.wav");
1369 precache_sound ("misc/gib_splat02.wav");
1370 precache_sound ("misc/gib_splat03.wav");
1371 precache_sound ("misc/gib_splat04.wav");
1372 precache_sound ("misc/hit.wav");
1373 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1374 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1375 precache_sound ("misc/null.wav");
1376 precache_sound ("misc/spawn.wav");
1377 precache_sound ("misc/talk.wav");
1378 precache_sound ("misc/teleport.wav");
1379 precache_sound ("misc/poweroff.wav");
1380 precache_sound ("player/lava.wav");
1381 precache_sound ("player/slime.wav");
1384 precache_sound ("misc/jetpack_fly.wav");
1386 // announcer sounds - male
1387 precache_sound ("announcer/male/electrobitch.wav");
1388 precache_sound ("announcer/male/airshot.wav");
1389 precache_sound ("announcer/male/03kills.wav");
1390 precache_sound ("announcer/male/05kills.wav");
1391 precache_sound ("announcer/male/10kills.wav");
1392 precache_sound ("announcer/male/15kills.wav");
1393 precache_sound ("announcer/male/20kills.wav");
1394 precache_sound ("announcer/male/25kills.wav");
1395 precache_sound ("announcer/male/30kills.wav");
1396 precache_sound ("announcer/male/botlike.wav");
1397 precache_sound ("announcer/male/yoda.wav");
1398 precache_sound ("announcer/male/amazing.wav");
1399 precache_sound ("announcer/male/awesome.wav");
1400 precache_sound ("announcer/male/headshot.wav");
1401 precache_sound ("announcer/male/impressive.wav");
1403 // announcer sounds - robotic
1404 precache_sound ("announcer/robotic/prepareforbattle.wav");
1405 precache_sound ("announcer/robotic/begin.wav");
1406 precache_sound ("announcer/robotic/timeoutcalled.wav");
1407 precache_sound ("announcer/robotic/1fragleft.wav");
1408 precache_sound ("announcer/robotic/2fragsleft.wav");
1409 precache_sound ("announcer/robotic/3fragsleft.wav");
1412 precache_sound ("announcer/robotic/lastsecond.wav");
1413 precache_sound ("announcer/robotic/narrowly.wav");
1416 precache_model ("models/sprites/1.spr32");
1417 precache_model ("models/sprites/2.spr32");
1418 precache_model ("models/sprites/3.spr32");
1419 precache_model ("models/sprites/4.spr32");
1420 precache_model ("models/sprites/5.spr32");
1421 precache_model ("models/sprites/6.spr32");
1422 precache_model ("models/sprites/7.spr32");
1423 precache_model ("models/sprites/8.spr32");
1424 precache_model ("models/sprites/9.spr32");
1425 precache_model ("models/sprites/10.spr32");
1426 precache_sound ("announcer/robotic/1.wav");
1427 precache_sound ("announcer/robotic/2.wav");
1428 precache_sound ("announcer/robotic/3.wav");
1429 precache_sound ("announcer/robotic/4.wav");
1430 precache_sound ("announcer/robotic/5.wav");
1431 precache_sound ("announcer/robotic/6.wav");
1432 precache_sound ("announcer/robotic/7.wav");
1433 precache_sound ("announcer/robotic/8.wav");
1434 precache_sound ("announcer/robotic/9.wav");
1435 precache_sound ("announcer/robotic/10.wav");
1437 // common weapon precaches
1438 precache_sound ("weapons/weapon_switch.wav");
1439 precache_sound ("weapons/weaponpickup.wav");
1440 precache_sound ("weapons/unavailable.wav");
1441 if(g_grappling_hook)
1443 precache_sound ("weapons/hook_fire.wav"); // hook
1444 precache_sound ("weapons/hook_impact.wav"); // hook
1447 if (cvar("sv_precacheweapons") || g_nixnex)
1449 //precache weapon models/sounds
1452 while (wep <= WEP_LAST)
1454 weapon_action(wep, WR_PRECACHE);
1459 precache_model("models/elaser.mdl");
1460 precache_model("models/laser.mdl");
1461 precache_model("models/ebomb.mdl");
1464 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1466 if (!self.noise && self.music) // quake 3 uses the music field
1467 self.noise = self.music;
1469 // plays music for the level if there is any
1472 precache_sound (self.noise);
1473 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1478 // sorry, but using \ in macros breaks line numbers
1479 #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
1480 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1481 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1483 vector ExactTriggerHit_mins;
1484 vector ExactTriggerHit_maxs;
1485 float ExactTriggerHit_Recurse()
1491 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1494 if(trace_ent == self)
1499 se.solid = SOLID_NOT;
1500 f = ExactTriggerHit_Recurse();
1506 float ExactTriggerHit()
1510 if not(self.modelindex)
1514 self.solid = SOLID_BSP;
1515 ExactTriggerHit_mins = other.absmin;
1516 ExactTriggerHit_maxs = other.absmax;
1517 f = ExactTriggerHit_Recurse();
1523 // WARNING: this kills the trace globals
1524 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1525 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1527 #define INITPRIO_FIRST 0
1528 #define INITPRIO_GAMETYPE 0
1529 #define INITPRIO_GAMETYPE_FALLBACK 1
1530 #define INITPRIO_CVARS 5
1531 #define INITPRIO_FINDTARGET 10
1532 #define INITPRIO_DROPTOFLOOR 20
1533 #define INITPRIO_SETLOCATION 90
1534 #define INITPRIO_LINKDOORS 91
1535 #define INITPRIO_LAST 99
1537 .void(void) initialize_entity;
1538 .float initialize_entity_order;
1539 .entity initialize_entity_next;
1540 entity initialize_entity_first;
1542 void make_safe_for_remove(entity e)
1544 if(e.initialize_entity)
1547 for(ent = initialize_entity_first; ent; )
1549 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1551 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1552 // skip it in linked list
1555 prev.initialize_entity_next = ent.initialize_entity_next;
1556 ent = prev.initialize_entity_next;
1560 initialize_entity_first = ent.initialize_entity_next;
1561 ent = initialize_entity_first;
1567 ent = ent.initialize_entity_next;
1573 void objerror(string s)
1575 make_safe_for_remove(self);
1576 objerror_builtin(s);
1579 void remove_unsafely(entity e)
1584 void remove_safely(entity e)
1586 make_safe_for_remove(e);
1590 void InitializeEntity(entity e, void(void) func, float order)
1594 if(!e || e.initialize_entity)
1596 // make a proxy initializer entity
1600 e.classname = "initialize_entity";
1604 e.initialize_entity = func;
1605 e.initialize_entity_order = order;
1607 cur = initialize_entity_first;
1610 if(!cur || cur.initialize_entity_order > order)
1612 // insert between prev and cur
1614 prev.initialize_entity_next = e;
1616 initialize_entity_first = e;
1617 e.initialize_entity_next = cur;
1621 cur = cur.initialize_entity_next;
1624 void InitializeEntitiesRun()
1627 startoflist = initialize_entity_first;
1628 initialize_entity_first = world;
1629 for(self = startoflist; self; )
1632 var void(void) func;
1633 e = self.initialize_entity_next;
1634 func = self.initialize_entity;
1635 self.initialize_entity_order = 0;
1636 self.initialize_entity = func_null;
1637 self.initialize_entity_next = world;
1638 if(self.classname == "initialize_entity")
1642 remove_builtin(self);
1645 //dprint("Delayed initialization: ", self.classname, "\n");
1651 .float uncustomizeentityforclient_set;
1652 .void(void) uncustomizeentityforclient;
1653 void(void) SUB_Nullpointer = #0;
1654 void UncustomizeEntitiesRun()
1658 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1659 self.uncustomizeentityforclient();
1662 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1664 e.customizeentityforclient = customizer;
1665 e.uncustomizeentityforclient = uncustomizer;
1666 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1670 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1673 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1677 if(e.classname == "")
1678 e.classname = "net_linked";
1680 if(e.model == "" || self.modelindex == 0)
1684 setmodel(e, "null");
1688 e.SendEntity = sendfunc;
1689 e.SendFlags = 0xFFFFFF;
1692 e.effects |= EF_NODEPTHTEST;
1696 e.nextthink = time + dt;
1697 e.think = SUB_Remove;
1701 void adaptor_think2touch()
1710 void adaptor_think2use()
1722 // deferred dropping
1723 void DropToFloor_Handler()
1725 droptofloor_builtin();
1726 self.dropped_origin = self.origin;
1731 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1736 float trace_hits_box_a0, trace_hits_box_a1;
1738 float trace_hits_box_1d(float end, float thmi, float thma)
1742 // just check if x is in range
1750 // do the trace with respect to x
1751 // 0 -> end has to stay in thmi -> thma
1752 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1753 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1754 if(trace_hits_box_a0 > trace_hits_box_a1)
1760 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1765 // now it is a trace from 0 to end
1767 trace_hits_box_a0 = 0;
1768 trace_hits_box_a1 = 1;
1770 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1772 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1774 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1780 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1782 return trace_hits_box(start, end, thmi - ma, thma - mi);
1785 float SUB_NoImpactCheck()
1787 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1789 if(other == world && self.size != '0 0 0')
1792 tic = self.velocity * sys_ticrate;
1793 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1794 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1795 if(trace_fraction >= 1)
1797 dprint("Odd... did not hit...?\n");
1799 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1801 dprint("Detected and prevented the sky-grapple bug.\n");
1809 #define SUB_OwnerCheck() (other && (other == self.owner))
1811 #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)
1813 float MAX_IPBAN_URIS = 16;
1815 float URI_GET_DISCARD = 0;
1816 float URI_GET_IPBAN = 1;
1817 float URI_GET_IPBAN_END = 16;
1819 void URI_Get_Callback(float id, float status, string data)
1821 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1823 dprint("\nEnd of data.\n");
1825 if(id == URI_GET_DISCARD)
1829 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1832 OnlineBanList_URI_Get_Callback(id, status, data);
1836 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1840 void print_to(entity e, string s)
1843 sprint(e, strcat(s, "\n"));
1862 for(i = 0; i < MapInfo_count; ++i)
1864 if(MapInfo_Get_ByID(i))
1866 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1869 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1870 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1878 for(i = 0; i < MapInfo_count; ++i)
1880 if(MapInfo_Get_ByID(i))
1882 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1885 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1886 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1891 MapInfo_ClearTemps();
1894 return "No records are available on this server.\n";
1896 return strcat("Records on this server:\n", s);
1899 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1902 vector start, org, delta, end, enddown, mstart;
1904 m = e.dphitcontentsmask;
1905 e.dphitcontentsmask = goodcontents | badcontents;
1908 delta = world.maxs - world.mins;
1910 for(i = 0; i < attempts; ++i)
1912 start_x = org_x + random() * delta_x;
1913 start_y = org_y + random() * delta_y;
1914 start_z = org_z + random() * delta_z;
1916 // rule 1: start inside world bounds, and outside
1917 // solid, and don't start from somewhere where you can
1918 // fall down to evil
1919 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1920 if(trace_fraction >= 1)
1922 if(trace_startsolid)
1924 if(trace_dphitcontents & badcontents)
1926 if(trace_dphitq3surfaceflags & badsurfaceflags)
1929 // rule 2: if we are too high, lower the point
1930 if(trace_fraction * delta_z > maxaboveground)
1931 start = trace_endpos + '0 0 1' * maxaboveground;
1932 enddown = trace_endpos;
1934 // rule 3: make sure we aren't outside the map. This only works
1935 // for somewhat well formed maps. A good rule of thumb is that
1936 // the map should have a convex outside hull.
1937 // these can be traceLINES as we already verified the starting box
1938 mstart = start + 0.5 * (e.mins + e.maxs);
1939 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1940 if(trace_fraction >= 1)
1942 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1943 if(trace_fraction >= 1)
1945 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1946 if(trace_fraction >= 1)
1948 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1949 if(trace_fraction >= 1)
1951 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1952 if(trace_fraction >= 1)
1955 // find a random vector to "look at"
1956 end_x = org_x + random() * delta_x;
1957 end_y = org_y + random() * delta_y;
1958 end_z = org_z + random() * delta_z;
1959 end = start + normalize(end - start) * vlen(delta);
1961 // rule 4: start TO end must not be too short
1962 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1963 if(trace_startsolid)
1965 if(trace_fraction < minviewdistance / vlen(delta))
1968 // rule 5: don't want to look at sky
1969 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1972 // rule 6: we must not end up in trigger_hurt
1973 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1975 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
1982 e.dphitcontentsmask = m;
1986 setorigin(e, start);
1987 e.angles = vectoangles(end - start);
1988 dprint("Needed ", ftos(i + 1), " attempts\n");
1995 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
1997 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
1998 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
1999 WriteShort(MSG_BROADCAST, effectno);
2000 WriteCoord(MSG_BROADCAST, start_x);
2001 WriteCoord(MSG_BROADCAST, start_y);
2002 WriteCoord(MSG_BROADCAST, start_z);
2003 WriteCoord(MSG_BROADCAST, end_x);
2004 WriteCoord(MSG_BROADCAST, end_y);
2005 WriteCoord(MSG_BROADCAST, end_z);
2006 WriteCoord(MSG_BROADCAST, end_dz);
2007 WriteShort(MSG_BROADCAST, spd / 16);
2010 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2013 vector vecxy, velxy;
2015 vecxy = end - start; vecxy_z = 0;
2016 velxy = vel; velxy_z = 0;
2018 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2020 trailparticles(world, effectno, start, end);
2024 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2025 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2028 string GetGametype(); // g_world.qc
2029 void write_recordmarker(entity pl, float tstart, float dt)
2031 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2033 // also write a marker into demo files for demotc-race-record-extractor to find
2036 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2037 " ", ftos(tstart), " ", ftos(dt), "\n"));
2040 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2044 if (cvar("g_shootfromeye"))
2057 else if (cvar("g_shootfromcenter"))
2062 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2077 void attach_sameorigin(entity e, entity to, string tag)
2079 vector org, t_forward, t_left, t_up, e_forward, e_up;
2086 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2087 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2088 t_forward = v_forward * tagscale;
2089 t_left = v_right * -tagscale;
2090 t_up = v_up * tagscale;
2092 e.origin_x = org * t_forward;
2093 e.origin_y = org * t_left;
2094 e.origin_z = org * t_up;
2096 // current forward and up directions
2097 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2098 e.angles_x = -e.angles_x;
2099 fixedmakevectors(e.angles);
2101 // untransform forward, up!
2102 e_forward_x = v_forward * t_forward;
2103 e_forward_y = v_forward * t_left;
2104 e_forward_z = v_forward * t_up;
2105 e_up_x = v_up * t_forward;
2106 e_up_y = v_up * t_left;
2107 e_up_z = v_up * t_up;
2109 e.angles = fixedvectoangles2(e_forward, e_up);
2110 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2111 e.angles_x = -e.angles_x;
2113 setattachment(e, to, tag);
2114 setorigin(e, e.origin);
2117 void detach_sameorigin(entity e)
2120 org = gettaginfo(e, 0);
2121 e.angles = fixedvectoangles2(v_forward, v_up);
2122 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2123 e.angles_x = -e.angles_x;
2125 setattachment(e, world, "");
2126 setorigin(e, e.origin);
2129 void follow_sameorigin(entity e, entity to)
2131 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2132 e.aiment = to; // make the hole follow bmodel
2133 e.punchangle = to.angles; // the original angles of bmodel
2134 e.view_ofs = e.origin - to.origin; // relative origin
2135 e.v_angle = e.angles - to.angles; // relative angles
2138 void unfollow_sameorigin(entity e)
2140 e.movetype = MOVETYPE_NONE;
2143 entity gettaginfo_relative_ent;
2144 vector gettaginfo_relative(entity e, float tag)
2146 if(!gettaginfo_relative_ent)
2148 gettaginfo_relative_ent = spawn();
2149 gettaginfo_relative_ent.effects = EF_NODRAW;
2151 gettaginfo_relative_ent.model = e.model;
2152 gettaginfo_relative_ent.modelindex = e.modelindex;
2153 gettaginfo_relative_ent.frame = e.frame;
2154 return gettaginfo(gettaginfo_relative_ent, tag);
2157 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2161 if(pl.soundentity.cnt & p)
2163 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2164 pl.soundentity.cnt |= p;
2167 void SoundEntity_StopSound(entity pl, float chan)
2171 if(pl.soundentity.cnt & p)
2173 stopsoundto(MSG_ALL, pl.soundentity, chan);
2174 pl.soundentity.cnt &~= p;
2178 void SoundEntity_Attach(entity pl)
2180 pl.soundentity = spawn();
2181 pl.soundentity.classname = "soundentity";
2182 setattachment(pl.soundentity, pl, "");
2183 setmodel(pl.soundentity, "null");
2186 void SoundEntity_Detach(entity pl)
2189 for(i = 0; i <= 7; ++i)
2190 SoundEntity_StopSound(pl, i);
2194 float ParseCommandPlayerSlotTarget_firsttoken;
2195 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2203 ParseCommandPlayerSlotTarget_firsttoken = -1;
2207 if(substring(argv(idx), 0, 1) == "#")
2209 s = substring(argv(idx), 1, -1);
2217 if(s == ftos(stof(s)))
2219 e = edict_num(stof(s));
2220 if(e.flags & FL_CLIENT)
2222 ParseCommandPlayerSlotTarget_firsttoken = idx;
2229 // it must be a nick name
2234 FOR_EACH_CLIENT(head)
2235 if(head.netname == s)
2242 ParseCommandPlayerSlotTarget_firsttoken = idx;
2246 s = strdecolorize(s);
2248 FOR_EACH_CLIENT(head)
2249 if(strdecolorize(head.netname) == s)
2256 ParseCommandPlayerSlotTarget_firsttoken = idx;