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");
1068 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1069 if(g_nixnex) g_weaponarena = 0;
1072 g_pickup_shells = cvar("g_pickup_shells");
1073 g_pickup_shells_max = cvar("g_pickup_shells_max");
1074 g_pickup_nails = cvar("g_pickup_nails");
1075 g_pickup_nails_max = cvar("g_pickup_nails_max");
1076 g_pickup_rockets = cvar("g_pickup_rockets");
1077 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1078 g_pickup_cells = cvar("g_pickup_cells");
1079 g_pickup_cells_max = cvar("g_pickup_cells_max");
1080 g_pickup_fuel = cvar("g_pickup_fuel");
1081 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1082 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1083 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1084 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1085 g_pickup_armormedium = cvar("g_pickup_armormedium");
1086 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1087 g_pickup_armorbig = cvar("g_pickup_armorbig");
1088 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1089 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1090 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1091 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1092 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1093 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1094 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1095 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1096 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1097 g_pickup_healthmega = cvar("g_pickup_healthmega");
1098 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1100 g_pinata = cvar("g_pinata");
1102 g_weapon_stay = cvar("g_weapon_stay");
1103 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1106 if not(inWarmupStage)
1107 game_starttime = cvar("g_start_delay");
1109 readplayerstartcvars();
1113 // TODO sound pack system
1116 string precache_sound_builtin (string s) = #19;
1117 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1118 string precache_sound(string s)
1120 return precache_sound_builtin(strcat(soundpack, s));
1122 void play2(entity e, string filename)
1124 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1126 void sound(entity e, float chan, string samp, float vol, float atten)
1128 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1133 string precache_sound (string s) = #19;
1134 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1135 float precache_sound_index (string s) = #19;
1137 #define SND_VOLUME 1
1138 #define SND_ATTENUATION 2
1139 #define SND_LARGEENTITY 8
1140 #define SND_LARGESOUND 16
1142 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1145 entno = num_for_edict(e);
1146 idx = precache_sound_index(samp);
1151 atten = floor(atten * 64);
1152 vol = floor(vol * 255);
1155 sflags |= SND_VOLUME;
1157 sflags |= SND_ATTENUATION;
1159 sflags |= SND_LARGEENTITY;
1161 sflags |= SND_LARGESOUND;
1163 WriteByte(dest, SVC_SOUND);
1164 WriteByte(dest, sflags);
1165 if(sflags & SND_VOLUME)
1166 WriteByte(dest, vol);
1167 if(sflags & SND_ATTENUATION)
1168 WriteByte(dest, atten);
1169 if(sflags & SND_LARGEENTITY)
1171 WriteShort(dest, entno);
1172 WriteByte(dest, chan);
1176 WriteShort(dest, entno * 8 + chan);
1178 if(sflags & SND_LARGESOUND)
1179 WriteShort(dest, idx);
1181 WriteByte(dest, idx);
1183 WriteCoord(dest, o_x);
1184 WriteCoord(dest, o_y);
1185 WriteCoord(dest, o_z);
1187 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1190 o = e.origin + 0.5 * (e.mins + e.maxs);
1191 soundtoat(dest, e, o, chan, samp, vol, atten);
1193 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1195 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1197 void stopsoundto(float dest, entity e, float chan)
1200 entno = num_for_edict(e);
1205 idx = precache_sound_index("misc/null.wav");
1206 sflags = SND_LARGEENTITY;
1208 sflags |= SND_LARGESOUND;
1209 WriteByte(dest, SVC_SOUND);
1210 WriteByte(dest, sflags);
1211 WriteShort(dest, entno);
1212 WriteByte(dest, chan);
1213 if(sflags & SND_LARGESOUND)
1214 WriteShort(dest, idx);
1216 WriteByte(dest, idx);
1217 WriteCoord(dest, e.origin_x);
1218 WriteCoord(dest, e.origin_y);
1219 WriteCoord(dest, e.origin_z);
1223 WriteByte(dest, SVC_STOPSOUND);
1224 WriteShort(dest, entno * 8 + chan);
1227 void stopsound(entity e, float chan)
1229 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1230 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1233 void play2(entity e, string filename)
1235 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1237 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1240 .float announcetime;
1241 float announce(entity player, string msg)
1243 if(time > player.announcetime)
1244 if(clienttype(player) == CLIENTTYPE_REAL)
1246 player.announcetime = time + 0.8;
1252 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1253 float spamsound(entity e, float chan, string samp, float vol, float atten)
1255 if(time > e.announcetime)
1257 e.announcetime = time;
1258 sound(e, chan, samp, vol, atten);
1264 void play2team(float t, string filename)
1267 FOR_EACH_REALPLAYER(head)
1270 play2(head, filename);
1274 void play2all(string samp)
1276 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1279 void PrecachePlayerSounds(string f);
1280 void precache_all_models(string pattern)
1282 float globhandle, i, n;
1285 globhandle = search_begin(pattern, TRUE, FALSE);
1288 n = search_getsize(globhandle);
1289 for(i = 0; i < n; ++i)
1291 //print(search_getfilename(globhandle, i), "\n");
1292 f = search_getfilename(globhandle, i);
1294 PrecachePlayerSounds(strcat(f, ".sounds"));
1296 search_end(globhandle);
1301 // gamemode related things
1302 precache_model ("models/misc/chatbubble.spr");
1303 precache_model ("models/misc/teambubble.spr");
1306 precache_model ("models/runematch/curse.mdl");
1307 precache_model ("models/runematch/rune.mdl");
1310 #ifdef TTURRETS_ENABLED
1311 if(cvar("g_turrets"))
1315 // Precache all player models if desired
1316 if (cvar("sv_precacheplayermodels"))
1318 PrecachePlayerSounds("sound/player/default.sounds");
1319 precache_all_models("models/player/*.zym");
1320 precache_all_models("models/player/*.dpm");
1321 precache_all_models("models/player/*.md3");
1322 precache_all_models("models/player/*.psk");
1323 //precache_model("models/player/carni.zym");
1324 //precache_model("models/player/crash.zym");
1325 //precache_model("models/player/grunt.zym");
1326 //precache_model("models/player/headhunter.zym");
1327 //precache_model("models/player/insurrectionist.zym");
1328 //precache_model("models/player/jeandarc.zym");
1329 //precache_model("models/player/lurk.zym");
1330 //precache_model("models/player/lycanthrope.zym");
1331 //precache_model("models/player/marine.zym");
1332 //precache_model("models/player/nexus.zym");
1333 //precache_model("models/player/pyria.zym");
1334 //precache_model("models/player/shock.zym");
1335 //precache_model("models/player/skadi.zym");
1336 //precache_model("models/player/specop.zym");
1337 //precache_model("models/player/visitant.zym");
1340 if(cvar("sv_defaultcharacter"))
1343 s = cvar_string("sv_defaultplayermodel_red");
1347 PrecachePlayerSounds(strcat(s, ".sounds"));
1349 s = cvar_string("sv_defaultplayermodel_blue");
1353 PrecachePlayerSounds(strcat(s, ".sounds"));
1355 s = cvar_string("sv_defaultplayermodel_yellow");
1359 PrecachePlayerSounds(strcat(s, ".sounds"));
1361 s = cvar_string("sv_defaultplayermodel_pink");
1365 PrecachePlayerSounds(strcat(s, ".sounds"));
1367 s = cvar_string("sv_defaultplayermodel");
1371 PrecachePlayerSounds(strcat(s, ".sounds"));
1377 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1378 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1381 // gore and miscellaneous sounds
1382 //precache_sound ("misc/h2ohit.wav");
1383 precache_model ("models/hook.md3");
1384 precache_sound ("misc/armorimpact.wav");
1385 precache_sound ("misc/bodyimpact1.wav");
1386 precache_sound ("misc/bodyimpact2.wav");
1387 precache_sound ("misc/gib.wav");
1388 precache_sound ("misc/gib_splat01.wav");
1389 precache_sound ("misc/gib_splat02.wav");
1390 precache_sound ("misc/gib_splat03.wav");
1391 precache_sound ("misc/gib_splat04.wav");
1392 precache_sound ("misc/hit.wav");
1393 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1394 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1395 precache_sound ("misc/null.wav");
1396 precache_sound ("misc/spawn.wav");
1397 precache_sound ("misc/talk.wav");
1398 precache_sound ("misc/teleport.wav");
1399 precache_sound ("misc/poweroff.wav");
1400 precache_sound ("player/lava.wav");
1401 precache_sound ("player/slime.wav");
1404 precache_sound ("misc/jetpack_fly.wav");
1406 // announcer sounds - male
1407 precache_sound ("announcer/male/electrobitch.wav");
1408 precache_sound ("announcer/male/airshot.wav");
1409 precache_sound ("announcer/male/03kills.wav");
1410 precache_sound ("announcer/male/05kills.wav");
1411 precache_sound ("announcer/male/10kills.wav");
1412 precache_sound ("announcer/male/15kills.wav");
1413 precache_sound ("announcer/male/20kills.wav");
1414 precache_sound ("announcer/male/25kills.wav");
1415 precache_sound ("announcer/male/30kills.wav");
1416 precache_sound ("announcer/male/botlike.wav");
1417 precache_sound ("announcer/male/yoda.wav");
1418 precache_sound ("announcer/male/headshot.wav");
1419 precache_sound ("announcer/male/impressive.wav");
1421 // announcer sounds - robotic
1422 precache_sound ("announcer/robotic/prepareforbattle.wav");
1423 precache_sound ("announcer/robotic/begin.wav");
1424 precache_sound ("announcer/robotic/timeoutcalled.wav");
1425 precache_sound ("announcer/robotic/1fragleft.wav");
1426 precache_sound ("announcer/robotic/2fragsleft.wav");
1427 precache_sound ("announcer/robotic/3fragsleft.wav");
1430 precache_sound ("announcer/robotic/lastsecond.wav");
1431 precache_sound ("announcer/robotic/narrowly.wav");
1434 precache_model ("models/sprites/1.spr32");
1435 precache_model ("models/sprites/2.spr32");
1436 precache_model ("models/sprites/3.spr32");
1437 precache_model ("models/sprites/4.spr32");
1438 precache_model ("models/sprites/5.spr32");
1439 precache_model ("models/sprites/6.spr32");
1440 precache_model ("models/sprites/7.spr32");
1441 precache_model ("models/sprites/8.spr32");
1442 precache_model ("models/sprites/9.spr32");
1443 precache_model ("models/sprites/10.spr32");
1444 precache_sound ("announcer/robotic/1.wav");
1445 precache_sound ("announcer/robotic/2.wav");
1446 precache_sound ("announcer/robotic/3.wav");
1447 precache_sound ("announcer/robotic/4.wav");
1448 precache_sound ("announcer/robotic/5.wav");
1449 precache_sound ("announcer/robotic/6.wav");
1450 precache_sound ("announcer/robotic/7.wav");
1451 precache_sound ("announcer/robotic/8.wav");
1452 precache_sound ("announcer/robotic/9.wav");
1453 precache_sound ("announcer/robotic/10.wav");
1455 // common weapon precaches
1456 precache_sound ("weapons/weapon_switch.wav");
1457 precache_sound ("weapons/weaponpickup.wav");
1458 if(g_grappling_hook)
1460 precache_sound ("weapons/hook_fire.wav"); // hook
1461 precache_sound ("weapons/hook_impact.wav"); // hook
1464 if (cvar("sv_precacheweapons") || g_nixnex)
1466 //precache weapon models/sounds
1469 while (wep <= WEP_LAST)
1471 weapon_action(wep, WR_PRECACHE);
1476 precache_model("models/elaser.mdl");
1477 precache_model("models/laser.mdl");
1478 precache_model("models/ebomb.mdl");
1481 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1483 if (!self.noise && self.music) // quake 3 uses the music field
1484 self.noise = self.music;
1486 // plays music for the level if there is any
1489 precache_sound (self.noise);
1490 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1495 // sorry, but using \ in macros breaks line numbers
1496 #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
1497 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1498 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1500 vector ExactTriggerHit_mins;
1501 vector ExactTriggerHit_maxs;
1502 float ExactTriggerHit_Recurse()
1508 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1511 if(trace_ent == self)
1516 se.solid = SOLID_NOT;
1517 f = ExactTriggerHit_Recurse();
1523 float ExactTriggerHit()
1527 if not(self.modelindex)
1531 self.solid = SOLID_BSP;
1532 ExactTriggerHit_mins = other.absmin;
1533 ExactTriggerHit_maxs = other.absmax;
1534 f = ExactTriggerHit_Recurse();
1540 // WARNING: this kills the trace globals
1541 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1542 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1544 #define INITPRIO_FIRST 0
1545 #define INITPRIO_GAMETYPE 0
1546 #define INITPRIO_GAMETYPE_FALLBACK 1
1547 #define INITPRIO_CVARS 5
1548 #define INITPRIO_FINDTARGET 10
1549 #define INITPRIO_DROPTOFLOOR 20
1550 #define INITPRIO_SETLOCATION 90
1551 #define INITPRIO_LINKDOORS 91
1552 #define INITPRIO_LAST 99
1554 .void(void) initialize_entity;
1555 .float initialize_entity_order;
1556 .entity initialize_entity_next;
1557 entity initialize_entity_first;
1559 void make_safe_for_remove(entity e)
1561 if(e.initialize_entity)
1564 for(ent = initialize_entity_first; ent; )
1566 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1568 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1569 // skip it in linked list
1572 prev.initialize_entity_next = ent.initialize_entity_next;
1573 ent = prev.initialize_entity_next;
1577 initialize_entity_first = ent.initialize_entity_next;
1578 ent = initialize_entity_first;
1584 ent = ent.initialize_entity_next;
1590 void objerror(string s)
1592 make_safe_for_remove(self);
1593 objerror_builtin(s);
1596 void remove_unsafely(entity e)
1601 void remove_safely(entity e)
1603 make_safe_for_remove(e);
1607 void InitializeEntity(entity e, void(void) func, float order)
1611 if(!e || e.initialize_entity)
1613 // make a proxy initializer entity
1617 e.classname = "initialize_entity";
1621 e.initialize_entity = func;
1622 e.initialize_entity_order = order;
1624 cur = initialize_entity_first;
1627 if(!cur || cur.initialize_entity_order > order)
1629 // insert between prev and cur
1631 prev.initialize_entity_next = e;
1633 initialize_entity_first = e;
1634 e.initialize_entity_next = cur;
1638 cur = cur.initialize_entity_next;
1641 void InitializeEntitiesRun()
1644 startoflist = initialize_entity_first;
1645 initialize_entity_first = world;
1646 for(self = startoflist; self; )
1649 var void(void) func;
1650 e = self.initialize_entity_next;
1651 func = self.initialize_entity;
1652 self.initialize_entity_order = 0;
1653 self.initialize_entity = func_null;
1654 self.initialize_entity_next = world;
1655 if(self.classname == "initialize_entity")
1659 remove_builtin(self);
1662 //dprint("Delayed initialization: ", self.classname, "\n");
1668 .float uncustomizeentityforclient_set;
1669 .void(void) uncustomizeentityforclient;
1670 void(void) SUB_Nullpointer = #0;
1671 void UncustomizeEntitiesRun()
1675 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1676 self.uncustomizeentityforclient();
1679 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1681 e.customizeentityforclient = customizer;
1682 e.uncustomizeentityforclient = uncustomizer;
1683 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1687 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1690 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1694 if(e.classname == "")
1695 e.classname = "net_linked";
1697 if(e.model == "" || self.modelindex == 0)
1701 setmodel(e, "null");
1705 e.SendEntity = sendfunc;
1706 e.SendFlags = 0xFFFFFF;
1709 e.effects |= EF_NODEPTHTEST;
1713 e.nextthink = time + dt;
1714 e.think = SUB_Remove;
1718 void adaptor_think2touch()
1727 void adaptor_think2use()
1739 // deferred dropping
1740 void DropToFloor_Handler()
1742 droptofloor_builtin();
1743 self.dropped_origin = self.origin;
1748 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1753 float trace_hits_box_a0, trace_hits_box_a1;
1755 float trace_hits_box_1d(float end, float thmi, float thma)
1759 // just check if x is in range
1767 // do the trace with respect to x
1768 // 0 -> end has to stay in thmi -> thma
1769 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1770 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1771 if(trace_hits_box_a0 > trace_hits_box_a1)
1777 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1782 // now it is a trace from 0 to end
1784 trace_hits_box_a0 = 0;
1785 trace_hits_box_a1 = 1;
1787 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1789 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1791 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1797 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1799 return trace_hits_box(start, end, thmi - ma, thma - mi);
1802 float SUB_NoImpactCheck()
1804 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1806 if(other == world && self.size != '0 0 0')
1809 tic = self.velocity * sys_ticrate;
1810 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1811 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1812 if(trace_fraction >= 1)
1814 dprint("Odd... did not hit...?\n");
1816 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1818 dprint("Detected and prevented the sky-grapple bug.\n");
1826 #define SUB_OwnerCheck() (other && (other == self.owner))
1828 #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)
1830 float MAX_IPBAN_URIS = 16;
1832 float URI_GET_DISCARD = 0;
1833 float URI_GET_IPBAN = 1;
1834 float URI_GET_IPBAN_END = 16;
1836 void URI_Get_Callback(float id, float status, string data)
1838 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1840 dprint("\nEnd of data.\n");
1842 if(id == URI_GET_DISCARD)
1846 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1849 OnlineBanList_URI_Get_Callback(id, status, data);
1853 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1857 void print_to(entity e, string s)
1860 sprint(e, strcat(s, "\n"));
1879 for(i = 0; i < MapInfo_count; ++i)
1881 if(MapInfo_Get_ByID(i))
1883 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1886 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1887 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1895 for(i = 0; i < MapInfo_count; ++i)
1897 if(MapInfo_Get_ByID(i))
1899 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1902 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1903 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1908 MapInfo_ClearTemps();
1911 return "No records are available on this server.\n";
1913 return strcat("Records on this server:\n", s);
1916 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1919 vector start, org, delta, end, enddown, mstart;
1921 m = e.dphitcontentsmask;
1922 e.dphitcontentsmask = goodcontents | badcontents;
1925 delta = world.maxs - world.mins;
1927 for(i = 0; i < attempts; ++i)
1929 start_x = org_x + random() * delta_x;
1930 start_y = org_y + random() * delta_y;
1931 start_z = org_z + random() * delta_z;
1933 // rule 1: start inside world bounds, and outside
1934 // solid, and don't start from somewhere where you can
1935 // fall down to evil
1936 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1937 if(trace_fraction >= 1)
1939 if(trace_startsolid)
1941 if(trace_dphitcontents & badcontents)
1943 if(trace_dphitq3surfaceflags & badsurfaceflags)
1946 // rule 2: if we are too high, lower the point
1947 if(trace_fraction * delta_z > maxaboveground)
1948 start = trace_endpos + '0 0 1' * maxaboveground;
1949 enddown = trace_endpos;
1951 // rule 3: make sure we aren't outside the map. This only works
1952 // for somewhat well formed maps. A good rule of thumb is that
1953 // the map should have a convex outside hull.
1954 // these can be traceLINES as we already verified the starting box
1955 mstart = start + 0.5 * (e.mins + e.maxs);
1956 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1957 if(trace_fraction >= 1)
1959 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1960 if(trace_fraction >= 1)
1962 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1963 if(trace_fraction >= 1)
1965 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1966 if(trace_fraction >= 1)
1968 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1969 if(trace_fraction >= 1)
1972 // find a random vector to "look at"
1973 end_x = org_x + random() * delta_x;
1974 end_y = org_y + random() * delta_y;
1975 end_z = org_z + random() * delta_z;
1976 end = start + normalize(end - start) * vlen(delta);
1978 // rule 4: start TO end must not be too short
1979 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1980 if(trace_startsolid)
1982 if(trace_fraction < minviewdistance / vlen(delta))
1985 // rule 5: don't want to look at sky
1986 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1989 // rule 6: we must not end up in trigger_hurt
1990 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1992 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
1999 e.dphitcontentsmask = m;
2003 setorigin(e, start);
2004 e.angles = vectoangles(end - start);
2005 dprint("Needed ", ftos(i + 1), " attempts\n");
2012 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2014 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2015 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2016 WriteShort(MSG_BROADCAST, effectno);
2017 WriteCoord(MSG_BROADCAST, start_x);
2018 WriteCoord(MSG_BROADCAST, start_y);
2019 WriteCoord(MSG_BROADCAST, start_z);
2020 WriteCoord(MSG_BROADCAST, end_x);
2021 WriteCoord(MSG_BROADCAST, end_y);
2022 WriteCoord(MSG_BROADCAST, end_z);
2023 WriteCoord(MSG_BROADCAST, end_dz);
2024 WriteShort(MSG_BROADCAST, spd / 16);
2027 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2030 vector vecxy, velxy;
2032 vecxy = end - start; vecxy_z = 0;
2033 velxy = vel; velxy_z = 0;
2035 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2037 trailparticles(world, effectno, start, end);
2041 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2042 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2045 string GetGametype(); // g_world.qc
2046 void write_recordmarker(entity pl, float tstart, float dt)
2048 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2050 // also write a marker into demo files for demotc-race-record-extractor to find
2053 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2054 " ", ftos(tstart), " ", ftos(dt), "\n"));
2057 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2061 if (cvar("g_shootfromeye"))
2074 else if (cvar("g_shootfromcenter"))
2079 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2094 void attach_sameorigin(entity e, entity to, string tag)
2096 vector org, t_forward, t_left, t_up, e_forward, e_up;
2103 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2104 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2105 t_forward = v_forward * tagscale;
2106 t_left = v_right * -tagscale;
2107 t_up = v_up * tagscale;
2109 e.origin_x = org * t_forward;
2110 e.origin_y = org * t_left;
2111 e.origin_z = org * t_up;
2113 // current forward and up directions
2114 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2115 e.angles_x = -e.angles_x;
2116 fixedmakevectors(e.angles);
2118 // untransform forward, up!
2119 e_forward_x = v_forward * t_forward;
2120 e_forward_y = v_forward * t_left;
2121 e_forward_z = v_forward * t_up;
2122 e_up_x = v_up * t_forward;
2123 e_up_y = v_up * t_left;
2124 e_up_z = v_up * t_up;
2126 e.angles = fixedvectoangles2(e_forward, e_up);
2127 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2128 e.angles_x = -e.angles_x;
2130 setattachment(e, to, tag);
2131 setorigin(e, e.origin);
2134 void detach_sameorigin(entity e)
2137 org = gettaginfo(e, 0);
2138 e.angles = fixedvectoangles2(v_forward, v_up);
2139 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2140 e.angles_x = -e.angles_x;
2142 setattachment(e, world, "");
2143 setorigin(e, e.origin);
2146 void follow_sameorigin(entity e, entity to)
2148 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2149 e.aiment = to; // make the hole follow bmodel
2150 e.punchangle = to.angles; // the original angles of bmodel
2151 e.view_ofs = e.origin - to.origin; // relative origin
2152 e.v_angle = e.angles - to.angles; // relative angles
2155 void unfollow_sameorigin(entity e)
2157 e.movetype = MOVETYPE_NONE;
2160 entity gettaginfo_relative_ent;
2161 vector gettaginfo_relative(entity e, float tag)
2163 if(!gettaginfo_relative_ent)
2165 gettaginfo_relative_ent = spawn();
2166 gettaginfo_relative_ent.effects = EF_NODRAW;
2168 gettaginfo_relative_ent.model = e.model;
2169 gettaginfo_relative_ent.modelindex = e.modelindex;
2170 gettaginfo_relative_ent.frame = e.frame;
2171 return gettaginfo(gettaginfo_relative_ent, tag);
2174 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2178 if(pl.soundentity.cnt & p)
2180 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2181 pl.soundentity.cnt |= p;
2184 void SoundEntity_StopSound(entity pl, float chan)
2188 if(pl.soundentity.cnt & p)
2190 stopsoundto(MSG_ALL, pl.soundentity, chan);
2191 pl.soundentity.cnt &~= p;
2195 void SoundEntity_Attach(entity pl)
2197 pl.soundentity = spawn();
2198 pl.soundentity.classname = "soundentity";
2199 setattachment(pl.soundentity, pl, "");
2200 setmodel(pl.soundentity, "null");
2203 void SoundEntity_Detach(entity pl)
2206 for(i = 0; i <= 7; ++i)
2207 SoundEntity_StopSound(pl, i);
2211 float ParseCommandPlayerSlotTarget_firsttoken;
2212 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2220 ParseCommandPlayerSlotTarget_firsttoken = -1;
2224 if(substring(argv(idx), 0, 1) == "#")
2226 s = substring(argv(idx), 1, -1);
2234 if(s == ftos(stof(s)))
2236 e = edict_num(stof(s));
2237 if(e.flags & FL_CLIENT)
2239 ParseCommandPlayerSlotTarget_firsttoken = idx;
2246 // it must be a nick name
2251 FOR_EACH_CLIENT(head)
2252 if(head.netname == s)
2259 ParseCommandPlayerSlotTarget_firsttoken = idx;
2263 s = strdecolorize(s);
2265 FOR_EACH_CLIENT(head)
2266 if(strdecolorize(head.netname) == s)
2273 ParseCommandPlayerSlotTarget_firsttoken = idx;