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_console(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 = 0; // weapon is set a few lines later
892 t = (i == WEP_LASER || i == WEP_SHOTGUN);
893 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
894 t += (i == WEP_HOOK);
897 if(g_nexball && i == WEP_PORTO)
902 start_weapons |= e.weapons;
903 weapon_action(e.weapon, WR_PRECACHE);
910 warmup_start_ammo_shells = start_ammo_shells;
911 warmup_start_ammo_nails = start_ammo_nails;
912 warmup_start_ammo_rockets = start_ammo_rockets;
913 warmup_start_ammo_cells = start_ammo_cells;
914 warmup_start_health = start_health;
915 warmup_start_armorvalue = start_armorvalue;
916 warmup_start_weapons = start_weapons;
918 if(!g_weaponarena && !g_nixnex && !g_minstagib)
920 if(cvar("g_use_ammunition"))
922 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
923 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
924 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
925 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
927 warmup_start_health = cvar("g_warmup_start_health");
928 warmup_start_armorvalue = cvar("g_warmup_start_armor");
929 if(cvar("g_warmup_allguns"))
931 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
933 e = get_weaponinfo(i);
936 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
938 warmup_start_weapons |= e.weapons;
939 weapon_action(e.weapon, WR_PRECACHE);
946 if(g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
948 g_grappling_hook = 0; // these two can't coexist, as they use the same button
949 start_items |= IT_FUEL_REGEN;
950 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
954 start_items |= IT_JETPACK;
956 if(g_weapon_stay == 2)
958 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
959 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
960 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
961 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
962 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
963 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
964 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
965 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
966 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
967 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
970 start_ammo_shells = max(0, start_ammo_shells);
971 start_ammo_nails = max(0, start_ammo_nails);
972 start_ammo_cells = max(0, start_ammo_cells);
973 start_ammo_rockets = max(0, start_ammo_rockets);
974 start_ammo_fuel = max(0, start_ammo_fuel);
976 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
977 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
978 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
979 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
980 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
984 float g_bugrigs_planar_movement;
985 float g_bugrigs_planar_movement_car_jumping;
986 float g_bugrigs_reverse_spinning;
987 float g_bugrigs_reverse_speeding;
988 float g_bugrigs_reverse_stopping;
989 float g_bugrigs_air_steering;
990 float g_bugrigs_angle_smoothing;
991 float g_bugrigs_friction_floor;
992 float g_bugrigs_friction_brake;
993 float g_bugrigs_friction_air;
994 float g_bugrigs_accel;
995 float g_bugrigs_speed_ref;
996 float g_bugrigs_speed_pow;
997 float g_bugrigs_steer;
999 float g_touchexplode;
1000 float g_touchexplode_radius;
1001 float g_touchexplode_damage;
1002 float g_touchexplode_edgedamage;
1003 float g_touchexplode_force;
1005 void readlevelcvars(void)
1007 g_bugrigs = cvar("g_bugrigs");
1008 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1009 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1010 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1011 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1012 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1013 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1014 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1015 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1016 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1017 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1018 g_bugrigs_accel = cvar("g_bugrigs_accel");
1019 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1020 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1021 g_bugrigs_steer = cvar("g_bugrigs_steer");
1023 g_touchexplode = cvar("g_touchexplode");
1024 g_touchexplode_radius = cvar("g_touchexplode_radius");
1025 g_touchexplode_damage = cvar("g_touchexplode_damage");
1026 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1027 g_touchexplode_force = cvar("g_touchexplode_force");
1029 sv_clones = cvar("sv_clones");
1030 sv_cheats = cvar("sv_cheats");
1031 sv_gentle = cvar("sv_gentle");
1032 sv_foginterval = cvar("sv_foginterval");
1033 g_cloaked = cvar("g_cloaked");
1034 g_jump_grunt = cvar("g_jump_grunt");
1035 g_footsteps = cvar("g_footsteps");
1036 g_grappling_hook = cvar("g_grappling_hook");
1037 g_jetpack = cvar("g_jetpack");
1038 g_laserguided_missile = cvar("g_laserguided_missile");
1039 g_midair = cvar("g_midair");
1040 g_minstagib = cvar("g_minstagib");
1041 g_nixnex = cvar("g_nixnex");
1042 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1043 g_norecoil = cvar("g_norecoil");
1044 g_vampire = cvar("g_vampire");
1045 g_bloodloss = cvar("g_bloodloss");
1046 sv_maxidle = cvar("sv_maxidle");
1047 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1048 sv_pogostick = cvar("sv_pogostick");
1049 sv_doublejump = cvar("sv_doublejump");
1050 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1051 g_ctf_reverse = cvar("g_ctf_reverse");
1053 inWarmupStage = cvar("g_warmup");
1054 g_warmup_limit = cvar("g_warmup_limit");
1055 g_warmup_allguns = cvar("g_warmup_allguns");
1056 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1058 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1059 inWarmupStage = 0; // these modes cannot work together, sorry
1061 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1062 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1063 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1064 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1065 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1066 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1067 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1068 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1069 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1070 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1071 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1072 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1074 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1075 if(g_nixnex) g_weaponarena = 0;
1078 g_pickup_shells = cvar("g_pickup_shells");
1079 g_pickup_shells_max = cvar("g_pickup_shells_max");
1080 g_pickup_nails = cvar("g_pickup_nails");
1081 g_pickup_nails_max = cvar("g_pickup_nails_max");
1082 g_pickup_rockets = cvar("g_pickup_rockets");
1083 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1084 g_pickup_cells = cvar("g_pickup_cells");
1085 g_pickup_cells_max = cvar("g_pickup_cells_max");
1086 g_pickup_fuel = cvar("g_pickup_fuel");
1087 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1088 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1089 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1090 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1091 g_pickup_armormedium = cvar("g_pickup_armormedium");
1092 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1093 g_pickup_armorbig = cvar("g_pickup_armorbig");
1094 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1095 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1096 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1097 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1098 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1099 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1100 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1101 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1102 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1103 g_pickup_healthmega = cvar("g_pickup_healthmega");
1104 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1106 g_pinata = cvar("g_pinata");
1108 g_weapon_stay = cvar("g_weapon_stay");
1109 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1112 if not(inWarmupStage)
1113 game_starttime = cvar("g_start_delay");
1115 readplayerstartcvars();
1119 // TODO sound pack system
1122 string precache_sound_builtin (string s) = #19;
1123 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1124 string precache_sound(string s)
1126 return precache_sound_builtin(strcat(soundpack, s));
1128 void play2(entity e, string filename)
1130 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1132 void sound(entity e, float chan, string samp, float vol, float atten)
1134 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1139 string precache_sound (string s) = #19;
1140 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1141 float precache_sound_index (string s) = #19;
1143 #define SND_VOLUME 1
1144 #define SND_ATTENUATION 2
1145 #define SND_LARGEENTITY 8
1146 #define SND_LARGESOUND 16
1148 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1151 entno = num_for_edict(e);
1152 idx = precache_sound_index(samp);
1157 atten = floor(atten * 64);
1158 vol = floor(vol * 255);
1161 sflags |= SND_VOLUME;
1163 sflags |= SND_ATTENUATION;
1165 sflags |= SND_LARGEENTITY;
1167 sflags |= SND_LARGESOUND;
1169 WriteByte(dest, SVC_SOUND);
1170 WriteByte(dest, sflags);
1171 if(sflags & SND_VOLUME)
1172 WriteByte(dest, vol);
1173 if(sflags & SND_ATTENUATION)
1174 WriteByte(dest, atten);
1175 if(sflags & SND_LARGEENTITY)
1177 WriteShort(dest, entno);
1178 WriteByte(dest, chan);
1182 WriteShort(dest, entno * 8 + chan);
1184 if(sflags & SND_LARGESOUND)
1185 WriteShort(dest, idx);
1187 WriteByte(dest, idx);
1189 WriteCoord(dest, o_x);
1190 WriteCoord(dest, o_y);
1191 WriteCoord(dest, o_z);
1193 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1196 o = e.origin + 0.5 * (e.mins + e.maxs);
1197 soundtoat(dest, e, o, chan, samp, vol, atten);
1199 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1201 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1203 void stopsoundto(float dest, entity e, float chan)
1206 entno = num_for_edict(e);
1211 idx = precache_sound_index("misc/null.wav");
1212 sflags = SND_LARGEENTITY;
1214 sflags |= SND_LARGESOUND;
1215 WriteByte(dest, SVC_SOUND);
1216 WriteByte(dest, sflags);
1217 WriteShort(dest, entno);
1218 WriteByte(dest, chan);
1219 if(sflags & SND_LARGESOUND)
1220 WriteShort(dest, idx);
1222 WriteByte(dest, idx);
1223 WriteCoord(dest, e.origin_x);
1224 WriteCoord(dest, e.origin_y);
1225 WriteCoord(dest, e.origin_z);
1229 WriteByte(dest, SVC_STOPSOUND);
1230 WriteShort(dest, entno * 8 + chan);
1233 void stopsound(entity e, float chan)
1235 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1236 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1239 void play2(entity e, string filename)
1241 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1243 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1246 .float announcetime;
1247 float announce(entity player, string msg)
1249 if(time > player.announcetime)
1250 if(clienttype(player) == CLIENTTYPE_REAL)
1252 player.announcetime = time + 0.8;
1258 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1259 float spamsound(entity e, float chan, string samp, float vol, float atten)
1261 if(time > e.announcetime)
1263 e.announcetime = time;
1264 sound(e, chan, samp, vol, atten);
1270 void play2team(float t, string filename)
1273 FOR_EACH_REALPLAYER(head)
1276 play2(head, filename);
1280 void play2all(string samp)
1282 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1285 void PrecachePlayerSounds(string f);
1286 void precache_all_models(string pattern)
1288 float globhandle, i, n;
1291 globhandle = search_begin(pattern, TRUE, FALSE);
1294 n = search_getsize(globhandle);
1295 for(i = 0; i < n; ++i)
1297 //print(search_getfilename(globhandle, i), "\n");
1298 f = search_getfilename(globhandle, i);
1300 PrecachePlayerSounds(strcat(f, ".sounds"));
1302 search_end(globhandle);
1307 // gamemode related things
1308 precache_model ("models/misc/chatbubble.spr");
1309 precache_model ("models/misc/teambubble.spr");
1312 precache_model ("models/runematch/curse.mdl");
1313 precache_model ("models/runematch/rune.mdl");
1316 #ifdef TTURRETS_ENABLED
1317 if(cvar("g_turrets"))
1321 // Precache all player models if desired
1322 if (cvar("sv_precacheplayermodels"))
1324 PrecachePlayerSounds("sound/player/default.sounds");
1325 precache_all_models("models/player/*.zym");
1326 precache_all_models("models/player/*.dpm");
1327 precache_all_models("models/player/*.md3");
1328 precache_all_models("models/player/*.psk");
1329 //precache_model("models/player/carni.zym");
1330 //precache_model("models/player/crash.zym");
1331 //precache_model("models/player/grunt.zym");
1332 //precache_model("models/player/headhunter.zym");
1333 //precache_model("models/player/insurrectionist.zym");
1334 //precache_model("models/player/jeandarc.zym");
1335 //precache_model("models/player/lurk.zym");
1336 //precache_model("models/player/lycanthrope.zym");
1337 //precache_model("models/player/marine.zym");
1338 //precache_model("models/player/nexus.zym");
1339 //precache_model("models/player/pyria.zym");
1340 //precache_model("models/player/shock.zym");
1341 //precache_model("models/player/skadi.zym");
1342 //precache_model("models/player/specop.zym");
1343 //precache_model("models/player/visitant.zym");
1346 if(cvar("sv_defaultcharacter"))
1349 s = cvar_string("sv_defaultplayermodel_red");
1353 PrecachePlayerSounds(strcat(s, ".sounds"));
1355 s = cvar_string("sv_defaultplayermodel_blue");
1359 PrecachePlayerSounds(strcat(s, ".sounds"));
1361 s = cvar_string("sv_defaultplayermodel_yellow");
1365 PrecachePlayerSounds(strcat(s, ".sounds"));
1367 s = cvar_string("sv_defaultplayermodel_pink");
1371 PrecachePlayerSounds(strcat(s, ".sounds"));
1373 s = cvar_string("sv_defaultplayermodel");
1377 PrecachePlayerSounds(strcat(s, ".sounds"));
1383 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1384 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1387 // gore and miscellaneous sounds
1388 //precache_sound ("misc/h2ohit.wav");
1389 precache_model ("models/hook.md3");
1390 precache_sound ("misc/armorimpact.wav");
1391 precache_sound ("misc/bodyimpact1.wav");
1392 precache_sound ("misc/bodyimpact2.wav");
1393 precache_sound ("misc/gib.wav");
1394 precache_sound ("misc/gib_splat01.wav");
1395 precache_sound ("misc/gib_splat02.wav");
1396 precache_sound ("misc/gib_splat03.wav");
1397 precache_sound ("misc/gib_splat04.wav");
1398 precache_sound ("misc/hit.wav");
1399 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1400 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1401 precache_sound ("misc/null.wav");
1402 precache_sound ("misc/spawn.wav");
1403 precache_sound ("misc/talk.wav");
1404 precache_sound ("misc/teleport.wav");
1405 precache_sound ("misc/poweroff.wav");
1406 precache_sound ("player/lava.wav");
1407 precache_sound ("player/slime.wav");
1410 precache_sound ("misc/jetpack_fly.wav");
1412 // announcer sounds - male
1413 precache_sound ("announcer/male/electrobitch.wav");
1414 precache_sound ("announcer/male/airshot.wav");
1415 precache_sound ("announcer/male/03kills.wav");
1416 precache_sound ("announcer/male/05kills.wav");
1417 precache_sound ("announcer/male/10kills.wav");
1418 precache_sound ("announcer/male/15kills.wav");
1419 precache_sound ("announcer/male/20kills.wav");
1420 precache_sound ("announcer/male/25kills.wav");
1421 precache_sound ("announcer/male/30kills.wav");
1422 precache_sound ("announcer/male/botlike.wav");
1423 precache_sound ("announcer/male/yoda.wav");
1424 precache_sound ("announcer/male/headshot.wav");
1425 precache_sound ("announcer/male/impressive.wav");
1427 // announcer sounds - robotic
1428 precache_sound ("announcer/robotic/prepareforbattle.wav");
1429 precache_sound ("announcer/robotic/begin.wav");
1430 precache_sound ("announcer/robotic/timeoutcalled.wav");
1431 precache_sound ("announcer/robotic/1fragleft.wav");
1432 precache_sound ("announcer/robotic/2fragsleft.wav");
1433 precache_sound ("announcer/robotic/3fragsleft.wav");
1436 precache_sound ("announcer/robotic/lastsecond.wav");
1437 precache_sound ("announcer/robotic/narrowly.wav");
1440 precache_model ("models/sprites/1.spr32");
1441 precache_model ("models/sprites/2.spr32");
1442 precache_model ("models/sprites/3.spr32");
1443 precache_model ("models/sprites/4.spr32");
1444 precache_model ("models/sprites/5.spr32");
1445 precache_model ("models/sprites/6.spr32");
1446 precache_model ("models/sprites/7.spr32");
1447 precache_model ("models/sprites/8.spr32");
1448 precache_model ("models/sprites/9.spr32");
1449 precache_model ("models/sprites/10.spr32");
1450 precache_sound ("announcer/robotic/1.wav");
1451 precache_sound ("announcer/robotic/2.wav");
1452 precache_sound ("announcer/robotic/3.wav");
1453 precache_sound ("announcer/robotic/4.wav");
1454 precache_sound ("announcer/robotic/5.wav");
1455 precache_sound ("announcer/robotic/6.wav");
1456 precache_sound ("announcer/robotic/7.wav");
1457 precache_sound ("announcer/robotic/8.wav");
1458 precache_sound ("announcer/robotic/9.wav");
1459 precache_sound ("announcer/robotic/10.wav");
1461 // common weapon precaches
1462 precache_sound ("weapons/weapon_switch.wav");
1463 precache_sound ("weapons/weaponpickup.wav");
1464 if(g_grappling_hook)
1466 precache_sound ("weapons/hook_fire.wav"); // hook
1467 precache_sound ("weapons/hook_impact.wav"); // hook
1470 if (cvar("sv_precacheweapons") || g_nixnex)
1472 //precache weapon models/sounds
1475 while (wep <= WEP_LAST)
1477 weapon_action(wep, WR_PRECACHE);
1482 precache_model("models/elaser.mdl");
1483 precache_model("models/laser.mdl");
1484 precache_model("models/ebomb.mdl");
1487 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1489 if (!self.noise && self.music) // quake 3 uses the music field
1490 self.noise = self.music;
1492 // plays music for the level if there is any
1495 precache_sound (self.noise);
1496 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1501 // sorry, but using \ in macros breaks line numbers
1502 #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
1503 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1504 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1506 vector ExactTriggerHit_mins;
1507 vector ExactTriggerHit_maxs;
1508 float ExactTriggerHit_Recurse()
1514 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1517 if(trace_ent == self)
1522 se.solid = SOLID_NOT;
1523 f = ExactTriggerHit_Recurse();
1529 float ExactTriggerHit()
1533 if not(self.modelindex)
1537 self.solid = SOLID_BSP;
1538 ExactTriggerHit_mins = other.absmin;
1539 ExactTriggerHit_maxs = other.absmax;
1540 f = ExactTriggerHit_Recurse();
1546 // WARNING: this kills the trace globals
1547 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1548 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1550 #define INITPRIO_FIRST 0
1551 #define INITPRIO_GAMETYPE 0
1552 #define INITPRIO_GAMETYPE_FALLBACK 1
1553 #define INITPRIO_CVARS 5
1554 #define INITPRIO_FINDTARGET 10
1555 #define INITPRIO_DROPTOFLOOR 20
1556 #define INITPRIO_SETLOCATION 90
1557 #define INITPRIO_LINKDOORS 91
1558 #define INITPRIO_LAST 99
1560 .void(void) initialize_entity;
1561 .float initialize_entity_order;
1562 .entity initialize_entity_next;
1563 entity initialize_entity_first;
1565 void make_safe_for_remove(entity e)
1567 if(e.initialize_entity)
1570 for(ent = initialize_entity_first; ent; )
1572 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1574 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1575 // skip it in linked list
1578 prev.initialize_entity_next = ent.initialize_entity_next;
1579 ent = prev.initialize_entity_next;
1583 initialize_entity_first = ent.initialize_entity_next;
1584 ent = initialize_entity_first;
1590 ent = ent.initialize_entity_next;
1596 void objerror(string s)
1598 make_safe_for_remove(self);
1599 objerror_builtin(s);
1602 void remove_unsafely(entity e)
1607 void remove_safely(entity e)
1609 make_safe_for_remove(e);
1613 void InitializeEntity(entity e, void(void) func, float order)
1617 if(!e || e.initialize_entity)
1619 // make a proxy initializer entity
1623 e.classname = "initialize_entity";
1627 e.initialize_entity = func;
1628 e.initialize_entity_order = order;
1630 cur = initialize_entity_first;
1633 if(!cur || cur.initialize_entity_order > order)
1635 // insert between prev and cur
1637 prev.initialize_entity_next = e;
1639 initialize_entity_first = e;
1640 e.initialize_entity_next = cur;
1644 cur = cur.initialize_entity_next;
1647 void InitializeEntitiesRun()
1650 startoflist = initialize_entity_first;
1651 initialize_entity_first = world;
1652 for(self = startoflist; self; )
1655 var void(void) func;
1656 e = self.initialize_entity_next;
1657 func = self.initialize_entity;
1658 self.initialize_entity_order = 0;
1659 self.initialize_entity = func_null;
1660 self.initialize_entity_next = world;
1661 if(self.classname == "initialize_entity")
1665 remove_builtin(self);
1668 //dprint("Delayed initialization: ", self.classname, "\n");
1674 .float uncustomizeentityforclient_set;
1675 .void(void) uncustomizeentityforclient;
1676 void(void) SUB_Nullpointer = #0;
1677 void UncustomizeEntitiesRun()
1681 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1682 self.uncustomizeentityforclient();
1685 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1687 e.customizeentityforclient = customizer;
1688 e.uncustomizeentityforclient = uncustomizer;
1689 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1693 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1696 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1700 if(e.classname == "")
1701 e.classname = "net_linked";
1703 if(e.model == "" || self.modelindex == 0)
1707 setmodel(e, "null");
1711 e.SendEntity = sendfunc;
1712 e.SendFlags = 0xFFFFFF;
1715 e.effects |= EF_NODEPTHTEST;
1719 e.nextthink = time + dt;
1720 e.think = SUB_Remove;
1724 void adaptor_think2touch()
1733 void adaptor_think2use()
1745 // deferred dropping
1746 void DropToFloor_Handler()
1748 droptofloor_builtin();
1749 self.dropped_origin = self.origin;
1754 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1759 float trace_hits_box_a0, trace_hits_box_a1;
1761 float trace_hits_box_1d(float end, float thmi, float thma)
1765 // just check if x is in range
1773 // do the trace with respect to x
1774 // 0 -> end has to stay in thmi -> thma
1775 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1776 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1777 if(trace_hits_box_a0 > trace_hits_box_a1)
1783 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1788 // now it is a trace from 0 to end
1790 trace_hits_box_a0 = 0;
1791 trace_hits_box_a1 = 1;
1793 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1795 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1797 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1803 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1805 return trace_hits_box(start, end, thmi - ma, thma - mi);
1808 float SUB_NoImpactCheck()
1810 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1812 if(other == world && self.size != '0 0 0')
1815 tic = self.velocity * sys_ticrate;
1816 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1817 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1818 if(trace_fraction >= 1)
1820 dprint("Odd... did not hit...?\n");
1822 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1824 dprint("Detected and prevented the sky-grapple bug.\n");
1832 #define SUB_OwnerCheck() (other && (other == self.owner))
1834 #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)
1836 float MAX_IPBAN_URIS = 16;
1838 float URI_GET_DISCARD = 0;
1839 float URI_GET_IPBAN = 1;
1840 float URI_GET_IPBAN_END = 16;
1842 void URI_Get_Callback(float id, float status, string data)
1844 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1846 dprint("\nEnd of data.\n");
1848 if(id == URI_GET_DISCARD)
1852 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1855 OnlineBanList_URI_Get_Callback(id, status, data);
1859 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1863 void print_to(entity e, string s)
1866 sprint(e, strcat(s, "\n"));
1885 for(i = 0; i < MapInfo_count; ++i)
1887 if(MapInfo_Get_ByID(i))
1889 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1892 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1893 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1901 for(i = 0; i < MapInfo_count; ++i)
1903 if(MapInfo_Get_ByID(i))
1905 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1908 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1909 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1914 MapInfo_ClearTemps();
1917 return "No records are available on this server.\n";
1919 return strcat("Records on this server:\n", s);
1922 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1925 vector start, org, delta, end, enddown, mstart;
1927 m = e.dphitcontentsmask;
1928 e.dphitcontentsmask = goodcontents | badcontents;
1931 delta = world.maxs - world.mins;
1933 for(i = 0; i < attempts; ++i)
1935 start_x = org_x + random() * delta_x;
1936 start_y = org_y + random() * delta_y;
1937 start_z = org_z + random() * delta_z;
1939 // rule 1: start inside world bounds, and outside
1940 // solid, and don't start from somewhere where you can
1941 // fall down to evil
1942 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1943 if(trace_fraction >= 1)
1945 if(trace_startsolid)
1947 if(trace_dphitcontents & badcontents)
1949 if(trace_dphitq3surfaceflags & badsurfaceflags)
1952 // rule 2: if we are too high, lower the point
1953 if(trace_fraction * delta_z > maxaboveground)
1954 start = trace_endpos + '0 0 1' * maxaboveground;
1955 enddown = trace_endpos;
1957 // rule 3: make sure we aren't outside the map. This only works
1958 // for somewhat well formed maps. A good rule of thumb is that
1959 // the map should have a convex outside hull.
1960 // these can be traceLINES as we already verified the starting box
1961 mstart = start + 0.5 * (e.mins + e.maxs);
1962 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1963 if(trace_fraction >= 1)
1965 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1966 if(trace_fraction >= 1)
1968 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1969 if(trace_fraction >= 1)
1971 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1972 if(trace_fraction >= 1)
1974 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1975 if(trace_fraction >= 1)
1978 // find a random vector to "look at"
1979 end_x = org_x + random() * delta_x;
1980 end_y = org_y + random() * delta_y;
1981 end_z = org_z + random() * delta_z;
1982 end = start + normalize(end - start) * vlen(delta);
1984 // rule 4: start TO end must not be too short
1985 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1986 if(trace_startsolid)
1988 if(trace_fraction < minviewdistance / vlen(delta))
1991 // rule 5: don't want to look at sky
1992 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1995 // rule 6: we must not end up in trigger_hurt
1996 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1998 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2005 e.dphitcontentsmask = m;
2009 setorigin(e, start);
2010 e.angles = vectoangles(end - start);
2011 dprint("Needed ", ftos(i + 1), " attempts\n");
2018 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2020 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2021 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2022 WriteShort(MSG_BROADCAST, effectno);
2023 WriteCoord(MSG_BROADCAST, start_x);
2024 WriteCoord(MSG_BROADCAST, start_y);
2025 WriteCoord(MSG_BROADCAST, start_z);
2026 WriteCoord(MSG_BROADCAST, end_x);
2027 WriteCoord(MSG_BROADCAST, end_y);
2028 WriteCoord(MSG_BROADCAST, end_z);
2029 WriteCoord(MSG_BROADCAST, end_dz);
2030 WriteShort(MSG_BROADCAST, spd / 16);
2033 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2036 vector vecxy, velxy;
2038 vecxy = end - start; vecxy_z = 0;
2039 velxy = vel; velxy_z = 0;
2041 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2043 trailparticles(world, effectno, start, end);
2047 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2048 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2051 string GetGametype(); // g_world.qc
2052 void write_recordmarker(entity pl, float tstart, float dt)
2054 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2056 // also write a marker into demo files for demotc-race-record-extractor to find
2059 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2060 " ", ftos(tstart), " ", ftos(dt), "\n"));
2063 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2067 if (cvar("g_shootfromeye"))
2080 else if (cvar("g_shootfromcenter"))
2085 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2100 void attach_sameorigin(entity e, entity to, string tag)
2102 vector org, t_forward, t_left, t_up, e_forward, e_up;
2109 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2110 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2111 t_forward = v_forward * tagscale;
2112 t_left = v_right * -tagscale;
2113 t_up = v_up * tagscale;
2115 e.origin_x = org * t_forward;
2116 e.origin_y = org * t_left;
2117 e.origin_z = org * t_up;
2119 // current forward and up directions
2120 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2121 e.angles_x = -e.angles_x;
2122 fixedmakevectors(e.angles);
2124 // untransform forward, up!
2125 e_forward_x = v_forward * t_forward;
2126 e_forward_y = v_forward * t_left;
2127 e_forward_z = v_forward * t_up;
2128 e_up_x = v_up * t_forward;
2129 e_up_y = v_up * t_left;
2130 e_up_z = v_up * t_up;
2132 e.angles = fixedvectoangles2(e_forward, e_up);
2133 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2134 e.angles_x = -e.angles_x;
2136 setattachment(e, to, tag);
2137 setorigin(e, e.origin);
2140 void detach_sameorigin(entity e)
2143 org = gettaginfo(e, 0);
2144 e.angles = fixedvectoangles2(v_forward, v_up);
2145 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2146 e.angles_x = -e.angles_x;
2148 setattachment(e, world, "");
2149 setorigin(e, e.origin);
2152 void follow_sameorigin(entity e, entity to)
2154 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2155 e.aiment = to; // make the hole follow bmodel
2156 e.punchangle = to.angles; // the original angles of bmodel
2157 e.view_ofs = e.origin - to.origin; // relative origin
2158 e.v_angle = e.angles - to.angles; // relative angles
2161 void unfollow_sameorigin(entity e)
2163 e.movetype = MOVETYPE_NONE;
2166 entity gettaginfo_relative_ent;
2167 vector gettaginfo_relative(entity e, float tag)
2169 if(!gettaginfo_relative_ent)
2171 gettaginfo_relative_ent = spawn();
2172 gettaginfo_relative_ent.effects = EF_NODRAW;
2174 gettaginfo_relative_ent.model = e.model;
2175 gettaginfo_relative_ent.modelindex = e.modelindex;
2176 gettaginfo_relative_ent.frame = e.frame;
2177 return gettaginfo(gettaginfo_relative_ent, tag);
2180 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2184 if(pl.soundentity.cnt & p)
2186 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2187 pl.soundentity.cnt |= p;
2190 void SoundEntity_StopSound(entity pl, float chan)
2194 if(pl.soundentity.cnt & p)
2196 stopsoundto(MSG_ALL, pl.soundentity, chan);
2197 pl.soundentity.cnt &~= p;
2201 void SoundEntity_Attach(entity pl)
2203 pl.soundentity = spawn();
2204 pl.soundentity.classname = "soundentity";
2205 setattachment(pl.soundentity, pl, "");
2206 setmodel(pl.soundentity, "null");
2209 void SoundEntity_Detach(entity pl)
2212 for(i = 0; i <= 7; ++i)
2213 SoundEntity_StopSound(pl, i);
2217 float ParseCommandPlayerSlotTarget_firsttoken;
2218 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2226 ParseCommandPlayerSlotTarget_firsttoken = -1;
2230 if(substring(argv(idx), 0, 1) == "#")
2232 s = substring(argv(idx), 1, -1);
2240 if(s == ftos(stof(s)))
2242 e = edict_num(stof(s));
2243 if(e.flags & FL_CLIENT)
2245 ParseCommandPlayerSlotTarget_firsttoken = idx;
2252 // it must be a nick name
2257 FOR_EACH_CLIENT(head)
2258 if(head.netname == s)
2265 ParseCommandPlayerSlotTarget_firsttoken = idx;
2269 s = strdecolorize(s);
2271 FOR_EACH_CLIENT(head)
2272 if(strdecolorize(head.netname) == s)
2279 ParseCommandPlayerSlotTarget_firsttoken = idx;