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 RandomSelection_totalweight;
11 float RandomSelection_best_priority;
12 entity RandomSelection_chosen_ent;
13 float RandomSelection_chosen_float;
14 void RandomSelection_Init()
16 RandomSelection_totalweight = 0;
17 RandomSelection_chosen_ent = world;
18 RandomSelection_chosen_float = 0;
19 RandomSelection_best_priority = -1;
21 void RandomSelection_Add(entity e, float f, float weight, float priority)
23 if(priority > RandomSelection_best_priority)
25 RandomSelection_best_priority = priority;
26 RandomSelection_chosen_ent = e;
27 RandomSelection_chosen_float = f;
28 RandomSelection_totalweight = weight;
30 else if(priority == RandomSelection_best_priority)
32 RandomSelection_totalweight += weight;
33 if(random() * RandomSelection_totalweight <= weight)
35 RandomSelection_chosen_ent = e;
36 RandomSelection_chosen_float = f;
41 float DistributeEvenly_amount;
42 float DistributeEvenly_totalweight;
43 void DistributeEvenly_Init(float amount, float totalweight)
45 if(DistributeEvenly_amount)
47 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
48 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
51 DistributeEvenly_amount = 0;
53 DistributeEvenly_amount = amount;
54 DistributeEvenly_totalweight = totalweight;
56 float DistributeEvenly_Get(float weight)
61 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
62 DistributeEvenly_totalweight -= weight;
63 DistributeEvenly_amount -= f;
67 void move_out_of_solid_expand(entity e, vector by)
70 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
73 if(trace_fraction < 1)
76 // adjust origin in the other direction...
77 e.origin = e.origin - by * (1 - trace_fraction);
81 float move_out_of_solid(entity e)
86 traceline(o, o, MOVE_WORLDONLY, e);
90 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
98 move_out_of_solid_expand(e, '1 0 0' * m0_x); e.mins_x = m0_x;
99 move_out_of_solid_expand(e, '1 0 0' * m1_x); e.maxs_x = m1_x;
100 move_out_of_solid_expand(e, '0 1 0' * m0_y); e.mins_y = m0_y;
101 move_out_of_solid_expand(e, '0 1 0' * m1_y); e.maxs_y = m1_y;
102 move_out_of_solid_expand(e, '0 0 1' * m0_z); e.mins_z = m0_z;
103 move_out_of_solid_expand(e, '0 0 1' * m1_z); e.maxs_z = m1_z;
104 setorigin(e, e.origin);
106 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
116 string STR_PLAYER = "player";
117 string STR_SPECTATOR = "spectator";
118 string STR_OBSERVER = "observer";
121 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
122 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
123 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
124 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
126 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
127 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
128 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
129 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
130 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
133 // copies a string to a tempstring (so one can strunzone it)
134 string strcat1(string s) = #115; // FRIK_FILE
139 void bcenterprint(string s)
141 // TODO replace by MSG_ALL (would show it to spectators too, though)?
143 FOR_EACH_PLAYER(head)
144 if(clienttype(head) == CLIENTTYPE_REAL)
145 centerprint(head, s);
148 void GameLogEcho(string s)
153 if(cvar("sv_eventlog_files"))
158 matches = cvar("sv_eventlog_files_counter") + 1;
159 cvar_set("sv_eventlog_files_counter", ftos(matches));
162 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
163 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
164 logfile = fopen(fn, FILE_APPEND);
165 fputs(logfile, ":logversion:3\n");
169 if(cvar("sv_eventlog_files_timestamps"))
170 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
172 fputs(logfile, strcat(s, "\n"));
175 if(cvar("sv_eventlog_console"))
184 // will be opened later
189 if(logfile_open && logfile >= 0)
196 float spawnpoint_nag;
197 void relocate_spawnpoint()
199 // nudge off the floor
200 setorigin(self, self.origin + '0 0 1');
202 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
203 if (trace_startsolid)
209 if(!move_out_of_solid(self))
210 objerror("could not get out of solid at all!");
211 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
212 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
213 print(" ", ftos(self.origin_y - o_y));
214 print(" ", ftos(self.origin_z - o_z), "'\n");
215 if(cvar("g_spawnpoints_auto_move_out_of_solid"))
218 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
224 self.mins = self.maxs = '0 0 0';
225 objerror("player spawn point in solid, mapper sucks!\n");
230 if(cvar("g_spawnpoints_autodrop"))
232 setsize(self, PL_MIN, PL_MAX);
236 self.use = spawnpoint_use;
237 self.team_saved = self.team;
241 if(g_ctf || g_assault || g_onslaught || g_domination)
243 have_team_spawns = 1;
245 if(cvar("r_showbboxes"))
247 // show where spawnpoints point at too
248 makevectors(self.angles);
251 e.classname = "info_player_foo";
252 setorigin(e, self.origin + v_forward * 24);
253 setsize(e, '-8 -8 -8', '8 8 8');
254 e.solid = SOLID_TRIGGER;
258 #define strstr strstrofs
260 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
261 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
262 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
263 // BE CONSTANT OR strzoneD!
264 float strstr(string haystack, string needle, float offset)
268 len = strlen(needle);
269 endpos = strlen(haystack) - len;
270 while(offset <= endpos)
272 found = substring(haystack, offset, len);
281 float NUM_NEAREST_ENTITIES = 4;
282 entity nearest_entity[NUM_NEAREST_ENTITIES];
283 float nearest_length[NUM_NEAREST_ENTITIES];
284 entity findnearest(vector point, .string field, string value, vector axismod)
295 localhead = find(world, field, value);
298 if((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
299 dist = localhead.oldorigin;
301 dist = localhead.origin;
303 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
306 for(i = 0; i < num_nearest; ++i)
308 if(len < nearest_length[i])
312 // now i tells us where to insert at
313 // INSERTION SORT! YOU'VE SEEN IT! RUN!
314 if(i < NUM_NEAREST_ENTITIES)
316 for(j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
318 nearest_length[j + 1] = nearest_length[j];
319 nearest_entity[j + 1] = nearest_entity[j];
321 nearest_length[i] = len;
322 nearest_entity[i] = localhead;
323 if(num_nearest < NUM_NEAREST_ENTITIES)
324 num_nearest = num_nearest + 1;
327 localhead = find(localhead, field, value);
330 // now use the first one from our list that we can see
331 for(i = 0; i < num_nearest; ++i)
333 traceline(point, nearest_entity[i].origin, TRUE, world);
334 if(trace_fraction == 1)
338 dprint("Nearest point (");
339 dprint(nearest_entity[0].netname);
340 dprint(") is not visible, using a visible one.\n");
342 return nearest_entity[i];
349 dprint("Not seeing any location point, using nearest as fallback.\n");
351 dprint("Candidates were: ");
352 for(j = 0; j < num_nearest; ++j)
356 dprint(nearest_entity[j].netname);
361 return nearest_entity[0];
364 void spawnfunc_target_location()
366 self.classname = "target_location";
367 // location name in netname
368 // eventually support: count, teamgame selectors, line of sight?
371 void spawnfunc_info_location()
373 self.classname = "target_location";
374 self.message = self.netname;
377 string NearestLocation(vector p)
382 loc = findnearest(p, classname, "target_location", '1 1 1');
389 loc = findnearest(p, target, "###item###", '1 1 4');
396 string formatmessage(string msg)
407 break; // too many replacements
409 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
410 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
420 replacement = substring(msg, p, 2);
421 escape = substring(msg, p + 1, 1);
424 else if(escape == "\\")
426 else if(escape == "n")
428 else if(escape == "a")
429 replacement = ftos(floor(self.armorvalue));
430 else if(escape == "h")
431 replacement = ftos(floor(self.health));
432 else if(escape == "l")
433 replacement = NearestLocation(self.origin);
434 else if(escape == "y")
435 replacement = NearestLocation(self.cursor_trace_endpos);
436 else if(escape == "d")
437 replacement = NearestLocation(self.death_origin);
438 else if(escape == "w")
443 wep = self.switchweapon;
446 replacement = W_Name(wep);
448 else if(escape == "W")
450 if(self.items & IT_SHELLS) replacement = "shells";
451 else if(self.items & IT_NAILS) replacement = "bullets";
452 else if(self.items & IT_ROCKETS) replacement = "rockets";
453 else if(self.items & IT_CELLS) replacement = "cells";
454 else replacement = "batteries"; // ;)
456 else if(escape == "x")
458 replacement = self.cursor_trace_ent.netname;
459 if(!replacement || !self.cursor_trace_ent)
460 replacement = "nothing";
462 else if(escape == "p")
464 if(self.last_selected_player)
465 replacement = self.last_selected_player.netname;
467 replacement = "(nobody)";
469 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
470 p = p + strlen(replacement);
481 >0: receives a cvar from name=argv(f) value=argv(f+1)
483 void GetCvars_handleString(string thisname, float f, .string field, string name)
488 strunzone(self.field);
489 self.field = string_null;
496 strunzone(self.field);
497 self.field = strzone(argv(f + 1));
501 stuffcmd(self, strcat("sendcvar ", name, "\n"));
503 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
505 GetCvars_handleString(thisname, f, field, name);
506 if(f >= 0) // also initialize to the fitting value for "" when sending cvars out
510 s = func(strcat1(self.field));
513 strunzone(self.field);
514 self.field = strzone(s);
518 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
526 self.field = stof(argv(f + 1));
529 stuffcmd(self, strcat("sendcvar ", name, "\n"));
531 string W_FixWeaponOrder_ForceComplete(string s);
532 string W_FixWeaponOrder_AllowIncomplete(string s);
533 float w_getbestweapon(entity e);
534 void GetCvars(float f)
538 s = strcat1(argv(f));
539 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
540 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
541 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
542 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
543 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
544 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
545 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
546 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
547 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
548 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
549 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
550 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
551 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
552 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
553 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
554 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
555 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
556 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
557 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
558 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
559 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
561 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
564 if(s == "cl_weaponpriority")
565 self.switchweapon = w_getbestweapon(self);
569 float fexists(string f)
572 fh = fopen(f, FILE_READ);
579 void backtrace(string msg)
582 dev = cvar("developer");
583 cvar_set("developer", "1");
585 dprint("--- CUT HERE ---\nWARNING: ");
588 remove(world); // isn't there any better way to cause a backtrace?
589 dprint("\n--- CUT UNTIL HERE ---\n");
590 cvar_set("developer", ftos(dev));
593 string Team_ColorCode(float teamid)
595 if(teamid == COLOR_TEAM1)
597 else if(teamid == COLOR_TEAM2)
599 else if(teamid == COLOR_TEAM3)
601 else if(teamid == COLOR_TEAM4)
606 string Team_ColorName(float t)
608 // fixme: Search for team entities and get their .netname's!
619 string Team_ColorNameLowerCase(float t)
621 // fixme: Search for team entities and get their .netname's!
633 #define CENTERPRIO_POINT 1
634 #define CENTERPRIO_SPAM 2
635 #define CENTERPRIO_VOTE 4
636 #define CENTERPRIO_NORMAL 5
637 #define CENTERPRIO_SHIELDING 7
638 #define CENTERPRIO_MAPVOTE 9
639 #define CENTERPRIO_IDLEKICK 50
640 #define CENTERPRIO_ADMIN 99
641 .float centerprint_priority;
642 .float centerprint_expires;
643 void centerprint_atprio(entity e, float prio, string s)
645 if(intermission_running)
646 if(prio < CENTERPRIO_MAPVOTE)
648 if(time > e.centerprint_expires)
649 e.centerprint_priority = 0;
650 if(prio >= e.centerprint_priority)
652 e.centerprint_priority = prio;
653 if(timeoutStatus == 2)
654 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
656 e.centerprint_expires = time + e.cvar_scr_centertime;
657 centerprint_builtin(e, s);
660 void centerprint_expire(entity e, float prio)
662 if(prio == e.centerprint_priority)
664 e.centerprint_priority = 0;
665 centerprint_builtin(e, "");
668 void centerprint(entity e, string s)
670 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
673 // decolorizes and team colors the player name when needed
674 string playername(entity p)
677 if(teams_matter && !intermission_running && p.classname == "player")
679 t = Team_ColorCode(p.team);
680 return strcat(t, strdecolorize(p.netname));
686 vector randompos(vector m1, vector m2)
690 v_x = m2_x * random() + m1_x;
691 v_y = m2_y * random() + m1_y;
692 v_z = m2_z * random() + m1_z;
696 float g_pickup_shells;
697 float g_pickup_shells_max;
698 float g_pickup_nails;
699 float g_pickup_nails_max;
700 float g_pickup_rockets;
701 float g_pickup_rockets_max;
702 float g_pickup_cells;
703 float g_pickup_cells_max;
705 float g_pickup_fuel_jetpack;
706 float g_pickup_fuel_max;
707 float g_pickup_armorsmall;
708 float g_pickup_armorsmall_max;
709 float g_pickup_armormedium;
710 float g_pickup_armormedium_max;
711 float g_pickup_armorbig;
712 float g_pickup_armorbig_max;
713 float g_pickup_armorlarge;
714 float g_pickup_armorlarge_max;
715 float g_pickup_healthsmall;
716 float g_pickup_healthsmall_max;
717 float g_pickup_healthmedium;
718 float g_pickup_healthmedium_max;
719 float g_pickup_healthlarge;
720 float g_pickup_healthlarge_max;
721 float g_pickup_healthmega;
722 float g_pickup_healthmega_max;
724 string g_weaponarena_list;
728 float start_ammo_shells;
729 float start_ammo_nails;
730 float start_ammo_rockets;
731 float start_ammo_cells;
732 float start_ammo_fuel;
734 float start_armorvalue;
735 float warmup_start_weapons;
736 float warmup_start_ammo_shells;
737 float warmup_start_ammo_nails;
738 float warmup_start_ammo_rockets;
739 float warmup_start_ammo_cells;
740 float warmup_start_ammo_fuel;
741 float warmup_start_health;
742 float warmup_start_armorvalue;
745 entity get_weaponinfo(float w);
747 float NixNex_CanChooseWeapon(float wpn);
748 void readplayerstartcvars()
754 // initialize starting values for players
757 start_ammo_shells = 0;
758 start_ammo_nails = 0;
759 start_ammo_rockets = 0;
760 start_ammo_cells = 0;
761 start_health = cvar("g_balance_health_start");
762 start_armorvalue = cvar("g_balance_armor_start");
765 s = cvar_string("g_weaponarena");
771 g_weaponarena_list = "All Weapons";
772 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
774 e = get_weaponinfo(j);
775 g_weaponarena |= e.weapons;
776 weapon_action(e.weapon, WR_PRECACHE);
781 g_weaponarena_list = "Most Weapons";
782 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
784 e = get_weaponinfo(j);
785 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
787 g_weaponarena |= e.weapons;
788 weapon_action(e.weapon, WR_PRECACHE);
794 t = tokenize_sane(s);
795 g_weaponarena_list = "";
796 for(i = 0; i < t; ++i)
799 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
801 e = get_weaponinfo(j);
804 g_weaponarena |= e.weapons;
805 weapon_action(e.weapon, WR_PRECACHE);
806 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
812 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
815 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
821 // will be done later
822 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
823 if(NixNex_CanChooseWeapon(i))
824 weapon_action(i, WR_PRECACHE);
826 else if(g_weaponarena)
828 start_weapons = g_weaponarena;
829 start_ammo_rockets = 999;
830 start_ammo_shells = 999;
831 start_ammo_cells = 999;
832 start_ammo_nails = 999;
833 start_ammo_fuel = 999;
834 start_items |= IT_UNLIMITED_AMMO;
839 start_armorvalue = 0;
840 start_weapons = WEPBIT_MINSTANEX;
841 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
842 start_ammo_cells = cvar("g_minstagib_ammo_start");
843 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
844 start_ammo_fuel = cvar("g_start_ammo_fuel");
846 if(g_minstagib_invis_alpha <= 0)
847 g_minstagib_invis_alpha = -1;
853 start_ammo_shells = cvar("g_lms_start_ammo_shells");
854 start_ammo_nails = cvar("g_lms_start_ammo_nails");
855 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
856 start_ammo_cells = cvar("g_lms_start_ammo_cells");
857 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
858 start_health = cvar("g_lms_start_health");
859 start_armorvalue = cvar("g_lms_start_armor");
860 } else if (cvar("g_use_ammunition")) {
861 start_ammo_shells = cvar("g_start_ammo_shells");
862 start_ammo_nails = cvar("g_start_ammo_nails");
863 start_ammo_rockets = cvar("g_start_ammo_rockets");
864 start_ammo_cells = cvar("g_start_ammo_cells");
865 start_ammo_fuel = cvar("g_start_ammo_fuel");
867 start_ammo_shells = cvar("g_pickup_shells_max");
868 start_ammo_nails = cvar("g_pickup_nails_max");
869 start_ammo_rockets = cvar("g_pickup_rockets_max");
870 start_ammo_cells = cvar("g_pickup_cells_max");
871 start_ammo_fuel = cvar("g_pickup_fuel_max");
872 start_items |= IT_UNLIMITED_AMMO;
875 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
877 e = get_weaponinfo(i);
881 t = cvar(strcat("g_start_weapon_", e.netname));
883 if(t < 0) // "default" weapon selection
886 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
888 t = (i == WEP_LASER);
890 t = (i == WEP_LASER || i == WEP_SHOTGUN);
891 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
892 t += (i == WEP_HOOK);
897 start_weapons |= e.weapons;
898 weapon_action(e.weapon, WR_PRECACHE);
905 warmup_start_ammo_shells = start_ammo_shells;
906 warmup_start_ammo_nails = start_ammo_nails;
907 warmup_start_ammo_rockets = start_ammo_rockets;
908 warmup_start_ammo_cells = start_ammo_cells;
909 warmup_start_health = start_health;
910 warmup_start_armorvalue = start_armorvalue;
911 warmup_start_weapons = start_weapons;
913 if(!g_weaponarena && !g_nixnex && !g_minstagib)
915 if(cvar("g_use_ammunition"))
917 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
918 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
919 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
920 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
922 warmup_start_health = cvar("g_warmup_start_health");
923 warmup_start_armorvalue = cvar("g_warmup_start_armor");
924 if(cvar("g_warmup_allguns"))
926 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
928 e = get_weaponinfo(i);
931 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
933 warmup_start_weapons |= e.weapons;
934 weapon_action(e.weapon, WR_PRECACHE);
941 if(start_weapons & WEPBIT_HOOK)
943 // can't have off-hand hook, if hook weapon is enabled
945 // note: if g_grappling_hook is 1, also give some initial cells
948 if(!start_ammo_cells)
949 start_ammo_cells = g_pickup_cells;
950 if(!warmup_start_ammo_cells)
951 warmup_start_ammo_cells = g_pickup_cells;
954 g_grappling_hook = 0;
959 g_grappling_hook = 0; // these two can't coexist, as they use the same button
960 start_items |= IT_JETPACK;
961 start_items |= IT_FUEL_REGEN;
962 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
965 if(g_weapon_stay == 2)
967 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
968 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
969 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
970 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
971 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
972 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
973 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
974 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
975 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
976 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
979 start_ammo_shells = max(0, start_ammo_shells);
980 start_ammo_nails = max(0, start_ammo_nails);
981 start_ammo_cells = max(0, start_ammo_cells);
982 start_ammo_rockets = max(0, start_ammo_rockets);
983 start_ammo_fuel = max(0, start_ammo_fuel);
985 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
986 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
987 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
988 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
989 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
993 float g_bugrigs_planar_movement;
994 float g_bugrigs_planar_movement_car_jumping;
995 float g_bugrigs_reverse_spinning;
996 float g_bugrigs_reverse_speeding;
997 float g_bugrigs_reverse_stopping;
998 float g_bugrigs_air_steering;
999 float g_bugrigs_angle_smoothing;
1000 float g_bugrigs_friction_floor;
1001 float g_bugrigs_friction_brake;
1002 float g_bugrigs_friction_air;
1003 float g_bugrigs_accel;
1004 float g_bugrigs_speed_ref;
1005 float g_bugrigs_speed_pow;
1006 float g_bugrigs_steer;
1008 float g_touchexplode;
1009 float g_touchexplode_radius;
1010 float g_touchexplode_damage;
1011 float g_touchexplode_edgedamage;
1012 float g_touchexplode_force;
1014 void readlevelcvars(void)
1016 g_bugrigs = cvar("g_bugrigs");
1017 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1018 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1019 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1020 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1021 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1022 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1023 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1024 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1025 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1026 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1027 g_bugrigs_accel = cvar("g_bugrigs_accel");
1028 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1029 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1030 g_bugrigs_steer = cvar("g_bugrigs_steer");
1032 g_touchexplode = cvar("g_touchexplode");
1033 g_touchexplode_radius = cvar("g_touchexplode_radius");
1034 g_touchexplode_damage = cvar("g_touchexplode_damage");
1035 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1036 g_touchexplode_force = cvar("g_touchexplode_force");
1038 sv_clones = cvar("sv_clones");
1039 sv_cheats = cvar("sv_cheats");
1040 sv_gentle = cvar("sv_gentle");
1041 sv_foginterval = cvar("sv_foginterval");
1042 g_cloaked = cvar("g_cloaked");
1043 g_jump_grunt = cvar("g_jump_grunt");
1044 g_footsteps = cvar("g_footsteps");
1045 g_grappling_hook = cvar("g_grappling_hook");
1046 g_jetpack = cvar("g_jetpack");
1047 g_laserguided_missile = cvar("g_laserguided_missile");
1048 g_midair = cvar("g_midair");
1049 g_minstagib = cvar("g_minstagib");
1050 g_nixnex = cvar("g_nixnex");
1051 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1052 g_norecoil = cvar("g_norecoil");
1053 g_vampire = cvar("g_vampire");
1054 g_bloodloss = cvar("g_bloodloss");
1055 sv_maxidle = cvar("sv_maxidle");
1056 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1057 sv_pogostick = cvar("sv_pogostick");
1058 sv_doublejump = cvar("sv_doublejump");
1059 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1060 g_ctf_reverse = cvar("g_ctf_reverse");
1062 inWarmupStage = cvar("g_warmup");
1063 g_warmup_limit = cvar("g_warmup_limit");
1064 g_warmup_allguns = cvar("g_warmup_allguns");
1065 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1067 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1068 inWarmupStage = 0; // these modes cannot work together, sorry
1070 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1071 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1072 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1073 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1074 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1075 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1077 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1078 if(g_nixnex) g_weaponarena = 0;
1081 g_pickup_shells = cvar("g_pickup_shells");
1082 g_pickup_shells_max = cvar("g_pickup_shells_max");
1083 g_pickup_nails = cvar("g_pickup_nails");
1084 g_pickup_nails_max = cvar("g_pickup_nails_max");
1085 g_pickup_rockets = cvar("g_pickup_rockets");
1086 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1087 g_pickup_cells = cvar("g_pickup_cells");
1088 g_pickup_cells_max = cvar("g_pickup_cells_max");
1089 g_pickup_fuel = cvar("g_pickup_fuel");
1090 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1091 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1092 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1093 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1094 g_pickup_armormedium = cvar("g_pickup_armormedium");
1095 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1096 g_pickup_armorbig = cvar("g_pickup_armorbig");
1097 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1098 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1099 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1100 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1101 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1102 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1103 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1104 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1105 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1106 g_pickup_healthmega = cvar("g_pickup_healthmega");
1107 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1109 g_pinata = cvar("g_pinata");
1111 g_weapon_stay = cvar("g_weapon_stay");
1112 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1115 if not(inWarmupStage)
1116 game_starttime = cvar("g_start_delay");
1118 readplayerstartcvars();
1122 // TODO sound pack system
1125 string precache_sound_builtin (string s) = #19;
1126 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1127 string precache_sound(string s)
1129 return precache_sound_builtin(strcat(soundpack, s));
1131 void play2(entity e, string filename)
1133 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1135 void sound(entity e, float chan, string samp, float vol, float atten)
1137 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1142 string precache_sound (string s) = #19;
1143 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1144 float precache_sound_index (string s) = #19;
1146 #define SND_VOLUME 1
1147 #define SND_ATTENUATION 2
1148 #define SND_LARGEENTITY 8
1149 #define SND_LARGESOUND 16
1151 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1154 entno = num_for_edict(e);
1155 idx = precache_sound_index(samp);
1160 atten = floor(atten * 64);
1161 vol = floor(vol * 255);
1164 sflags |= SND_VOLUME;
1166 sflags |= SND_ATTENUATION;
1168 sflags |= SND_LARGEENTITY;
1170 sflags |= SND_LARGESOUND;
1172 WriteByte(dest, SVC_SOUND);
1173 WriteByte(dest, sflags);
1174 if(sflags & SND_VOLUME)
1175 WriteByte(dest, vol);
1176 if(sflags & SND_ATTENUATION)
1177 WriteByte(dest, atten);
1178 if(sflags & SND_LARGEENTITY)
1180 WriteShort(dest, entno);
1181 WriteByte(dest, chan);
1185 WriteShort(dest, entno * 8 + chan);
1187 if(sflags & SND_LARGESOUND)
1188 WriteShort(dest, idx);
1190 WriteByte(dest, idx);
1192 WriteCoord(dest, o_x);
1193 WriteCoord(dest, o_y);
1194 WriteCoord(dest, o_z);
1196 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1199 o = e.origin + 0.5 * (e.mins + e.maxs);
1200 soundtoat(dest, e, o, chan, samp, vol, atten);
1202 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1204 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1206 void stopsoundto(float dest, entity e, float chan)
1209 entno = num_for_edict(e);
1214 idx = precache_sound_index("misc/null.wav");
1215 sflags = SND_LARGEENTITY;
1217 sflags |= SND_LARGESOUND;
1218 WriteByte(dest, SVC_SOUND);
1219 WriteByte(dest, sflags);
1220 WriteShort(dest, entno);
1221 WriteByte(dest, chan);
1222 if(sflags & SND_LARGESOUND)
1223 WriteShort(dest, idx);
1225 WriteByte(dest, idx);
1226 WriteCoord(dest, e.origin_x);
1227 WriteCoord(dest, e.origin_y);
1228 WriteCoord(dest, e.origin_z);
1232 WriteByte(dest, SVC_STOPSOUND);
1233 WriteShort(dest, entno * 8 + chan);
1236 void stopsound(entity e, float chan)
1238 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1239 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1242 void play2(entity e, string filename)
1244 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1246 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1249 .float announcetime;
1250 float announce(entity player, string msg)
1252 if(time > player.announcetime)
1253 if(clienttype(player) == CLIENTTYPE_REAL)
1255 player.announcetime = time + 0.8;
1261 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1262 float spamsound(entity e, float chan, string samp, float vol, float atten)
1264 if(time > e.announcetime)
1266 e.announcetime = time;
1267 sound(e, chan, samp, vol, atten);
1273 void play2team(float t, string filename)
1276 FOR_EACH_REALPLAYER(head)
1279 play2(head, filename);
1283 void play2all(string samp)
1285 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1288 void PrecachePlayerSounds(string f);
1289 void precache_all_models(string pattern)
1291 float globhandle, i, n;
1294 globhandle = search_begin(pattern, TRUE, FALSE);
1297 n = search_getsize(globhandle);
1298 for(i = 0; i < n; ++i)
1300 //print(search_getfilename(globhandle, i), "\n");
1301 f = search_getfilename(globhandle, i);
1303 PrecachePlayerSounds(strcat(f, ".sounds"));
1305 search_end(globhandle);
1310 // gamemode related things
1311 precache_model ("models/misc/chatbubble.spr");
1312 precache_model ("models/misc/teambubble.spr");
1315 precache_model ("models/runematch/curse.mdl");
1316 precache_model ("models/runematch/rune.mdl");
1319 #ifdef TTURRETS_ENABLED
1320 if(cvar("g_turrets"))
1324 // Precache all player models if desired
1325 if (cvar("sv_precacheplayermodels"))
1327 PrecachePlayerSounds("sound/player/default.sounds");
1328 precache_all_models("models/player/*.zym");
1329 precache_all_models("models/player/*.dpm");
1330 precache_all_models("models/player/*.md3");
1331 precache_all_models("models/player/*.psk");
1332 //precache_model("models/player/carni.zym");
1333 //precache_model("models/player/crash.zym");
1334 //precache_model("models/player/grunt.zym");
1335 //precache_model("models/player/headhunter.zym");
1336 //precache_model("models/player/insurrectionist.zym");
1337 //precache_model("models/player/jeandarc.zym");
1338 //precache_model("models/player/lurk.zym");
1339 //precache_model("models/player/lycanthrope.zym");
1340 //precache_model("models/player/marine.zym");
1341 //precache_model("models/player/nexus.zym");
1342 //precache_model("models/player/pyria.zym");
1343 //precache_model("models/player/shock.zym");
1344 //precache_model("models/player/skadi.zym");
1345 //precache_model("models/player/specop.zym");
1346 //precache_model("models/player/visitant.zym");
1349 if(cvar("sv_defaultcharacter"))
1352 s = cvar_string("sv_defaultplayermodel_red");
1356 PrecachePlayerSounds(strcat(s, ".sounds"));
1358 s = cvar_string("sv_defaultplayermodel_blue");
1362 PrecachePlayerSounds(strcat(s, ".sounds"));
1364 s = cvar_string("sv_defaultplayermodel_yellow");
1368 PrecachePlayerSounds(strcat(s, ".sounds"));
1370 s = cvar_string("sv_defaultplayermodel_pink");
1374 PrecachePlayerSounds(strcat(s, ".sounds"));
1376 s = cvar_string("sv_defaultplayermodel");
1380 PrecachePlayerSounds(strcat(s, ".sounds"));
1386 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1387 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1390 // gore and miscellaneous sounds
1391 //precache_sound ("misc/h2ohit.wav");
1392 precache_model ("models/hook.md3");
1393 precache_sound ("misc/armorimpact.wav");
1394 precache_sound ("misc/bodyimpact1.wav");
1395 precache_sound ("misc/bodyimpact2.wav");
1396 precache_sound ("misc/gib.wav");
1397 precache_sound ("misc/gib_splat01.wav");
1398 precache_sound ("misc/gib_splat02.wav");
1399 precache_sound ("misc/gib_splat03.wav");
1400 precache_sound ("misc/gib_splat04.wav");
1401 precache_sound ("misc/hit.wav");
1402 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1403 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1404 precache_sound ("misc/null.wav");
1405 precache_sound ("misc/spawn.wav");
1406 precache_sound ("misc/talk.wav");
1407 precache_sound ("misc/teleport.wav");
1408 precache_sound ("misc/poweroff.wav");
1409 precache_sound ("player/lava.wav");
1410 precache_sound ("player/slime.wav");
1413 precache_sound ("misc/jetpack_fly.wav");
1415 // announcer sounds - male
1416 precache_sound ("announcer/male/electrobitch.wav");
1417 precache_sound ("announcer/male/airshot.wav");
1418 precache_sound ("announcer/male/03kills.wav");
1419 precache_sound ("announcer/male/05kills.wav");
1420 precache_sound ("announcer/male/10kills.wav");
1421 precache_sound ("announcer/male/15kills.wav");
1422 precache_sound ("announcer/male/20kills.wav");
1423 precache_sound ("announcer/male/25kills.wav");
1424 precache_sound ("announcer/male/30kills.wav");
1425 precache_sound ("announcer/male/botlike.wav");
1426 precache_sound ("announcer/male/yoda.wav");
1427 precache_sound ("announcer/male/headshot.wav");
1428 precache_sound ("announcer/male/impressive.wav");
1430 // announcer sounds - robotic
1431 precache_sound ("announcer/robotic/prepareforbattle.wav");
1432 precache_sound ("announcer/robotic/begin.wav");
1433 precache_sound ("announcer/robotic/timeoutcalled.wav");
1434 precache_sound ("announcer/robotic/1fragleft.wav");
1435 precache_sound ("announcer/robotic/1minuteremains.wav");
1436 precache_sound ("announcer/robotic/2fragsleft.wav");
1437 precache_sound ("announcer/robotic/3fragsleft.wav");
1440 precache_sound ("announcer/robotic/lastsecond.wav");
1441 precache_sound ("announcer/robotic/narrowly.wav");
1444 precache_model ("models/sprites/1.spr32");
1445 precache_model ("models/sprites/2.spr32");
1446 precache_model ("models/sprites/3.spr32");
1447 precache_model ("models/sprites/4.spr32");
1448 precache_model ("models/sprites/5.spr32");
1449 precache_model ("models/sprites/6.spr32");
1450 precache_model ("models/sprites/7.spr32");
1451 precache_model ("models/sprites/8.spr32");
1452 precache_model ("models/sprites/9.spr32");
1453 precache_model ("models/sprites/10.spr32");
1454 precache_sound ("announcer/robotic/1.wav");
1455 precache_sound ("announcer/robotic/2.wav");
1456 precache_sound ("announcer/robotic/3.wav");
1457 precache_sound ("announcer/robotic/4.wav");
1458 precache_sound ("announcer/robotic/5.wav");
1459 precache_sound ("announcer/robotic/6.wav");
1460 precache_sound ("announcer/robotic/7.wav");
1461 precache_sound ("announcer/robotic/8.wav");
1462 precache_sound ("announcer/robotic/9.wav");
1463 precache_sound ("announcer/robotic/10.wav");
1465 // common weapon precaches
1466 precache_sound ("weapons/weapon_switch.wav");
1467 precache_sound ("weapons/weaponpickup.wav");
1468 if(g_grappling_hook)
1470 precache_sound ("weapons/hook_fire.wav"); // hook
1471 precache_sound ("weapons/hook_impact.wav"); // hook
1474 if (cvar("sv_precacheweapons") || g_nixnex)
1476 //precache weapon models/sounds
1479 while (wep <= WEP_LAST)
1481 weapon_action(wep, WR_PRECACHE);
1486 precache_model("models/elaser.mdl");
1487 precache_model("models/laser.mdl");
1488 precache_model("models/ebomb.mdl");
1491 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1493 if (!self.noise && self.music) // quake 3 uses the music field
1494 self.noise = self.music;
1496 // plays music for the level if there is any
1499 precache_sound (self.noise);
1500 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1505 // sorry, but using \ in macros breaks line numbers
1506 #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
1507 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1508 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1510 vector ExactTriggerHit_mins;
1511 vector ExactTriggerHit_maxs;
1512 float ExactTriggerHit_Recurse()
1518 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1521 if(trace_ent == self)
1526 se.solid = SOLID_NOT;
1527 f = ExactTriggerHit_Recurse();
1533 float ExactTriggerHit()
1537 if not(self.modelindex)
1541 self.solid = SOLID_BSP;
1542 ExactTriggerHit_mins = other.absmin;
1543 ExactTriggerHit_maxs = other.absmax;
1544 f = ExactTriggerHit_Recurse();
1550 // WARNING: this kills the trace globals
1551 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1552 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1554 #define INITPRIO_FIRST 0
1555 #define INITPRIO_GAMETYPE 0
1556 #define INITPRIO_GAMETYPE_FALLBACK 1
1557 #define INITPRIO_CVARS 5
1558 #define INITPRIO_FINDTARGET 10
1559 #define INITPRIO_DROPTOFLOOR 20
1560 #define INITPRIO_SETLOCATION 90
1561 #define INITPRIO_LINKDOORS 91
1562 #define INITPRIO_LAST 99
1564 .void(void) initialize_entity;
1565 .float initialize_entity_order;
1566 .entity initialize_entity_next;
1567 entity initialize_entity_first;
1569 void make_safe_for_remove(entity e)
1571 if(e.initialize_entity)
1574 for(ent = initialize_entity_first; ent; )
1576 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1578 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1579 // skip it in linked list
1582 prev.initialize_entity_next = ent.initialize_entity_next;
1583 ent = prev.initialize_entity_next;
1587 initialize_entity_first = ent.initialize_entity_next;
1588 ent = initialize_entity_first;
1594 ent = ent.initialize_entity_next;
1600 void objerror(string s)
1602 make_safe_for_remove(self);
1603 objerror_builtin(s);
1606 void remove_unsafely(entity e)
1611 void remove_safely(entity e)
1613 make_safe_for_remove(e);
1617 void InitializeEntity(entity e, void(void) func, float order)
1621 if(!e || e.initialize_entity)
1623 // make a proxy initializer entity
1627 e.classname = "initialize_entity";
1631 e.initialize_entity = func;
1632 e.initialize_entity_order = order;
1634 cur = initialize_entity_first;
1637 if(!cur || cur.initialize_entity_order > order)
1639 // insert between prev and cur
1641 prev.initialize_entity_next = e;
1643 initialize_entity_first = e;
1644 e.initialize_entity_next = cur;
1648 cur = cur.initialize_entity_next;
1651 void InitializeEntitiesRun()
1654 startoflist = initialize_entity_first;
1655 initialize_entity_first = world;
1656 for(self = startoflist; self; )
1659 var void(void) func;
1660 e = self.initialize_entity_next;
1661 func = self.initialize_entity;
1662 self.initialize_entity_order = 0;
1663 self.initialize_entity = func_null;
1664 self.initialize_entity_next = world;
1665 if(self.classname == "initialize_entity")
1669 remove_builtin(self);
1672 //dprint("Delayed initialization: ", self.classname, "\n");
1678 .float uncustomizeentityforclient_set;
1679 .void(void) uncustomizeentityforclient;
1680 void(void) SUB_Nullpointer = #0;
1681 void UncustomizeEntitiesRun()
1685 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1686 self.uncustomizeentityforclient();
1689 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1691 e.customizeentityforclient = customizer;
1692 e.uncustomizeentityforclient = uncustomizer;
1693 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1697 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1700 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1704 if(e.classname == "")
1705 e.classname = "net_linked";
1707 if(e.model == "" || self.modelindex == 0)
1711 setmodel(e, "null");
1715 e.SendEntity = sendfunc;
1716 e.SendFlags = 0xFFFFFF;
1719 e.effects |= EF_NODEPTHTEST;
1723 e.nextthink = time + dt;
1724 e.think = SUB_Remove;
1728 void adaptor_think2touch()
1737 void adaptor_think2use()
1749 // deferred dropping
1750 void DropToFloor_Handler()
1752 droptofloor_builtin();
1753 self.dropped_origin = self.origin;
1758 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1763 float trace_hits_box_a0, trace_hits_box_a1;
1765 float trace_hits_box_1d(float end, float thmi, float thma)
1769 // just check if x is in range
1777 // do the trace with respect to x
1778 // 0 -> end has to stay in thmi -> thma
1779 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1780 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1781 if(trace_hits_box_a0 > trace_hits_box_a1)
1787 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1792 // now it is a trace from 0 to end
1794 trace_hits_box_a0 = 0;
1795 trace_hits_box_a1 = 1;
1797 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1799 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1801 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1807 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1809 return trace_hits_box(start, end, thmi - ma, thma - mi);
1812 float SUB_NoImpactCheck()
1814 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1816 if(other == world && self.size != '0 0 0')
1819 tic = self.velocity * sys_ticrate;
1820 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1821 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1822 if(trace_fraction >= 1)
1824 dprint("Odd... did not hit...?\n");
1826 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1828 dprint("Detected and prevented the sky-grapple bug.\n");
1836 #define SUB_OwnerCheck() (other && (other == self.owner))
1838 #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)
1840 float MAX_IPBAN_URIS = 16;
1842 float URI_GET_DISCARD = 0;
1843 float URI_GET_IPBAN = 1;
1844 float URI_GET_IPBAN_END = 16;
1846 void URI_Get_Callback(float id, float status, string data)
1848 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1850 dprint("\nEnd of data.\n");
1852 if(id == URI_GET_DISCARD)
1856 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1859 OnlineBanList_URI_Get_Callback(id, status, data);
1863 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1867 void print_to(entity e, string s)
1870 sprint(e, strcat(s, "\n"));
1889 for(i = 0; i < MapInfo_count; ++i)
1891 if(MapInfo_Get_ByID(i))
1893 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1896 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1897 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1905 for(i = 0; i < MapInfo_count; ++i)
1907 if(MapInfo_Get_ByID(i))
1909 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1912 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1913 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1918 MapInfo_ClearTemps();
1921 return "No records are available on this server.\n";
1923 return strcat("Records on this server:\n", s);
1926 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1929 vector start, org, delta, end, enddown, mstart;
1931 m = e.dphitcontentsmask;
1932 e.dphitcontentsmask = goodcontents | badcontents;
1935 delta = world.maxs - world.mins;
1937 for(i = 0; i < attempts; ++i)
1939 start_x = org_x + random() * delta_x;
1940 start_y = org_y + random() * delta_y;
1941 start_z = org_z + random() * delta_z;
1943 // rule 1: start inside world bounds, and outside
1944 // solid, and don't start from somewhere where you can
1945 // fall down to evil
1946 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1947 if(trace_fraction >= 1)
1949 if(trace_startsolid)
1951 if(trace_dphitcontents & badcontents)
1953 if(trace_dphitq3surfaceflags & badsurfaceflags)
1956 // rule 2: if we are too high, lower the point
1957 if(trace_fraction * delta_z > maxaboveground)
1958 start = trace_endpos + '0 0 1' * maxaboveground;
1959 enddown = trace_endpos;
1961 // rule 3: make sure we aren't outside the map. This only works
1962 // for somewhat well formed maps. A good rule of thumb is that
1963 // the map should have a convex outside hull.
1964 // these can be traceLINES as we already verified the starting box
1965 mstart = start + 0.5 * (e.mins + e.maxs);
1966 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1967 if(trace_fraction >= 1)
1969 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1970 if(trace_fraction >= 1)
1972 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1973 if(trace_fraction >= 1)
1975 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1976 if(trace_fraction >= 1)
1978 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1979 if(trace_fraction >= 1)
1982 // find a random vector to "look at"
1983 end_x = org_x + random() * delta_x;
1984 end_y = org_y + random() * delta_y;
1985 end_z = org_z + random() * delta_z;
1986 end = start + normalize(end - start) * vlen(delta);
1988 // rule 4: start TO end must not be too short
1989 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1990 if(trace_startsolid)
1992 if(trace_fraction < minviewdistance / vlen(delta))
1995 // rule 5: don't want to look at sky
1996 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1999 // rule 6: we must not end up in trigger_hurt
2000 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2002 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2009 e.dphitcontentsmask = m;
2013 setorigin(e, start);
2014 e.angles = vectoangles(end - start);
2015 dprint("Needed ", ftos(i + 1), " attempts\n");
2022 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2024 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2025 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2026 WriteShort(MSG_BROADCAST, effectno);
2027 WriteCoord(MSG_BROADCAST, start_x);
2028 WriteCoord(MSG_BROADCAST, start_y);
2029 WriteCoord(MSG_BROADCAST, start_z);
2030 WriteCoord(MSG_BROADCAST, end_x);
2031 WriteCoord(MSG_BROADCAST, end_y);
2032 WriteCoord(MSG_BROADCAST, end_z);
2033 WriteCoord(MSG_BROADCAST, end_dz);
2034 WriteShort(MSG_BROADCAST, spd / 16);
2037 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2040 vector vecxy, velxy;
2042 vecxy = end - start; vecxy_z = 0;
2043 velxy = vel; velxy_z = 0;
2045 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2047 trailparticles(world, effectno, start, end);
2051 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2052 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2055 string GetGametype(); // g_world.qc
2056 void write_recordmarker(entity pl, float tstart, float dt)
2058 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2060 // also write a marker into demo files for demotc-race-record-extractor to find
2063 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2064 " ", ftos(tstart), " ", ftos(dt), "\n"));
2067 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2071 if (cvar("g_shootfromeye"))
2084 else if (cvar("g_shootfromcenter"))
2089 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2104 void attach_sameorigin(entity e, entity to, string tag)
2106 vector org, t_forward, t_left, t_up, e_forward, e_up;
2113 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2114 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2115 t_forward = v_forward * tagscale;
2116 t_left = v_right * -tagscale;
2117 t_up = v_up * tagscale;
2119 e.origin_x = org * t_forward;
2120 e.origin_y = org * t_left;
2121 e.origin_z = org * t_up;
2123 // current forward and up directions
2124 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2125 e.angles_x = -e.angles_x;
2126 fixedmakevectors(e.angles);
2128 // untransform forward, up!
2129 e_forward_x = v_forward * t_forward;
2130 e_forward_y = v_forward * t_left;
2131 e_forward_z = v_forward * t_up;
2132 e_up_x = v_up * t_forward;
2133 e_up_y = v_up * t_left;
2134 e_up_z = v_up * t_up;
2136 e.angles = fixedvectoangles2(e_forward, e_up);
2137 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2138 e.angles_x = -e.angles_x;
2140 setattachment(e, to, tag);
2141 setorigin(e, e.origin);
2144 void detach_sameorigin(entity e)
2147 org = gettaginfo(e, 0);
2148 e.angles = fixedvectoangles2(v_forward, v_up);
2149 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2150 e.angles_x = -e.angles_x;
2152 setattachment(e, world, "");
2153 setorigin(e, e.origin);
2156 void follow_sameorigin(entity e, entity to)
2158 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2159 e.aiment = to; // make the hole follow bmodel
2160 e.punchangle = to.angles; // the original angles of bmodel
2161 e.view_ofs = e.origin - to.origin; // relative origin
2162 e.v_angle = e.angles - to.angles; // relative angles
2165 void unfollow_sameorigin(entity e)
2167 e.movetype = MOVETYPE_NONE;
2170 entity gettaginfo_relative_ent;
2171 vector gettaginfo_relative(entity e, float tag)
2173 if(!gettaginfo_relative_ent)
2175 gettaginfo_relative_ent = spawn();
2176 gettaginfo_relative_ent.effects = EF_NODRAW;
2178 gettaginfo_relative_ent.model = e.model;
2179 gettaginfo_relative_ent.modelindex = e.modelindex;
2180 gettaginfo_relative_ent.frame = e.frame;
2181 return gettaginfo(gettaginfo_relative_ent, tag);
2184 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2188 if(pl.soundentity.cnt & p)
2190 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2191 pl.soundentity.cnt |= p;
2194 void SoundEntity_StopSound(entity pl, float chan)
2198 if(pl.soundentity.cnt & p)
2200 stopsoundto(MSG_ALL, pl.soundentity, chan);
2201 pl.soundentity.cnt &~= p;
2205 void SoundEntity_Attach(entity pl)
2207 pl.soundentity = spawn();
2208 pl.soundentity.classname = "soundentity";
2209 setattachment(pl.soundentity, pl, "");
2210 setmodel(pl.soundentity, "null");
2213 void SoundEntity_Detach(entity pl)
2216 for(i = 0; i <= 7; ++i)
2217 SoundEntity_StopSound(pl, i);