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 || g_nexball)
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);
895 if(g_nexball && i == WEP_GRENADE_LAUNCHER)
900 start_weapons |= e.weapons;
901 weapon_action(e.weapon, WR_PRECACHE);
908 warmup_start_ammo_shells = start_ammo_shells;
909 warmup_start_ammo_nails = start_ammo_nails;
910 warmup_start_ammo_rockets = start_ammo_rockets;
911 warmup_start_ammo_cells = start_ammo_cells;
912 warmup_start_health = start_health;
913 warmup_start_armorvalue = start_armorvalue;
914 warmup_start_weapons = start_weapons;
916 if(!g_weaponarena && !g_nixnex && !g_minstagib)
918 if(cvar("g_use_ammunition"))
920 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
921 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
922 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
923 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
925 warmup_start_health = cvar("g_warmup_start_health");
926 warmup_start_armorvalue = cvar("g_warmup_start_armor");
927 if(cvar("g_warmup_allguns"))
929 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
931 e = get_weaponinfo(i);
934 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
936 warmup_start_weapons |= e.weapons;
937 weapon_action(e.weapon, WR_PRECACHE);
944 if(start_weapons & WEPBIT_HOOK)
946 // can't have off-hand hook, if hook weapon is enabled
948 // note: if g_grappling_hook is 1, also give some initial cells
951 if(!start_ammo_cells)
952 start_ammo_cells = g_pickup_cells;
953 if(!warmup_start_ammo_cells)
954 warmup_start_ammo_cells = g_pickup_cells;
957 g_grappling_hook = 0;
962 g_grappling_hook = 0; // these two can't coexist, as they use the same button
963 start_items |= IT_JETPACK;
964 start_items |= IT_FUEL_REGEN;
965 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
968 if(g_weapon_stay == 2)
970 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
971 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
972 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
973 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
974 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
975 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
976 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
977 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
978 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
979 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
982 start_ammo_shells = max(0, start_ammo_shells);
983 start_ammo_nails = max(0, start_ammo_nails);
984 start_ammo_cells = max(0, start_ammo_cells);
985 start_ammo_rockets = max(0, start_ammo_rockets);
986 start_ammo_fuel = max(0, start_ammo_fuel);
988 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
989 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
990 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
991 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
992 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
996 float g_bugrigs_planar_movement;
997 float g_bugrigs_planar_movement_car_jumping;
998 float g_bugrigs_reverse_spinning;
999 float g_bugrigs_reverse_speeding;
1000 float g_bugrigs_reverse_stopping;
1001 float g_bugrigs_air_steering;
1002 float g_bugrigs_angle_smoothing;
1003 float g_bugrigs_friction_floor;
1004 float g_bugrigs_friction_brake;
1005 float g_bugrigs_friction_air;
1006 float g_bugrigs_accel;
1007 float g_bugrigs_speed_ref;
1008 float g_bugrigs_speed_pow;
1009 float g_bugrigs_steer;
1011 float g_touchexplode;
1012 float g_touchexplode_radius;
1013 float g_touchexplode_damage;
1014 float g_touchexplode_edgedamage;
1015 float g_touchexplode_force;
1017 void readlevelcvars(void)
1019 g_bugrigs = cvar("g_bugrigs");
1020 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1021 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1022 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1023 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1024 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1025 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1026 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1027 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1028 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1029 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1030 g_bugrigs_accel = cvar("g_bugrigs_accel");
1031 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1032 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1033 g_bugrigs_steer = cvar("g_bugrigs_steer");
1035 g_touchexplode = cvar("g_touchexplode");
1036 g_touchexplode_radius = cvar("g_touchexplode_radius");
1037 g_touchexplode_damage = cvar("g_touchexplode_damage");
1038 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1039 g_touchexplode_force = cvar("g_touchexplode_force");
1041 sv_clones = cvar("sv_clones");
1042 sv_cheats = cvar("sv_cheats");
1043 sv_gentle = cvar("sv_gentle");
1044 sv_foginterval = cvar("sv_foginterval");
1045 g_cloaked = cvar("g_cloaked");
1046 g_jump_grunt = cvar("g_jump_grunt");
1047 g_footsteps = cvar("g_footsteps");
1048 g_grappling_hook = cvar("g_grappling_hook");
1049 g_jetpack = cvar("g_jetpack");
1050 g_laserguided_missile = cvar("g_laserguided_missile");
1051 g_midair = cvar("g_midair");
1052 g_minstagib = cvar("g_minstagib");
1053 g_nixnex = cvar("g_nixnex");
1054 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1055 g_norecoil = cvar("g_norecoil");
1056 g_vampire = cvar("g_vampire");
1057 g_bloodloss = cvar("g_bloodloss");
1058 sv_maxidle = cvar("sv_maxidle");
1059 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1060 sv_pogostick = cvar("sv_pogostick");
1061 sv_doublejump = cvar("sv_doublejump");
1062 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1063 g_ctf_reverse = cvar("g_ctf_reverse");
1065 inWarmupStage = cvar("g_warmup");
1066 g_warmup_limit = cvar("g_warmup_limit");
1067 g_warmup_allguns = cvar("g_warmup_allguns");
1068 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1070 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1071 inWarmupStage = 0; // these modes cannot work together, sorry
1073 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1074 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1075 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1076 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1077 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1078 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1080 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1081 if(g_nixnex) g_weaponarena = 0;
1084 g_pickup_shells = cvar("g_pickup_shells");
1085 g_pickup_shells_max = cvar("g_pickup_shells_max");
1086 g_pickup_nails = cvar("g_pickup_nails");
1087 g_pickup_nails_max = cvar("g_pickup_nails_max");
1088 g_pickup_rockets = cvar("g_pickup_rockets");
1089 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1090 g_pickup_cells = cvar("g_pickup_cells");
1091 g_pickup_cells_max = cvar("g_pickup_cells_max");
1092 g_pickup_fuel = cvar("g_pickup_fuel");
1093 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1094 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1095 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1096 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1097 g_pickup_armormedium = cvar("g_pickup_armormedium");
1098 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1099 g_pickup_armorbig = cvar("g_pickup_armorbig");
1100 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1101 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1102 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1103 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1104 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1105 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1106 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1107 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1108 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1109 g_pickup_healthmega = cvar("g_pickup_healthmega");
1110 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1112 g_pinata = cvar("g_pinata");
1114 g_weapon_stay = cvar("g_weapon_stay");
1115 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1118 if not(inWarmupStage)
1119 game_starttime = cvar("g_start_delay");
1121 readplayerstartcvars();
1125 // TODO sound pack system
1128 string precache_sound_builtin (string s) = #19;
1129 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1130 string precache_sound(string s)
1132 return precache_sound_builtin(strcat(soundpack, s));
1134 void play2(entity e, string filename)
1136 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1138 void sound(entity e, float chan, string samp, float vol, float atten)
1140 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1145 string precache_sound (string s) = #19;
1146 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1147 float precache_sound_index (string s) = #19;
1149 #define SND_VOLUME 1
1150 #define SND_ATTENUATION 2
1151 #define SND_LARGEENTITY 8
1152 #define SND_LARGESOUND 16
1154 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1157 entno = num_for_edict(e);
1158 idx = precache_sound_index(samp);
1163 atten = floor(atten * 64);
1164 vol = floor(vol * 255);
1167 sflags |= SND_VOLUME;
1169 sflags |= SND_ATTENUATION;
1171 sflags |= SND_LARGEENTITY;
1173 sflags |= SND_LARGESOUND;
1175 WriteByte(dest, SVC_SOUND);
1176 WriteByte(dest, sflags);
1177 if(sflags & SND_VOLUME)
1178 WriteByte(dest, vol);
1179 if(sflags & SND_ATTENUATION)
1180 WriteByte(dest, atten);
1181 if(sflags & SND_LARGEENTITY)
1183 WriteShort(dest, entno);
1184 WriteByte(dest, chan);
1188 WriteShort(dest, entno * 8 + chan);
1190 if(sflags & SND_LARGESOUND)
1191 WriteShort(dest, idx);
1193 WriteByte(dest, idx);
1195 WriteCoord(dest, o_x);
1196 WriteCoord(dest, o_y);
1197 WriteCoord(dest, o_z);
1199 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1202 o = e.origin + 0.5 * (e.mins + e.maxs);
1203 soundtoat(dest, e, o, chan, samp, vol, atten);
1205 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1207 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1209 void stopsoundto(float dest, entity e, float chan)
1212 entno = num_for_edict(e);
1217 idx = precache_sound_index("misc/null.wav");
1218 sflags = SND_LARGEENTITY;
1220 sflags |= SND_LARGESOUND;
1221 WriteByte(dest, SVC_SOUND);
1222 WriteByte(dest, sflags);
1223 WriteShort(dest, entno);
1224 WriteByte(dest, chan);
1225 if(sflags & SND_LARGESOUND)
1226 WriteShort(dest, idx);
1228 WriteByte(dest, idx);
1229 WriteCoord(dest, e.origin_x);
1230 WriteCoord(dest, e.origin_y);
1231 WriteCoord(dest, e.origin_z);
1235 WriteByte(dest, SVC_STOPSOUND);
1236 WriteShort(dest, entno * 8 + chan);
1239 void stopsound(entity e, float chan)
1241 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1242 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1245 void play2(entity e, string filename)
1247 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1249 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1252 .float announcetime;
1253 float announce(entity player, string msg)
1255 if(time > player.announcetime)
1256 if(clienttype(player) == CLIENTTYPE_REAL)
1258 player.announcetime = time + 0.8;
1264 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1265 float spamsound(entity e, float chan, string samp, float vol, float atten)
1267 if(time > e.announcetime)
1269 e.announcetime = time;
1270 sound(e, chan, samp, vol, atten);
1276 void play2team(float t, string filename)
1279 FOR_EACH_REALPLAYER(head)
1282 play2(head, filename);
1286 void play2all(string samp)
1288 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1291 void PrecachePlayerSounds(string f);
1292 void precache_all_models(string pattern)
1294 float globhandle, i, n;
1297 globhandle = search_begin(pattern, TRUE, FALSE);
1300 n = search_getsize(globhandle);
1301 for(i = 0; i < n; ++i)
1303 //print(search_getfilename(globhandle, i), "\n");
1304 f = search_getfilename(globhandle, i);
1306 PrecachePlayerSounds(strcat(f, ".sounds"));
1308 search_end(globhandle);
1313 // gamemode related things
1314 precache_model ("models/misc/chatbubble.spr");
1315 precache_model ("models/misc/teambubble.spr");
1318 precache_model ("models/runematch/curse.mdl");
1319 precache_model ("models/runematch/rune.mdl");
1322 #ifdef TTURRETS_ENABLED
1323 if(cvar("g_turrets"))
1327 // Precache all player models if desired
1328 if (cvar("sv_precacheplayermodels"))
1330 PrecachePlayerSounds("sound/player/default.sounds");
1331 precache_all_models("models/player/*.zym");
1332 precache_all_models("models/player/*.dpm");
1333 precache_all_models("models/player/*.md3");
1334 precache_all_models("models/player/*.psk");
1335 //precache_model("models/player/carni.zym");
1336 //precache_model("models/player/crash.zym");
1337 //precache_model("models/player/grunt.zym");
1338 //precache_model("models/player/headhunter.zym");
1339 //precache_model("models/player/insurrectionist.zym");
1340 //precache_model("models/player/jeandarc.zym");
1341 //precache_model("models/player/lurk.zym");
1342 //precache_model("models/player/lycanthrope.zym");
1343 //precache_model("models/player/marine.zym");
1344 //precache_model("models/player/nexus.zym");
1345 //precache_model("models/player/pyria.zym");
1346 //precache_model("models/player/shock.zym");
1347 //precache_model("models/player/skadi.zym");
1348 //precache_model("models/player/specop.zym");
1349 //precache_model("models/player/visitant.zym");
1352 if(cvar("sv_defaultcharacter"))
1355 s = cvar_string("sv_defaultplayermodel_red");
1359 PrecachePlayerSounds(strcat(s, ".sounds"));
1361 s = cvar_string("sv_defaultplayermodel_blue");
1365 PrecachePlayerSounds(strcat(s, ".sounds"));
1367 s = cvar_string("sv_defaultplayermodel_yellow");
1371 PrecachePlayerSounds(strcat(s, ".sounds"));
1373 s = cvar_string("sv_defaultplayermodel_pink");
1377 PrecachePlayerSounds(strcat(s, ".sounds"));
1379 s = cvar_string("sv_defaultplayermodel");
1383 PrecachePlayerSounds(strcat(s, ".sounds"));
1389 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1390 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1393 // gore and miscellaneous sounds
1394 //precache_sound ("misc/h2ohit.wav");
1395 precache_model ("models/hook.md3");
1396 precache_sound ("misc/armorimpact.wav");
1397 precache_sound ("misc/bodyimpact1.wav");
1398 precache_sound ("misc/bodyimpact2.wav");
1399 precache_sound ("misc/gib.wav");
1400 precache_sound ("misc/gib_splat01.wav");
1401 precache_sound ("misc/gib_splat02.wav");
1402 precache_sound ("misc/gib_splat03.wav");
1403 precache_sound ("misc/gib_splat04.wav");
1404 precache_sound ("misc/hit.wav");
1405 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1406 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1407 precache_sound ("misc/null.wav");
1408 precache_sound ("misc/spawn.wav");
1409 precache_sound ("misc/talk.wav");
1410 precache_sound ("misc/teleport.wav");
1411 precache_sound ("misc/poweroff.wav");
1412 precache_sound ("player/lava.wav");
1413 precache_sound ("player/slime.wav");
1416 precache_sound ("misc/jetpack_fly.wav");
1418 // announcer sounds - male
1419 precache_sound ("announcer/male/electrobitch.wav");
1420 precache_sound ("announcer/male/airshot.wav");
1421 precache_sound ("announcer/male/03kills.wav");
1422 precache_sound ("announcer/male/05kills.wav");
1423 precache_sound ("announcer/male/10kills.wav");
1424 precache_sound ("announcer/male/15kills.wav");
1425 precache_sound ("announcer/male/20kills.wav");
1426 precache_sound ("announcer/male/25kills.wav");
1427 precache_sound ("announcer/male/30kills.wav");
1428 precache_sound ("announcer/male/botlike.wav");
1429 precache_sound ("announcer/male/yoda.wav");
1430 precache_sound ("announcer/male/headshot.wav");
1431 precache_sound ("announcer/male/impressive.wav");
1433 // announcer sounds - robotic
1434 precache_sound ("announcer/robotic/prepareforbattle.wav");
1435 precache_sound ("announcer/robotic/begin.wav");
1436 precache_sound ("announcer/robotic/timeoutcalled.wav");
1437 precache_sound ("announcer/robotic/1fragleft.wav");
1438 precache_sound ("announcer/robotic/1minuteremains.wav");
1439 precache_sound ("announcer/robotic/2fragsleft.wav");
1440 precache_sound ("announcer/robotic/3fragsleft.wav");
1443 precache_sound ("announcer/robotic/lastsecond.wav");
1444 precache_sound ("announcer/robotic/narrowly.wav");
1447 precache_model ("models/sprites/1.spr32");
1448 precache_model ("models/sprites/2.spr32");
1449 precache_model ("models/sprites/3.spr32");
1450 precache_model ("models/sprites/4.spr32");
1451 precache_model ("models/sprites/5.spr32");
1452 precache_model ("models/sprites/6.spr32");
1453 precache_model ("models/sprites/7.spr32");
1454 precache_model ("models/sprites/8.spr32");
1455 precache_model ("models/sprites/9.spr32");
1456 precache_model ("models/sprites/10.spr32");
1457 precache_sound ("announcer/robotic/1.wav");
1458 precache_sound ("announcer/robotic/2.wav");
1459 precache_sound ("announcer/robotic/3.wav");
1460 precache_sound ("announcer/robotic/4.wav");
1461 precache_sound ("announcer/robotic/5.wav");
1462 precache_sound ("announcer/robotic/6.wav");
1463 precache_sound ("announcer/robotic/7.wav");
1464 precache_sound ("announcer/robotic/8.wav");
1465 precache_sound ("announcer/robotic/9.wav");
1466 precache_sound ("announcer/robotic/10.wav");
1468 // common weapon precaches
1469 precache_sound ("weapons/weapon_switch.wav");
1470 precache_sound ("weapons/weaponpickup.wav");
1471 if(g_grappling_hook)
1473 precache_sound ("weapons/hook_fire.wav"); // hook
1474 precache_sound ("weapons/hook_impact.wav"); // hook
1477 if (cvar("sv_precacheweapons") || g_nixnex)
1479 //precache weapon models/sounds
1482 while (wep <= WEP_LAST)
1484 weapon_action(wep, WR_PRECACHE);
1489 precache_model("models/elaser.mdl");
1490 precache_model("models/laser.mdl");
1491 precache_model("models/ebomb.mdl");
1494 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1496 if (!self.noise && self.music) // quake 3 uses the music field
1497 self.noise = self.music;
1499 // plays music for the level if there is any
1502 precache_sound (self.noise);
1503 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1508 // sorry, but using \ in macros breaks line numbers
1509 #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
1510 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1511 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1513 vector ExactTriggerHit_mins;
1514 vector ExactTriggerHit_maxs;
1515 float ExactTriggerHit_Recurse()
1521 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1524 if(trace_ent == self)
1529 se.solid = SOLID_NOT;
1530 f = ExactTriggerHit_Recurse();
1536 float ExactTriggerHit()
1540 if not(self.modelindex)
1544 self.solid = SOLID_BSP;
1545 ExactTriggerHit_mins = other.absmin;
1546 ExactTriggerHit_maxs = other.absmax;
1547 f = ExactTriggerHit_Recurse();
1553 // WARNING: this kills the trace globals
1554 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1555 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1557 #define INITPRIO_FIRST 0
1558 #define INITPRIO_GAMETYPE 0
1559 #define INITPRIO_GAMETYPE_FALLBACK 1
1560 #define INITPRIO_CVARS 5
1561 #define INITPRIO_FINDTARGET 10
1562 #define INITPRIO_DROPTOFLOOR 20
1563 #define INITPRIO_SETLOCATION 90
1564 #define INITPRIO_LINKDOORS 91
1565 #define INITPRIO_LAST 99
1567 .void(void) initialize_entity;
1568 .float initialize_entity_order;
1569 .entity initialize_entity_next;
1570 entity initialize_entity_first;
1572 void make_safe_for_remove(entity e)
1574 if(e.initialize_entity)
1577 for(ent = initialize_entity_first; ent; )
1579 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1581 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1582 // skip it in linked list
1585 prev.initialize_entity_next = ent.initialize_entity_next;
1586 ent = prev.initialize_entity_next;
1590 initialize_entity_first = ent.initialize_entity_next;
1591 ent = initialize_entity_first;
1597 ent = ent.initialize_entity_next;
1603 void objerror(string s)
1605 make_safe_for_remove(self);
1606 objerror_builtin(s);
1609 void remove_unsafely(entity e)
1614 void remove_safely(entity e)
1616 make_safe_for_remove(e);
1620 void InitializeEntity(entity e, void(void) func, float order)
1624 if(!e || e.initialize_entity)
1626 // make a proxy initializer entity
1630 e.classname = "initialize_entity";
1634 e.initialize_entity = func;
1635 e.initialize_entity_order = order;
1637 cur = initialize_entity_first;
1640 if(!cur || cur.initialize_entity_order > order)
1642 // insert between prev and cur
1644 prev.initialize_entity_next = e;
1646 initialize_entity_first = e;
1647 e.initialize_entity_next = cur;
1651 cur = cur.initialize_entity_next;
1654 void InitializeEntitiesRun()
1657 startoflist = initialize_entity_first;
1658 initialize_entity_first = world;
1659 for(self = startoflist; self; )
1662 var void(void) func;
1663 e = self.initialize_entity_next;
1664 func = self.initialize_entity;
1665 self.initialize_entity_order = 0;
1666 self.initialize_entity = func_null;
1667 self.initialize_entity_next = world;
1668 if(self.classname == "initialize_entity")
1672 remove_builtin(self);
1675 //dprint("Delayed initialization: ", self.classname, "\n");
1681 .float uncustomizeentityforclient_set;
1682 .void(void) uncustomizeentityforclient;
1683 void(void) SUB_Nullpointer = #0;
1684 void UncustomizeEntitiesRun()
1688 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1689 self.uncustomizeentityforclient();
1692 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1694 e.customizeentityforclient = customizer;
1695 e.uncustomizeentityforclient = uncustomizer;
1696 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1700 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1703 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1707 if(e.classname == "")
1708 e.classname = "net_linked";
1710 if(e.model == "" || self.modelindex == 0)
1714 setmodel(e, "null");
1718 e.SendEntity = sendfunc;
1719 e.SendFlags = 0xFFFFFF;
1722 e.effects |= EF_NODEPTHTEST;
1726 e.nextthink = time + dt;
1727 e.think = SUB_Remove;
1731 void adaptor_think2touch()
1740 void adaptor_think2use()
1752 // deferred dropping
1753 void DropToFloor_Handler()
1755 droptofloor_builtin();
1756 self.dropped_origin = self.origin;
1761 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1766 float trace_hits_box_a0, trace_hits_box_a1;
1768 float trace_hits_box_1d(float end, float thmi, float thma)
1772 // just check if x is in range
1780 // do the trace with respect to x
1781 // 0 -> end has to stay in thmi -> thma
1782 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1783 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1784 if(trace_hits_box_a0 > trace_hits_box_a1)
1790 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1795 // now it is a trace from 0 to end
1797 trace_hits_box_a0 = 0;
1798 trace_hits_box_a1 = 1;
1800 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1802 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1804 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1810 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1812 return trace_hits_box(start, end, thmi - ma, thma - mi);
1815 float SUB_NoImpactCheck()
1817 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1819 if(other == world && self.size != '0 0 0')
1822 tic = self.velocity * sys_ticrate;
1823 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1824 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1825 if(trace_fraction >= 1)
1827 dprint("Odd... did not hit...?\n");
1829 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1831 dprint("Detected and prevented the sky-grapple bug.\n");
1839 #define SUB_OwnerCheck() (other && (other == self.owner))
1841 #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)
1843 float MAX_IPBAN_URIS = 16;
1845 float URI_GET_DISCARD = 0;
1846 float URI_GET_IPBAN = 1;
1847 float URI_GET_IPBAN_END = 16;
1849 void URI_Get_Callback(float id, float status, string data)
1851 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1853 dprint("\nEnd of data.\n");
1855 if(id == URI_GET_DISCARD)
1859 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1862 OnlineBanList_URI_Get_Callback(id, status, data);
1866 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1870 void print_to(entity e, string s)
1873 sprint(e, strcat(s, "\n"));
1892 for(i = 0; i < MapInfo_count; ++i)
1894 if(MapInfo_Get_ByID(i))
1896 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1899 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1900 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1908 for(i = 0; i < MapInfo_count; ++i)
1910 if(MapInfo_Get_ByID(i))
1912 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1915 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1916 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1921 MapInfo_ClearTemps();
1924 return "No records are available on this server.\n";
1926 return strcat("Records on this server:\n", s);
1929 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1932 vector start, org, delta, end, enddown, mstart;
1934 m = e.dphitcontentsmask;
1935 e.dphitcontentsmask = goodcontents | badcontents;
1938 delta = world.maxs - world.mins;
1940 for(i = 0; i < attempts; ++i)
1942 start_x = org_x + random() * delta_x;
1943 start_y = org_y + random() * delta_y;
1944 start_z = org_z + random() * delta_z;
1946 // rule 1: start inside world bounds, and outside
1947 // solid, and don't start from somewhere where you can
1948 // fall down to evil
1949 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1950 if(trace_fraction >= 1)
1952 if(trace_startsolid)
1954 if(trace_dphitcontents & badcontents)
1956 if(trace_dphitq3surfaceflags & badsurfaceflags)
1959 // rule 2: if we are too high, lower the point
1960 if(trace_fraction * delta_z > maxaboveground)
1961 start = trace_endpos + '0 0 1' * maxaboveground;
1962 enddown = trace_endpos;
1964 // rule 3: make sure we aren't outside the map. This only works
1965 // for somewhat well formed maps. A good rule of thumb is that
1966 // the map should have a convex outside hull.
1967 // these can be traceLINES as we already verified the starting box
1968 mstart = start + 0.5 * (e.mins + e.maxs);
1969 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1970 if(trace_fraction >= 1)
1972 traceline(mstart, mstart - '1 0 0' * delta_x, 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 1 0' * delta_y, MOVE_NORMAL, e);
1979 if(trace_fraction >= 1)
1981 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1982 if(trace_fraction >= 1)
1985 // find a random vector to "look at"
1986 end_x = org_x + random() * delta_x;
1987 end_y = org_y + random() * delta_y;
1988 end_z = org_z + random() * delta_z;
1989 end = start + normalize(end - start) * vlen(delta);
1991 // rule 4: start TO end must not be too short
1992 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1993 if(trace_startsolid)
1995 if(trace_fraction < minviewdistance / vlen(delta))
1998 // rule 5: don't want to look at sky
1999 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2002 // rule 6: we must not end up in trigger_hurt
2003 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2005 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2012 e.dphitcontentsmask = m;
2016 setorigin(e, start);
2017 e.angles = vectoangles(end - start);
2018 dprint("Needed ", ftos(i + 1), " attempts\n");
2025 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2027 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2028 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2029 WriteShort(MSG_BROADCAST, effectno);
2030 WriteCoord(MSG_BROADCAST, start_x);
2031 WriteCoord(MSG_BROADCAST, start_y);
2032 WriteCoord(MSG_BROADCAST, start_z);
2033 WriteCoord(MSG_BROADCAST, end_x);
2034 WriteCoord(MSG_BROADCAST, end_y);
2035 WriteCoord(MSG_BROADCAST, end_z);
2036 WriteCoord(MSG_BROADCAST, end_dz);
2037 WriteShort(MSG_BROADCAST, spd / 16);
2040 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2043 vector vecxy, velxy;
2045 vecxy = end - start; vecxy_z = 0;
2046 velxy = vel; velxy_z = 0;
2048 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2050 trailparticles(world, effectno, start, end);
2054 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2055 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2058 string GetGametype(); // g_world.qc
2059 void write_recordmarker(entity pl, float tstart, float dt)
2061 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2063 // also write a marker into demo files for demotc-race-record-extractor to find
2066 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2067 " ", ftos(tstart), " ", ftos(dt), "\n"));
2070 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2074 if (cvar("g_shootfromeye"))
2087 else if (cvar("g_shootfromcenter"))
2092 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2107 void attach_sameorigin(entity e, entity to, string tag)
2109 vector org, t_forward, t_left, t_up, e_forward, e_up;
2116 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2117 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2118 t_forward = v_forward * tagscale;
2119 t_left = v_right * -tagscale;
2120 t_up = v_up * tagscale;
2122 e.origin_x = org * t_forward;
2123 e.origin_y = org * t_left;
2124 e.origin_z = org * t_up;
2126 // current forward and up directions
2127 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2128 e.angles_x = -e.angles_x;
2129 fixedmakevectors(e.angles);
2131 // untransform forward, up!
2132 e_forward_x = v_forward * t_forward;
2133 e_forward_y = v_forward * t_left;
2134 e_forward_z = v_forward * t_up;
2135 e_up_x = v_up * t_forward;
2136 e_up_y = v_up * t_left;
2137 e_up_z = v_up * t_up;
2139 e.angles = fixedvectoangles2(e_forward, e_up);
2140 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2141 e.angles_x = -e.angles_x;
2143 setattachment(e, to, tag);
2144 setorigin(e, e.origin);
2147 void detach_sameorigin(entity e)
2150 org = gettaginfo(e, 0);
2151 e.angles = fixedvectoangles2(v_forward, v_up);
2152 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2153 e.angles_x = -e.angles_x;
2155 setattachment(e, world, "");
2156 setorigin(e, e.origin);
2159 void follow_sameorigin(entity e, entity to)
2161 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2162 e.aiment = to; // make the hole follow bmodel
2163 e.punchangle = to.angles; // the original angles of bmodel
2164 e.view_ofs = e.origin - to.origin; // relative origin
2165 e.v_angle = e.angles - to.angles; // relative angles
2168 void unfollow_sameorigin(entity e)
2170 e.movetype = MOVETYPE_NONE;
2173 entity gettaginfo_relative_ent;
2174 vector gettaginfo_relative(entity e, float tag)
2176 if(!gettaginfo_relative_ent)
2178 gettaginfo_relative_ent = spawn();
2179 gettaginfo_relative_ent.effects = EF_NODRAW;
2181 gettaginfo_relative_ent.model = e.model;
2182 gettaginfo_relative_ent.modelindex = e.modelindex;
2183 gettaginfo_relative_ent.frame = e.frame;
2184 return gettaginfo(gettaginfo_relative_ent, tag);
2187 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2191 if(pl.soundentity.cnt & p)
2193 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2194 pl.soundentity.cnt |= p;
2197 void SoundEntity_StopSound(entity pl, float chan)
2201 if(pl.soundentity.cnt & p)
2203 stopsoundto(MSG_ALL, pl.soundentity, chan);
2204 pl.soundentity.cnt &~= p;
2208 void SoundEntity_Attach(entity pl)
2210 pl.soundentity = spawn();
2211 pl.soundentity.classname = "soundentity";
2212 setattachment(pl.soundentity, pl, "");
2213 setmodel(pl.soundentity, "null");
2216 void SoundEntity_Detach(entity pl)
2219 for(i = 0; i <= 7; ++i)
2220 SoundEntity_StopSound(pl, i);