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 g_weaponarena_list = "No Weapons";
766 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
770 t = tokenize_console(s);
771 g_weaponarena_list = "";
772 for(i = 0; i < t; ++i)
775 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
777 e = get_weaponinfo(j);
780 g_weaponarena |= e.weapons;
781 weapon_action(e.weapon, WR_PRECACHE);
782 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
788 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
791 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
797 // will be done later
798 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
799 if(NixNex_CanChooseWeapon(i))
800 weapon_action(i, WR_PRECACHE);
802 else if(g_weaponarena)
804 start_weapons = g_weaponarena;
805 start_ammo_rockets = 999;
806 start_ammo_shells = 999;
807 start_ammo_cells = 999;
808 start_ammo_nails = 999;
809 start_ammo_fuel = 999;
810 start_items |= IT_UNLIMITED_AMMO;
815 start_armorvalue = 0;
816 start_weapons = WEPBIT_MINSTANEX;
817 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
818 start_ammo_cells = cvar("g_minstagib_ammo_start");
819 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
820 start_ammo_fuel = cvar("g_start_ammo_fuel");
822 if(g_minstagib_invis_alpha <= 0)
823 g_minstagib_invis_alpha = -1;
829 start_ammo_shells = cvar("g_lms_start_ammo_shells");
830 start_ammo_nails = cvar("g_lms_start_ammo_nails");
831 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
832 start_ammo_cells = cvar("g_lms_start_ammo_cells");
833 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
834 start_health = cvar("g_lms_start_health");
835 start_armorvalue = cvar("g_lms_start_armor");
836 } else if (cvar("g_use_ammunition")) {
837 start_ammo_shells = cvar("g_start_ammo_shells");
838 start_ammo_nails = cvar("g_start_ammo_nails");
839 start_ammo_rockets = cvar("g_start_ammo_rockets");
840 start_ammo_cells = cvar("g_start_ammo_cells");
841 start_ammo_fuel = cvar("g_start_ammo_fuel");
843 start_ammo_shells = cvar("g_pickup_shells_max");
844 start_ammo_nails = cvar("g_pickup_nails_max");
845 start_ammo_rockets = cvar("g_pickup_rockets_max");
846 start_ammo_cells = cvar("g_pickup_cells_max");
847 start_ammo_fuel = cvar("g_pickup_fuel_max");
848 start_items |= IT_UNLIMITED_AMMO;
851 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
853 e = get_weaponinfo(i);
857 t = cvar(strcat("g_start_weapon_", e.netname));
859 if(t < 0) // "default" weapon selection
862 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
864 t = (i == WEP_LASER);
866 t = 0; // weapon is set a few lines later
868 t = (i == WEP_LASER || i == WEP_SHOTGUN);
869 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
870 t += (i == WEP_HOOK);
873 if(g_nexball && i == WEP_PORTO)
878 start_weapons |= e.weapons;
879 weapon_action(e.weapon, WR_PRECACHE);
886 warmup_start_ammo_shells = start_ammo_shells;
887 warmup_start_ammo_nails = start_ammo_nails;
888 warmup_start_ammo_rockets = start_ammo_rockets;
889 warmup_start_ammo_cells = start_ammo_cells;
890 warmup_start_health = start_health;
891 warmup_start_armorvalue = start_armorvalue;
892 warmup_start_weapons = start_weapons;
894 if(!g_weaponarena && !g_nixnex && !g_minstagib)
896 if(cvar("g_use_ammunition"))
898 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
899 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
900 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
901 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
903 warmup_start_health = cvar("g_warmup_start_health");
904 warmup_start_armorvalue = cvar("g_warmup_start_armor");
905 if(cvar("g_warmup_allguns"))
907 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
909 e = get_weaponinfo(i);
912 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
914 warmup_start_weapons |= e.weapons;
915 weapon_action(e.weapon, WR_PRECACHE);
922 if(g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
924 g_grappling_hook = 0; // these two can't coexist, as they use the same button
925 start_items |= IT_FUEL_REGEN;
926 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
930 start_items |= IT_JETPACK;
932 if(g_weapon_stay == 2)
934 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
935 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
936 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
937 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
938 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
939 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
940 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
941 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
942 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
943 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
946 start_ammo_shells = max(0, start_ammo_shells);
947 start_ammo_nails = max(0, start_ammo_nails);
948 start_ammo_cells = max(0, start_ammo_cells);
949 start_ammo_rockets = max(0, start_ammo_rockets);
950 start_ammo_fuel = max(0, start_ammo_fuel);
952 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
953 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
954 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
955 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
956 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
960 float g_bugrigs_planar_movement;
961 float g_bugrigs_planar_movement_car_jumping;
962 float g_bugrigs_reverse_spinning;
963 float g_bugrigs_reverse_speeding;
964 float g_bugrigs_reverse_stopping;
965 float g_bugrigs_air_steering;
966 float g_bugrigs_angle_smoothing;
967 float g_bugrigs_friction_floor;
968 float g_bugrigs_friction_brake;
969 float g_bugrigs_friction_air;
970 float g_bugrigs_accel;
971 float g_bugrigs_speed_ref;
972 float g_bugrigs_speed_pow;
973 float g_bugrigs_steer;
975 float g_touchexplode;
976 float g_touchexplode_radius;
977 float g_touchexplode_damage;
978 float g_touchexplode_edgedamage;
979 float g_touchexplode_force;
981 void readlevelcvars(void)
983 g_bugrigs = cvar("g_bugrigs");
984 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
985 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
986 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
987 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
988 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
989 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
990 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
991 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
992 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
993 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
994 g_bugrigs_accel = cvar("g_bugrigs_accel");
995 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
996 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
997 g_bugrigs_steer = cvar("g_bugrigs_steer");
999 g_touchexplode = cvar("g_touchexplode");
1000 g_touchexplode_radius = cvar("g_touchexplode_radius");
1001 g_touchexplode_damage = cvar("g_touchexplode_damage");
1002 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1003 g_touchexplode_force = cvar("g_touchexplode_force");
1005 sv_clones = cvar("sv_clones");
1006 sv_cheats = cvar("sv_cheats");
1007 sv_gentle = cvar("sv_gentle");
1008 sv_foginterval = cvar("sv_foginterval");
1009 g_cloaked = cvar("g_cloaked");
1010 g_jump_grunt = cvar("g_jump_grunt");
1011 g_footsteps = cvar("g_footsteps");
1012 g_grappling_hook = cvar("g_grappling_hook");
1013 g_jetpack = cvar("g_jetpack");
1014 g_laserguided_missile = cvar("g_laserguided_missile");
1015 g_midair = cvar("g_midair");
1016 g_minstagib = cvar("g_minstagib");
1017 g_nixnex = cvar("g_nixnex");
1018 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1019 g_norecoil = cvar("g_norecoil");
1020 g_vampire = cvar("g_vampire");
1021 g_bloodloss = cvar("g_bloodloss");
1022 sv_maxidle = cvar("sv_maxidle");
1023 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1024 sv_pogostick = cvar("sv_pogostick");
1025 sv_doublejump = cvar("sv_doublejump");
1026 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1027 g_ctf_reverse = cvar("g_ctf_reverse");
1029 inWarmupStage = cvar("g_warmup");
1030 g_warmup_limit = cvar("g_warmup_limit");
1031 g_warmup_allguns = cvar("g_warmup_allguns");
1032 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1034 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1035 inWarmupStage = 0; // these modes cannot work together, sorry
1037 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1038 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1039 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1040 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1041 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1042 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1043 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1044 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1045 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1046 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1047 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1048 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1050 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1051 if(g_nixnex) g_weaponarena = 0;
1054 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1055 g_weapondamagefactor = cvar("g_weapondamagefactor");
1057 g_pickup_shells = cvar("g_pickup_shells");
1058 g_pickup_shells_max = cvar("g_pickup_shells_max");
1059 g_pickup_nails = cvar("g_pickup_nails");
1060 g_pickup_nails_max = cvar("g_pickup_nails_max");
1061 g_pickup_rockets = cvar("g_pickup_rockets");
1062 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1063 g_pickup_cells = cvar("g_pickup_cells");
1064 g_pickup_cells_max = cvar("g_pickup_cells_max");
1065 g_pickup_fuel = cvar("g_pickup_fuel");
1066 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1067 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1068 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1069 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1070 g_pickup_armormedium = cvar("g_pickup_armormedium");
1071 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1072 g_pickup_armorbig = cvar("g_pickup_armorbig");
1073 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1074 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1075 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1076 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1077 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1078 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1079 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1080 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1081 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1082 g_pickup_healthmega = cvar("g_pickup_healthmega");
1083 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1085 g_pinata = cvar("g_pinata");
1087 g_weapon_stay = cvar("g_weapon_stay");
1088 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1091 if not(inWarmupStage)
1092 game_starttime = cvar("g_start_delay");
1094 readplayerstartcvars();
1098 // TODO sound pack system
1101 string precache_sound_builtin (string s) = #19;
1102 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1103 string precache_sound(string s)
1105 return precache_sound_builtin(strcat(soundpack, s));
1107 void play2(entity e, string filename)
1109 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1111 void sound(entity e, float chan, string samp, float vol, float atten)
1113 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1118 string precache_sound (string s) = #19;
1119 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1120 float precache_sound_index (string s) = #19;
1122 #define SND_VOLUME 1
1123 #define SND_ATTENUATION 2
1124 #define SND_LARGEENTITY 8
1125 #define SND_LARGESOUND 16
1127 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1130 entno = num_for_edict(e);
1131 idx = precache_sound_index(samp);
1136 atten = floor(atten * 64);
1137 vol = floor(vol * 255);
1140 sflags |= SND_VOLUME;
1142 sflags |= SND_ATTENUATION;
1144 sflags |= SND_LARGEENTITY;
1146 sflags |= SND_LARGESOUND;
1148 WriteByte(dest, SVC_SOUND);
1149 WriteByte(dest, sflags);
1150 if(sflags & SND_VOLUME)
1151 WriteByte(dest, vol);
1152 if(sflags & SND_ATTENUATION)
1153 WriteByte(dest, atten);
1154 if(sflags & SND_LARGEENTITY)
1156 WriteShort(dest, entno);
1157 WriteByte(dest, chan);
1161 WriteShort(dest, entno * 8 + chan);
1163 if(sflags & SND_LARGESOUND)
1164 WriteShort(dest, idx);
1166 WriteByte(dest, idx);
1168 WriteCoord(dest, o_x);
1169 WriteCoord(dest, o_y);
1170 WriteCoord(dest, o_z);
1172 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1175 o = e.origin + 0.5 * (e.mins + e.maxs);
1176 soundtoat(dest, e, o, chan, samp, vol, atten);
1178 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1180 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1182 void stopsoundto(float dest, entity e, float chan)
1185 entno = num_for_edict(e);
1190 idx = precache_sound_index("misc/null.wav");
1191 sflags = SND_LARGEENTITY;
1193 sflags |= SND_LARGESOUND;
1194 WriteByte(dest, SVC_SOUND);
1195 WriteByte(dest, sflags);
1196 WriteShort(dest, entno);
1197 WriteByte(dest, chan);
1198 if(sflags & SND_LARGESOUND)
1199 WriteShort(dest, idx);
1201 WriteByte(dest, idx);
1202 WriteCoord(dest, e.origin_x);
1203 WriteCoord(dest, e.origin_y);
1204 WriteCoord(dest, e.origin_z);
1208 WriteByte(dest, SVC_STOPSOUND);
1209 WriteShort(dest, entno * 8 + chan);
1212 void stopsound(entity e, float chan)
1214 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1215 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1218 void play2(entity e, string filename)
1220 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1222 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1225 .float announcetime;
1226 float announce(entity player, string msg)
1228 if(time > player.announcetime)
1229 if(clienttype(player) == CLIENTTYPE_REAL)
1231 player.announcetime = time + 0.8;
1237 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1238 float spamsound(entity e, float chan, string samp, float vol, float atten)
1240 if(time > e.announcetime)
1242 e.announcetime = time;
1243 sound(e, chan, samp, vol, atten);
1249 void play2team(float t, string filename)
1252 FOR_EACH_REALPLAYER(head)
1255 play2(head, filename);
1259 void play2all(string samp)
1261 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1264 void PrecachePlayerSounds(string f);
1265 void precache_all_models(string pattern)
1267 float globhandle, i, n;
1270 globhandle = search_begin(pattern, TRUE, FALSE);
1273 n = search_getsize(globhandle);
1274 for(i = 0; i < n; ++i)
1276 //print(search_getfilename(globhandle, i), "\n");
1277 f = search_getfilename(globhandle, i);
1279 PrecachePlayerSounds(strcat(f, ".sounds"));
1281 search_end(globhandle);
1286 // gamemode related things
1287 precache_model ("models/misc/chatbubble.spr");
1288 precache_model ("models/misc/teambubble.spr");
1291 precache_model ("models/runematch/curse.mdl");
1292 precache_model ("models/runematch/rune.mdl");
1295 #ifdef TTURRETS_ENABLED
1296 if(cvar("g_turrets"))
1300 // Precache all player models if desired
1301 if (cvar("sv_precacheplayermodels"))
1303 PrecachePlayerSounds("sound/player/default.sounds");
1304 precache_all_models("models/player/*.zym");
1305 precache_all_models("models/player/*.dpm");
1306 precache_all_models("models/player/*.md3");
1307 precache_all_models("models/player/*.psk");
1308 //precache_model("models/player/carni.zym");
1309 //precache_model("models/player/crash.zym");
1310 //precache_model("models/player/grunt.zym");
1311 //precache_model("models/player/headhunter.zym");
1312 //precache_model("models/player/insurrectionist.zym");
1313 //precache_model("models/player/jeandarc.zym");
1314 //precache_model("models/player/lurk.zym");
1315 //precache_model("models/player/lycanthrope.zym");
1316 //precache_model("models/player/marine.zym");
1317 //precache_model("models/player/nexus.zym");
1318 //precache_model("models/player/pyria.zym");
1319 //precache_model("models/player/shock.zym");
1320 //precache_model("models/player/skadi.zym");
1321 //precache_model("models/player/specop.zym");
1322 //precache_model("models/player/visitant.zym");
1325 if(cvar("sv_defaultcharacter"))
1328 s = cvar_string("sv_defaultplayermodel_red");
1332 PrecachePlayerSounds(strcat(s, ".sounds"));
1334 s = cvar_string("sv_defaultplayermodel_blue");
1338 PrecachePlayerSounds(strcat(s, ".sounds"));
1340 s = cvar_string("sv_defaultplayermodel_yellow");
1344 PrecachePlayerSounds(strcat(s, ".sounds"));
1346 s = cvar_string("sv_defaultplayermodel_pink");
1350 PrecachePlayerSounds(strcat(s, ".sounds"));
1352 s = cvar_string("sv_defaultplayermodel");
1356 PrecachePlayerSounds(strcat(s, ".sounds"));
1362 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1363 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1366 // gore and miscellaneous sounds
1367 //precache_sound ("misc/h2ohit.wav");
1368 precache_model ("models/hook.md3");
1369 precache_sound ("misc/armorimpact.wav");
1370 precache_sound ("misc/bodyimpact1.wav");
1371 precache_sound ("misc/bodyimpact2.wav");
1372 precache_sound ("misc/gib.wav");
1373 precache_sound ("misc/gib_splat01.wav");
1374 precache_sound ("misc/gib_splat02.wav");
1375 precache_sound ("misc/gib_splat03.wav");
1376 precache_sound ("misc/gib_splat04.wav");
1377 precache_sound ("misc/hit.wav");
1378 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1379 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1380 precache_sound ("misc/null.wav");
1381 precache_sound ("misc/spawn.wav");
1382 precache_sound ("misc/talk.wav");
1383 precache_sound ("misc/teleport.wav");
1384 precache_sound ("misc/poweroff.wav");
1385 precache_sound ("player/lava.wav");
1386 precache_sound ("player/slime.wav");
1389 precache_sound ("misc/jetpack_fly.wav");
1391 // announcer sounds - male
1392 precache_sound ("announcer/male/electrobitch.wav");
1393 precache_sound ("announcer/male/airshot.wav");
1394 precache_sound ("announcer/male/03kills.wav");
1395 precache_sound ("announcer/male/05kills.wav");
1396 precache_sound ("announcer/male/10kills.wav");
1397 precache_sound ("announcer/male/15kills.wav");
1398 precache_sound ("announcer/male/20kills.wav");
1399 precache_sound ("announcer/male/25kills.wav");
1400 precache_sound ("announcer/male/30kills.wav");
1401 precache_sound ("announcer/male/botlike.wav");
1402 precache_sound ("announcer/male/yoda.wav");
1403 precache_sound ("announcer/male/amazing.wav");
1404 precache_sound ("announcer/male/awesome.wav");
1405 precache_sound ("announcer/male/headshot.wav");
1406 precache_sound ("announcer/male/impressive.wav");
1408 // announcer sounds - robotic
1409 precache_sound ("announcer/robotic/prepareforbattle.wav");
1410 precache_sound ("announcer/robotic/begin.wav");
1411 precache_sound ("announcer/robotic/timeoutcalled.wav");
1412 precache_sound ("announcer/robotic/1fragleft.wav");
1413 precache_sound ("announcer/robotic/2fragsleft.wav");
1414 precache_sound ("announcer/robotic/3fragsleft.wav");
1417 precache_sound ("announcer/robotic/lastsecond.wav");
1418 precache_sound ("announcer/robotic/narrowly.wav");
1421 precache_model ("models/sprites/1.spr32");
1422 precache_model ("models/sprites/2.spr32");
1423 precache_model ("models/sprites/3.spr32");
1424 precache_model ("models/sprites/4.spr32");
1425 precache_model ("models/sprites/5.spr32");
1426 precache_model ("models/sprites/6.spr32");
1427 precache_model ("models/sprites/7.spr32");
1428 precache_model ("models/sprites/8.spr32");
1429 precache_model ("models/sprites/9.spr32");
1430 precache_model ("models/sprites/10.spr32");
1431 precache_sound ("announcer/robotic/1.wav");
1432 precache_sound ("announcer/robotic/2.wav");
1433 precache_sound ("announcer/robotic/3.wav");
1434 precache_sound ("announcer/robotic/4.wav");
1435 precache_sound ("announcer/robotic/5.wav");
1436 precache_sound ("announcer/robotic/6.wav");
1437 precache_sound ("announcer/robotic/7.wav");
1438 precache_sound ("announcer/robotic/8.wav");
1439 precache_sound ("announcer/robotic/9.wav");
1440 precache_sound ("announcer/robotic/10.wav");
1442 // common weapon precaches
1443 precache_sound ("weapons/weapon_switch.wav");
1444 precache_sound ("weapons/weaponpickup.wav");
1445 precache_sound ("weapons/unavailable.wav");
1446 if(g_grappling_hook)
1448 precache_sound ("weapons/hook_fire.wav"); // hook
1449 precache_sound ("weapons/hook_impact.wav"); // hook
1452 if (cvar("sv_precacheweapons") || g_nixnex)
1454 //precache weapon models/sounds
1457 while (wep <= WEP_LAST)
1459 weapon_action(wep, WR_PRECACHE);
1464 precache_model("models/elaser.mdl");
1465 precache_model("models/laser.mdl");
1466 precache_model("models/ebomb.mdl");
1469 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1471 if (!self.noise && self.music) // quake 3 uses the music field
1472 self.noise = self.music;
1474 // plays music for the level if there is any
1477 precache_sound (self.noise);
1478 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1483 // sorry, but using \ in macros breaks line numbers
1484 #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
1485 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1486 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1488 vector ExactTriggerHit_mins;
1489 vector ExactTriggerHit_maxs;
1490 float ExactTriggerHit_Recurse()
1496 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1499 if(trace_ent == self)
1504 se.solid = SOLID_NOT;
1505 f = ExactTriggerHit_Recurse();
1511 float ExactTriggerHit()
1515 if not(self.modelindex)
1519 self.solid = SOLID_BSP;
1520 ExactTriggerHit_mins = other.absmin;
1521 ExactTriggerHit_maxs = other.absmax;
1522 f = ExactTriggerHit_Recurse();
1528 // WARNING: this kills the trace globals
1529 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1530 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1532 #define INITPRIO_FIRST 0
1533 #define INITPRIO_GAMETYPE 0
1534 #define INITPRIO_GAMETYPE_FALLBACK 1
1535 #define INITPRIO_CVARS 5
1536 #define INITPRIO_FINDTARGET 10
1537 #define INITPRIO_DROPTOFLOOR 20
1538 #define INITPRIO_SETLOCATION 90
1539 #define INITPRIO_LINKDOORS 91
1540 #define INITPRIO_LAST 99
1542 .void(void) initialize_entity;
1543 .float initialize_entity_order;
1544 .entity initialize_entity_next;
1545 entity initialize_entity_first;
1547 void make_safe_for_remove(entity e)
1549 if(e.initialize_entity)
1552 for(ent = initialize_entity_first; ent; )
1554 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1556 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1557 // skip it in linked list
1560 prev.initialize_entity_next = ent.initialize_entity_next;
1561 ent = prev.initialize_entity_next;
1565 initialize_entity_first = ent.initialize_entity_next;
1566 ent = initialize_entity_first;
1572 ent = ent.initialize_entity_next;
1578 void objerror(string s)
1580 make_safe_for_remove(self);
1581 objerror_builtin(s);
1584 void remove_unsafely(entity e)
1589 void remove_safely(entity e)
1591 make_safe_for_remove(e);
1595 void InitializeEntity(entity e, void(void) func, float order)
1599 if(!e || e.initialize_entity)
1601 // make a proxy initializer entity
1605 e.classname = "initialize_entity";
1609 e.initialize_entity = func;
1610 e.initialize_entity_order = order;
1612 cur = initialize_entity_first;
1615 if(!cur || cur.initialize_entity_order > order)
1617 // insert between prev and cur
1619 prev.initialize_entity_next = e;
1621 initialize_entity_first = e;
1622 e.initialize_entity_next = cur;
1626 cur = cur.initialize_entity_next;
1629 void InitializeEntitiesRun()
1632 startoflist = initialize_entity_first;
1633 initialize_entity_first = world;
1634 for(self = startoflist; self; )
1637 var void(void) func;
1638 e = self.initialize_entity_next;
1639 func = self.initialize_entity;
1640 self.initialize_entity_order = 0;
1641 self.initialize_entity = func_null;
1642 self.initialize_entity_next = world;
1643 if(self.classname == "initialize_entity")
1647 remove_builtin(self);
1650 //dprint("Delayed initialization: ", self.classname, "\n");
1656 .float uncustomizeentityforclient_set;
1657 .void(void) uncustomizeentityforclient;
1658 void(void) SUB_Nullpointer = #0;
1659 void UncustomizeEntitiesRun()
1663 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1664 self.uncustomizeentityforclient();
1667 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1669 e.customizeentityforclient = customizer;
1670 e.uncustomizeentityforclient = uncustomizer;
1671 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1675 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1678 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1682 if(e.classname == "")
1683 e.classname = "net_linked";
1685 if(e.model == "" || self.modelindex == 0)
1689 setmodel(e, "null");
1693 e.SendEntity = sendfunc;
1694 e.SendFlags = 0xFFFFFF;
1697 e.effects |= EF_NODEPTHTEST;
1701 e.nextthink = time + dt;
1702 e.think = SUB_Remove;
1706 void adaptor_think2touch()
1715 void adaptor_think2use()
1727 // deferred dropping
1728 void DropToFloor_Handler()
1730 droptofloor_builtin();
1731 self.dropped_origin = self.origin;
1736 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1741 float trace_hits_box_a0, trace_hits_box_a1;
1743 float trace_hits_box_1d(float end, float thmi, float thma)
1747 // just check if x is in range
1755 // do the trace with respect to x
1756 // 0 -> end has to stay in thmi -> thma
1757 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1758 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1759 if(trace_hits_box_a0 > trace_hits_box_a1)
1765 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1770 // now it is a trace from 0 to end
1772 trace_hits_box_a0 = 0;
1773 trace_hits_box_a1 = 1;
1775 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1777 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1779 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1785 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1787 return trace_hits_box(start, end, thmi - ma, thma - mi);
1790 float SUB_NoImpactCheck()
1792 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1794 if(other == world && self.size != '0 0 0')
1797 tic = self.velocity * sys_ticrate;
1798 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1799 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1800 if(trace_fraction >= 1)
1802 dprint("Odd... did not hit...?\n");
1804 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1806 dprint("Detected and prevented the sky-grapple bug.\n");
1814 #define SUB_OwnerCheck() (other && (other == self.owner))
1816 #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)
1818 float MAX_IPBAN_URIS = 16;
1820 float URI_GET_DISCARD = 0;
1821 float URI_GET_IPBAN = 1;
1822 float URI_GET_IPBAN_END = 16;
1824 void URI_Get_Callback(float id, float status, string data)
1826 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1828 dprint("\nEnd of data.\n");
1830 if(id == URI_GET_DISCARD)
1834 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1837 OnlineBanList_URI_Get_Callback(id, status, data);
1841 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1845 void print_to(entity e, string s)
1848 sprint(e, strcat(s, "\n"));
1867 for(i = 0; i < MapInfo_count; ++i)
1869 if(MapInfo_Get_ByID(i))
1871 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1874 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1875 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1883 for(i = 0; i < MapInfo_count; ++i)
1885 if(MapInfo_Get_ByID(i))
1887 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1890 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1891 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1896 MapInfo_ClearTemps();
1899 return "No records are available on this server.\n";
1901 return strcat("Records on this server:\n", s);
1904 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1907 vector start, org, delta, end, enddown, mstart;
1909 m = e.dphitcontentsmask;
1910 e.dphitcontentsmask = goodcontents | badcontents;
1913 delta = world.maxs - world.mins;
1915 for(i = 0; i < attempts; ++i)
1917 start_x = org_x + random() * delta_x;
1918 start_y = org_y + random() * delta_y;
1919 start_z = org_z + random() * delta_z;
1921 // rule 1: start inside world bounds, and outside
1922 // solid, and don't start from somewhere where you can
1923 // fall down to evil
1924 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1925 if(trace_fraction >= 1)
1927 if(trace_startsolid)
1929 if(trace_dphitcontents & badcontents)
1931 if(trace_dphitq3surfaceflags & badsurfaceflags)
1934 // rule 2: if we are too high, lower the point
1935 if(trace_fraction * delta_z > maxaboveground)
1936 start = trace_endpos + '0 0 1' * maxaboveground;
1937 enddown = trace_endpos;
1939 // rule 3: make sure we aren't outside the map. This only works
1940 // for somewhat well formed maps. A good rule of thumb is that
1941 // the map should have a convex outside hull.
1942 // these can be traceLINES as we already verified the starting box
1943 mstart = start + 0.5 * (e.mins + e.maxs);
1944 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1945 if(trace_fraction >= 1)
1947 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1948 if(trace_fraction >= 1)
1950 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1951 if(trace_fraction >= 1)
1953 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1954 if(trace_fraction >= 1)
1956 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1957 if(trace_fraction >= 1)
1960 // find a random vector to "look at"
1961 end_x = org_x + random() * delta_x;
1962 end_y = org_y + random() * delta_y;
1963 end_z = org_z + random() * delta_z;
1964 end = start + normalize(end - start) * vlen(delta);
1966 // rule 4: start TO end must not be too short
1967 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1968 if(trace_startsolid)
1970 if(trace_fraction < minviewdistance / vlen(delta))
1973 // rule 5: don't want to look at sky
1974 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1977 // rule 6: we must not end up in trigger_hurt
1978 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1980 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
1987 e.dphitcontentsmask = m;
1991 setorigin(e, start);
1992 e.angles = vectoangles(end - start);
1993 dprint("Needed ", ftos(i + 1), " attempts\n");
2000 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2002 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2003 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2004 WriteShort(MSG_BROADCAST, effectno);
2005 WriteCoord(MSG_BROADCAST, start_x);
2006 WriteCoord(MSG_BROADCAST, start_y);
2007 WriteCoord(MSG_BROADCAST, start_z);
2008 WriteCoord(MSG_BROADCAST, end_x);
2009 WriteCoord(MSG_BROADCAST, end_y);
2010 WriteCoord(MSG_BROADCAST, end_z);
2011 WriteCoord(MSG_BROADCAST, end_dz);
2012 WriteShort(MSG_BROADCAST, spd / 16);
2015 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2018 vector vecxy, velxy;
2020 vecxy = end - start; vecxy_z = 0;
2021 velxy = vel; velxy_z = 0;
2023 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2025 trailparticles(world, effectno, start, end);
2029 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2030 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2033 string GetGametype(); // g_world.qc
2034 void write_recordmarker(entity pl, float tstart, float dt)
2036 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2038 // also write a marker into demo files for demotc-race-record-extractor to find
2041 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2042 " ", ftos(tstart), " ", ftos(dt), "\n"));
2045 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2049 if (cvar("g_shootfromeye"))
2062 else if (cvar("g_shootfromcenter"))
2067 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2082 void attach_sameorigin(entity e, entity to, string tag)
2084 vector org, t_forward, t_left, t_up, e_forward, e_up;
2091 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2092 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2093 t_forward = v_forward * tagscale;
2094 t_left = v_right * -tagscale;
2095 t_up = v_up * tagscale;
2097 e.origin_x = org * t_forward;
2098 e.origin_y = org * t_left;
2099 e.origin_z = org * t_up;
2101 // current forward and up directions
2102 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2103 e.angles_x = -e.angles_x;
2104 fixedmakevectors(e.angles);
2106 // untransform forward, up!
2107 e_forward_x = v_forward * t_forward;
2108 e_forward_y = v_forward * t_left;
2109 e_forward_z = v_forward * t_up;
2110 e_up_x = v_up * t_forward;
2111 e_up_y = v_up * t_left;
2112 e_up_z = v_up * t_up;
2114 e.angles = fixedvectoangles2(e_forward, e_up);
2115 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2116 e.angles_x = -e.angles_x;
2118 setattachment(e, to, tag);
2119 setorigin(e, e.origin);
2122 void detach_sameorigin(entity e)
2125 org = gettaginfo(e, 0);
2126 e.angles = fixedvectoangles2(v_forward, v_up);
2127 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2128 e.angles_x = -e.angles_x;
2130 setattachment(e, world, "");
2131 setorigin(e, e.origin);
2134 void follow_sameorigin(entity e, entity to)
2136 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2137 e.aiment = to; // make the hole follow bmodel
2138 e.punchangle = to.angles; // the original angles of bmodel
2139 e.view_ofs = e.origin - to.origin; // relative origin
2140 e.v_angle = e.angles - to.angles; // relative angles
2143 void unfollow_sameorigin(entity e)
2145 e.movetype = MOVETYPE_NONE;
2148 entity gettaginfo_relative_ent;
2149 vector gettaginfo_relative(entity e, float tag)
2151 if(!gettaginfo_relative_ent)
2153 gettaginfo_relative_ent = spawn();
2154 gettaginfo_relative_ent.effects = EF_NODRAW;
2156 gettaginfo_relative_ent.model = e.model;
2157 gettaginfo_relative_ent.modelindex = e.modelindex;
2158 gettaginfo_relative_ent.frame = e.frame;
2159 return gettaginfo(gettaginfo_relative_ent, tag);
2162 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2166 if(pl.soundentity.cnt & p)
2168 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2169 pl.soundentity.cnt |= p;
2172 void SoundEntity_StopSound(entity pl, float chan)
2176 if(pl.soundentity.cnt & p)
2178 stopsoundto(MSG_ALL, pl.soundentity, chan);
2179 pl.soundentity.cnt &~= p;
2183 void SoundEntity_Attach(entity pl)
2185 pl.soundentity = spawn();
2186 pl.soundentity.classname = "soundentity";
2187 setattachment(pl.soundentity, pl, "");
2188 setmodel(pl.soundentity, "null");
2191 void SoundEntity_Detach(entity pl)
2194 for(i = 0; i <= 7; ++i)
2195 SoundEntity_StopSound(pl, i);
2199 float ParseCommandPlayerSlotTarget_firsttoken;
2200 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2208 ParseCommandPlayerSlotTarget_firsttoken = -1;
2212 if(substring(argv(idx), 0, 1) == "#")
2214 s = substring(argv(idx), 1, -1);
2222 if(s == ftos(stof(s)))
2224 e = edict_num(stof(s));
2225 if(e.flags & FL_CLIENT)
2227 ParseCommandPlayerSlotTarget_firsttoken = idx;
2234 // it must be a nick name
2239 FOR_EACH_CLIENT(head)
2240 if(head.netname == s)
2247 ParseCommandPlayerSlotTarget_firsttoken = idx;
2251 s = strdecolorize(s);
2253 FOR_EACH_CLIENT(head)
2254 if(strdecolorize(head.netname) == s)
2261 ParseCommandPlayerSlotTarget_firsttoken = idx;