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);
1271 void play2team(float t, string filename)
1274 FOR_EACH_REALPLAYER(head)
1277 play2(head, filename);
1281 void play2all(string samp)
1283 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1286 void PrecachePlayerSounds(string f);
1287 void precache_all_models(string pattern)
1289 float globhandle, i, n;
1292 globhandle = search_begin(pattern, TRUE, FALSE);
1295 n = search_getsize(globhandle);
1296 for(i = 0; i < n; ++i)
1298 //print(search_getfilename(globhandle, i), "\n");
1299 f = search_getfilename(globhandle, i);
1301 PrecachePlayerSounds(strcat(f, ".sounds"));
1303 search_end(globhandle);
1308 // gamemode related things
1309 precache_model ("models/misc/chatbubble.spr");
1310 precache_model ("models/misc/teambubble.spr");
1313 precache_model ("models/runematch/curse.mdl");
1314 precache_model ("models/runematch/rune.mdl");
1317 #ifdef TTURRETS_ENABLED
1318 if(cvar("g_turrets"))
1322 // Precache all player models if desired
1323 if (cvar("sv_precacheplayermodels"))
1325 PrecachePlayerSounds("sound/player/default.sounds");
1326 precache_all_models("models/player/*.zym");
1327 precache_all_models("models/player/*.dpm");
1328 precache_all_models("models/player/*.md3");
1329 precache_all_models("models/player/*.psk");
1330 //precache_model("models/player/carni.zym");
1331 //precache_model("models/player/crash.zym");
1332 //precache_model("models/player/grunt.zym");
1333 //precache_model("models/player/headhunter.zym");
1334 //precache_model("models/player/insurrectionist.zym");
1335 //precache_model("models/player/jeandarc.zym");
1336 //precache_model("models/player/lurk.zym");
1337 //precache_model("models/player/lycanthrope.zym");
1338 //precache_model("models/player/marine.zym");
1339 //precache_model("models/player/nexus.zym");
1340 //precache_model("models/player/pyria.zym");
1341 //precache_model("models/player/shock.zym");
1342 //precache_model("models/player/skadi.zym");
1343 //precache_model("models/player/specop.zym");
1344 //precache_model("models/player/visitant.zym");
1347 if(cvar("sv_defaultcharacter"))
1350 s = cvar_string("sv_defaultplayermodel_red");
1354 PrecachePlayerSounds(strcat(s, ".sounds"));
1356 s = cvar_string("sv_defaultplayermodel_blue");
1360 PrecachePlayerSounds(strcat(s, ".sounds"));
1362 s = cvar_string("sv_defaultplayermodel_yellow");
1366 PrecachePlayerSounds(strcat(s, ".sounds"));
1368 s = cvar_string("sv_defaultplayermodel_pink");
1372 PrecachePlayerSounds(strcat(s, ".sounds"));
1374 s = cvar_string("sv_defaultplayermodel");
1378 PrecachePlayerSounds(strcat(s, ".sounds"));
1384 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1385 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1388 // gore and miscellaneous sounds
1389 //precache_sound ("misc/h2ohit.wav");
1390 precache_model ("models/hook.md3");
1391 precache_sound ("misc/armorimpact.wav");
1392 precache_sound ("misc/bodyimpact1.wav");
1393 precache_sound ("misc/bodyimpact2.wav");
1394 precache_sound ("misc/gib.wav");
1395 precache_sound ("misc/gib_splat01.wav");
1396 precache_sound ("misc/gib_splat02.wav");
1397 precache_sound ("misc/gib_splat03.wav");
1398 precache_sound ("misc/gib_splat04.wav");
1399 precache_sound ("misc/hit.wav");
1400 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1401 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1402 precache_sound ("misc/null.wav");
1403 precache_sound ("misc/spawn.wav");
1404 precache_sound ("misc/talk.wav");
1405 precache_sound ("misc/teleport.wav");
1406 precache_sound ("misc/poweroff.wav");
1407 precache_sound ("player/lava.wav");
1408 precache_sound ("player/slime.wav");
1411 precache_sound ("misc/jetpack_fly.wav");
1413 // announcer sounds - male
1414 precache_sound ("announcer/male/electrobitch.wav");
1415 precache_sound ("announcer/male/airshot.wav");
1416 precache_sound ("announcer/male/03kills.wav");
1417 precache_sound ("announcer/male/05kills.wav");
1418 precache_sound ("announcer/male/10kills.wav");
1419 precache_sound ("announcer/male/15kills.wav");
1420 precache_sound ("announcer/male/20kills.wav");
1421 precache_sound ("announcer/male/25kills.wav");
1422 precache_sound ("announcer/male/30kills.wav");
1423 precache_sound ("announcer/male/botlike.wav");
1424 precache_sound ("announcer/male/yoda.wav");
1425 precache_sound ("announcer/male/headshot.wav");
1426 precache_sound ("announcer/male/impressive.wav");
1428 // announcer sounds - robotic
1429 precache_sound ("announcer/robotic/prepareforbattle.wav");
1430 precache_sound ("announcer/robotic/begin.wav");
1431 precache_sound ("announcer/robotic/timeoutcalled.wav");
1432 precache_sound ("announcer/robotic/1fragleft.wav");
1433 precache_sound ("announcer/robotic/1minuteremains.wav");
1434 precache_sound ("announcer/robotic/2fragsleft.wav");
1435 precache_sound ("announcer/robotic/3fragsleft.wav");
1438 precache_sound ("announcer/robotic/lastsecond.wav");
1439 precache_sound ("announcer/robotic/narrowly.wav");
1442 precache_model ("models/sprites/1.spr32");
1443 precache_model ("models/sprites/2.spr32");
1444 precache_model ("models/sprites/3.spr32");
1445 precache_model ("models/sprites/4.spr32");
1446 precache_model ("models/sprites/5.spr32");
1447 precache_model ("models/sprites/6.spr32");
1448 precache_model ("models/sprites/7.spr32");
1449 precache_model ("models/sprites/8.spr32");
1450 precache_model ("models/sprites/9.spr32");
1451 precache_model ("models/sprites/10.spr32");
1452 precache_sound ("announcer/robotic/1.wav");
1453 precache_sound ("announcer/robotic/2.wav");
1454 precache_sound ("announcer/robotic/3.wav");
1455 precache_sound ("announcer/robotic/4.wav");
1456 precache_sound ("announcer/robotic/5.wav");
1457 precache_sound ("announcer/robotic/6.wav");
1458 precache_sound ("announcer/robotic/7.wav");
1459 precache_sound ("announcer/robotic/8.wav");
1460 precache_sound ("announcer/robotic/9.wav");
1461 precache_sound ("announcer/robotic/10.wav");
1463 // common weapon precaches
1464 precache_sound ("weapons/weapon_switch.wav");
1465 precache_sound ("weapons/weaponpickup.wav");
1466 if(g_grappling_hook)
1468 precache_sound ("weapons/hook_fire.wav"); // hook
1469 precache_sound ("weapons/hook_impact.wav"); // hook
1472 if (cvar("sv_precacheweapons") || g_nixnex)
1474 //precache weapon models/sounds
1477 while (wep <= WEP_LAST)
1479 weapon_action(wep, WR_PRECACHE);
1484 precache_model("models/elaser.mdl");
1485 precache_model("models/laser.mdl");
1486 precache_model("models/ebomb.mdl");
1489 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1491 if (!self.noise && self.music) // quake 3 uses the music field
1492 self.noise = self.music;
1494 // plays music for the level if there is any
1497 precache_sound (self.noise);
1498 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1503 // sorry, but using \ in macros breaks line numbers
1504 #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
1505 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1506 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1508 vector ExactTriggerHit_mins;
1509 vector ExactTriggerHit_maxs;
1510 float ExactTriggerHit_Recurse()
1516 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1519 if(trace_ent == self)
1524 se.solid = SOLID_NOT;
1525 f = ExactTriggerHit_Recurse();
1531 float ExactTriggerHit()
1535 if not(self.modelindex)
1539 self.solid = SOLID_BSP;
1540 ExactTriggerHit_mins = other.absmin;
1541 ExactTriggerHit_maxs = other.absmax;
1542 f = ExactTriggerHit_Recurse();
1548 // WARNING: this kills the trace globals
1549 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1550 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1552 #define INITPRIO_FIRST 0
1553 #define INITPRIO_GAMETYPE 0
1554 #define INITPRIO_GAMETYPE_FALLBACK 1
1555 #define INITPRIO_CVARS 5
1556 #define INITPRIO_FINDTARGET 10
1557 #define INITPRIO_DROPTOFLOOR 20
1558 #define INITPRIO_SETLOCATION 90
1559 #define INITPRIO_LINKDOORS 91
1560 #define INITPRIO_LAST 99
1562 .void(void) initialize_entity;
1563 .float initialize_entity_order;
1564 .entity initialize_entity_next;
1565 entity initialize_entity_first;
1567 void make_safe_for_remove(entity e)
1569 if(e.initialize_entity)
1572 for(ent = initialize_entity_first; ent; )
1574 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1576 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1577 // skip it in linked list
1580 prev.initialize_entity_next = ent.initialize_entity_next;
1581 ent = prev.initialize_entity_next;
1585 initialize_entity_first = ent.initialize_entity_next;
1586 ent = initialize_entity_first;
1592 ent = ent.initialize_entity_next;
1598 void objerror(string s)
1600 make_safe_for_remove(self);
1601 objerror_builtin(s);
1604 void remove_unsafely(entity e)
1609 void remove_safely(entity e)
1611 make_safe_for_remove(e);
1615 void InitializeEntity(entity e, void(void) func, float order)
1619 if(!e || e.initialize_entity)
1621 // make a proxy initializer entity
1625 e.classname = "initialize_entity";
1629 e.initialize_entity = func;
1630 e.initialize_entity_order = order;
1632 cur = initialize_entity_first;
1635 if(!cur || cur.initialize_entity_order > order)
1637 // insert between prev and cur
1639 prev.initialize_entity_next = e;
1641 initialize_entity_first = e;
1642 e.initialize_entity_next = cur;
1646 cur = cur.initialize_entity_next;
1649 void InitializeEntitiesRun()
1652 startoflist = initialize_entity_first;
1653 initialize_entity_first = world;
1654 for(self = startoflist; self; )
1657 var void(void) func;
1658 e = self.initialize_entity_next;
1659 func = self.initialize_entity;
1660 self.initialize_entity_order = 0;
1661 self.initialize_entity = func_null;
1662 self.initialize_entity_next = world;
1663 if(self.classname == "initialize_entity")
1667 remove_builtin(self);
1670 //dprint("Delayed initialization: ", self.classname, "\n");
1676 .float uncustomizeentityforclient_set;
1677 .void(void) uncustomizeentityforclient;
1678 void(void) SUB_Nullpointer = #0;
1679 void UncustomizeEntitiesRun()
1683 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1684 self.uncustomizeentityforclient();
1687 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1689 e.customizeentityforclient = customizer;
1690 e.uncustomizeentityforclient = uncustomizer;
1691 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1695 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1698 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1702 if(e.classname == "")
1703 e.classname = "net_linked";
1705 if(e.model == "" || self.modelindex == 0)
1709 setmodel(e, "null");
1713 e.SendEntity = sendfunc;
1714 e.SendFlags = 0xFFFFFF;
1717 e.effects |= EF_NODEPTHTEST;
1721 e.nextthink = time + dt;
1722 e.think = SUB_Remove;
1726 void adaptor_think2touch()
1735 void adaptor_think2use()
1747 // deferred dropping
1748 void DropToFloor_Handler()
1750 droptofloor_builtin();
1751 self.dropped_origin = self.origin;
1756 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1761 float trace_hits_box_a0, trace_hits_box_a1;
1763 float trace_hits_box_1d(float end, float thmi, float thma)
1767 // just check if x is in range
1775 // do the trace with respect to x
1776 // 0 -> end has to stay in thmi -> thma
1777 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1778 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1779 if(trace_hits_box_a0 > trace_hits_box_a1)
1785 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1790 // now it is a trace from 0 to end
1792 trace_hits_box_a0 = 0;
1793 trace_hits_box_a1 = 1;
1795 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1797 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1799 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1805 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1807 return trace_hits_box(start, end, thmi - ma, thma - mi);
1810 float SUB_NoImpactCheck()
1812 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1814 if(other == world && self.size != '0 0 0')
1817 tic = self.velocity * sys_ticrate;
1818 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1819 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1820 if(trace_fraction >= 1)
1822 dprint("Odd... did not hit...?\n");
1824 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1826 dprint("Detected and prevented the sky-grapple bug.\n");
1834 #define SUB_OwnerCheck() (other && (other == self.owner))
1836 #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)
1838 float MAX_IPBAN_URIS = 16;
1840 float URI_GET_DISCARD = 0;
1841 float URI_GET_IPBAN = 1;
1842 float URI_GET_IPBAN_END = 16;
1844 void URI_Get_Callback(float id, float status, string data)
1846 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is\n:");
1848 dprint("\nEnd of data.\n");
1850 if(id == URI_GET_DISCARD)
1854 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1857 OnlineBanList_URI_Get_Callback(id, status, data);
1861 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1865 void print_to(entity e, string s)
1868 sprint(e, strcat(s, "\n"));
1887 for(i = 0; i < MapInfo_count; ++i)
1889 if(MapInfo_Get_ByID(i))
1891 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1894 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1895 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1903 for(i = 0; i < MapInfo_count; ++i)
1905 if(MapInfo_Get_ByID(i))
1907 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1910 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1911 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1916 MapInfo_ClearTemps();
1919 return "No records are available on this server.\n";
1921 return strcat("Records on this server:\n", s);
1924 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1927 vector start, org, delta, end, enddown, mstart;
1929 m = e.dphitcontentsmask;
1930 e.dphitcontentsmask = goodcontents | badcontents;
1933 delta = world.maxs - world.mins;
1935 for(i = 0; i < attempts; ++i)
1937 start_x = org_x + random() * delta_x;
1938 start_y = org_y + random() * delta_y;
1939 start_z = org_z + random() * delta_z;
1941 // rule 1: start inside world bounds, and outside
1942 // solid, and don't start from somewhere where you can
1943 // fall down to evil
1944 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1945 if(trace_fraction >= 1)
1947 if(trace_startsolid)
1949 if(trace_dphitcontents & badcontents)
1951 if(trace_dphitq3surfaceflags & badsurfaceflags)
1954 // rule 2: if we are too high, lower the point
1955 if(trace_fraction * delta_z > maxaboveground)
1956 start = trace_endpos + '0 0 1' * maxaboveground;
1957 enddown = trace_endpos;
1959 // rule 3: make sure we aren't outside the map. This only works
1960 // for somewhat well formed maps. A good rule of thumb is that
1961 // the map should have a convex outside hull.
1962 // these can be traceLINES as we already verified the starting box
1963 mstart = start + 0.5 * (e.mins + e.maxs);
1964 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1965 if(trace_fraction >= 1)
1967 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1968 if(trace_fraction >= 1)
1970 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1971 if(trace_fraction >= 1)
1973 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1974 if(trace_fraction >= 1)
1976 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1977 if(trace_fraction >= 1)
1980 // find a random vector to "look at"
1981 end_x = org_x + random() * delta_x;
1982 end_y = org_y + random() * delta_y;
1983 end_z = org_z + random() * delta_z;
1984 end = start + normalize(end - start) * vlen(delta);
1986 // rule 4: start TO end must not be too short
1987 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1988 if(trace_startsolid)
1990 if(trace_fraction < minviewdistance / vlen(delta))
1993 // rule 5: don't want to look at sky
1994 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1997 // rule 6: we must not end up in trigger_hurt
1998 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2000 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2007 e.dphitcontentsmask = m;
2011 setorigin(e, start);
2012 e.angles = vectoangles(end - start);
2013 dprint("Needed ", ftos(i + 1), " attempts\n");
2020 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2022 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2023 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2024 WriteShort(MSG_BROADCAST, effectno);
2025 WriteCoord(MSG_BROADCAST, start_x);
2026 WriteCoord(MSG_BROADCAST, start_y);
2027 WriteCoord(MSG_BROADCAST, start_z);
2028 WriteCoord(MSG_BROADCAST, end_x);
2029 WriteCoord(MSG_BROADCAST, end_y);
2030 WriteCoord(MSG_BROADCAST, end_z);
2031 WriteCoord(MSG_BROADCAST, end_dz);
2032 WriteShort(MSG_BROADCAST, spd / 16);
2035 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2038 vector vecxy, velxy;
2040 vecxy = end - start; vecxy_z = 0;
2041 velxy = vel; velxy_z = 0;
2043 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2045 trailparticles(world, effectno, start, end);
2049 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2050 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2053 string GetGametype(); // g_world.qc
2054 void write_recordmarker(entity pl, float tstart, float dt)
2056 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2058 // also write a marker into demo files for demotc-race-record-extractor to find
2061 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2062 " ", ftos(tstart), " ", ftos(dt), "\n"));
2065 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2069 if (cvar("g_shootfromeye"))
2082 else if (cvar("g_shootfromcenter"))
2087 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2102 void attach_sameorigin(entity e, entity to, string tag)
2104 vector org, t_forward, t_left, t_up, e_forward, e_up;
2111 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2112 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2113 t_forward = v_forward * tagscale;
2114 t_left = v_right * -tagscale;
2115 t_up = v_up * tagscale;
2117 e.origin_x = org * t_forward;
2118 e.origin_y = org * t_left;
2119 e.origin_z = org * t_up;
2121 // current forward and up directions
2122 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2123 e.angles_x = -e.angles_x;
2124 fixedmakevectors(e.angles);
2126 // untransform forward, up!
2127 e_forward_x = v_forward * t_forward;
2128 e_forward_y = v_forward * t_left;
2129 e_forward_z = v_forward * t_up;
2130 e_up_x = v_up * t_forward;
2131 e_up_y = v_up * t_left;
2132 e_up_z = v_up * t_up;
2134 e.angles = fixedvectoangles2(e_forward, e_up);
2135 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2136 e.angles_x = -e.angles_x;
2138 setattachment(e, to, tag);
2139 setorigin(e, e.origin);
2142 void detach_sameorigin(entity e)
2145 org = gettaginfo(e, 0);
2146 e.angles = fixedvectoangles2(v_forward, v_up);
2147 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2148 e.angles_x = -e.angles_x;
2150 setattachment(e, world, "");
2151 setorigin(e, e.origin);
2154 void follow_sameorigin(entity e, entity to)
2156 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2157 e.aiment = to; // make the hole follow bmodel
2158 e.punchangle = to.angles; // the original angles of bmodel
2159 e.view_ofs = e.origin - to.origin; // relative origin
2160 e.v_angle = e.angles - to.angles; // relative angles
2163 void unfollow_sameorigin(entity e)
2165 e.movetype = MOVETYPE_NONE;
2168 entity gettaginfo_relative_ent;
2169 vector gettaginfo_relative(entity e, float tag)
2171 if(!gettaginfo_relative_ent)
2173 gettaginfo_relative_ent = spawn();
2174 gettaginfo_relative_ent.effects = EF_NODRAW;
2176 gettaginfo_relative_ent.model = e.model;
2177 gettaginfo_relative_ent.modelindex = e.modelindex;
2178 gettaginfo_relative_ent.frame = e.frame;
2179 return gettaginfo(gettaginfo_relative_ent, tag);
2182 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2186 if(pl.soundentity.cnt & p)
2188 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2189 pl.soundentity.cnt |= p;
2192 void SoundEntity_StopSound(entity pl, float chan)
2196 if(pl.soundentity.cnt & p)
2198 stopsoundto(MSG_ALL, pl.soundentity, chan);
2199 pl.soundentity.cnt &~= p;
2203 void SoundEntity_Attach(entity pl)
2205 pl.soundentity = spawn();
2206 pl.soundentity.classname = "soundentity";
2207 setattachment(pl.soundentity, pl, "");
2208 setmodel(pl.soundentity, "null");
2211 void SoundEntity_Detach(entity pl)
2214 for(i = 0; i <= 7; ++i)
2215 SoundEntity_StopSound(pl, i);