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 DistributeEvenly_amount;
11 float DistributeEvenly_totalweight;
12 void DistributeEvenly_Init(float amount, float totalweight)
14 if(DistributeEvenly_amount)
16 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
17 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
20 DistributeEvenly_amount = 0;
22 DistributeEvenly_amount = amount;
23 DistributeEvenly_totalweight = totalweight;
25 float DistributeEvenly_Get(float weight)
30 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
31 DistributeEvenly_totalweight -= weight;
32 DistributeEvenly_amount -= f;
36 void move_out_of_solid_expand(entity e, vector by)
39 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
42 if(trace_fraction < 1)
45 // adjust origin in the other direction...
46 e.origin = e.origin - by * (1 - trace_fraction);
50 float move_out_of_solid(entity e)
55 traceline(o, o, MOVE_WORLDONLY, e);
59 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
67 move_out_of_solid_expand(e, '1 0 0' * m0_x); e.mins_x = m0_x;
68 move_out_of_solid_expand(e, '1 0 0' * m1_x); e.maxs_x = m1_x;
69 move_out_of_solid_expand(e, '0 1 0' * m0_y); e.mins_y = m0_y;
70 move_out_of_solid_expand(e, '0 1 0' * m1_y); e.maxs_y = m1_y;
71 move_out_of_solid_expand(e, '0 0 1' * m0_z); e.mins_z = m0_z;
72 move_out_of_solid_expand(e, '0 0 1' * m1_z); e.maxs_z = m1_z;
73 setorigin(e, e.origin);
75 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
85 string STR_PLAYER = "player";
86 string STR_SPECTATOR = "spectator";
87 string STR_OBSERVER = "observer";
90 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
91 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
92 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
93 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
95 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
96 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
97 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
98 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
102 // copies a string to a tempstring (so one can strunzone it)
103 string strcat1(string s) = #115; // FRIK_FILE
108 void bcenterprint(string s)
110 // TODO replace by MSG_ALL (would show it to spectators too, though)?
112 FOR_EACH_PLAYER(head)
113 if(clienttype(head) == CLIENTTYPE_REAL)
114 centerprint(head, s);
117 void GameLogEcho(string s)
122 if(cvar("sv_eventlog_files"))
127 matches = cvar("sv_eventlog_files_counter") + 1;
128 cvar_set("sv_eventlog_files_counter", ftos(matches));
131 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
132 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
133 logfile = fopen(fn, FILE_APPEND);
134 fputs(logfile, ":logversion:3\n");
138 if(cvar("sv_eventlog_files_timestamps"))
139 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
141 fputs(logfile, strcat(s, "\n"));
144 if(cvar("sv_eventlog_console"))
153 // will be opened later
158 if(logfile_open && logfile >= 0)
165 float spawnpoint_nag;
166 void relocate_spawnpoint()
168 // nudge off the floor
169 setorigin(self, self.origin + '0 0 1');
171 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
172 if (trace_startsolid)
178 if(!move_out_of_solid(self))
179 objerror("could not get out of solid at all!");
180 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
181 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
182 print(" ", ftos(self.origin_y - o_y));
183 print(" ", ftos(self.origin_z - o_z), "'\n");
184 if(cvar("g_spawnpoints_auto_move_out_of_solid"))
187 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
193 self.mins = self.maxs = '0 0 0';
194 objerror("player spawn point in solid, mapper sucks!\n");
199 if(cvar("g_spawnpoints_autodrop"))
201 setsize(self, PL_MIN, PL_MAX);
205 self.use = spawnpoint_use;
206 self.team_saved = self.team;
210 if(g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
212 have_team_spawns = 1;
214 if(cvar("r_showbboxes"))
216 // show where spawnpoints point at too
217 makevectors(self.angles);
220 e.classname = "info_player_foo";
221 setorigin(e, self.origin + v_forward * 24);
222 setsize(e, '-8 -8 -8', '8 8 8');
223 e.solid = SOLID_TRIGGER;
227 #define strstr strstrofs
229 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
230 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
231 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
232 // BE CONSTANT OR strzoneD!
233 float strstr(string haystack, string needle, float offset)
237 len = strlen(needle);
238 endpos = strlen(haystack) - len;
239 while(offset <= endpos)
241 found = substring(haystack, offset, len);
250 float NUM_NEAREST_ENTITIES = 4;
251 entity nearest_entity[NUM_NEAREST_ENTITIES];
252 float nearest_length[NUM_NEAREST_ENTITIES];
253 entity findnearest(vector point, .string field, string value, vector axismod)
264 localhead = find(world, field, value);
267 if((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
268 dist = localhead.oldorigin;
270 dist = localhead.origin;
272 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
275 for(i = 0; i < num_nearest; ++i)
277 if(len < nearest_length[i])
281 // now i tells us where to insert at
282 // INSERTION SORT! YOU'VE SEEN IT! RUN!
283 if(i < NUM_NEAREST_ENTITIES)
285 for(j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
287 nearest_length[j + 1] = nearest_length[j];
288 nearest_entity[j + 1] = nearest_entity[j];
290 nearest_length[i] = len;
291 nearest_entity[i] = localhead;
292 if(num_nearest < NUM_NEAREST_ENTITIES)
293 num_nearest = num_nearest + 1;
296 localhead = find(localhead, field, value);
299 // now use the first one from our list that we can see
300 for(i = 0; i < num_nearest; ++i)
302 traceline(point, nearest_entity[i].origin, TRUE, world);
303 if(trace_fraction == 1)
307 dprint("Nearest point (");
308 dprint(nearest_entity[0].netname);
309 dprint(") is not visible, using a visible one.\n");
311 return nearest_entity[i];
318 dprint("Not seeing any location point, using nearest as fallback.\n");
320 dprint("Candidates were: ");
321 for(j = 0; j < num_nearest; ++j)
325 dprint(nearest_entity[j].netname);
330 return nearest_entity[0];
333 void spawnfunc_target_location()
335 self.classname = "target_location";
336 // location name in netname
337 // eventually support: count, teamgame selectors, line of sight?
340 void spawnfunc_info_location()
342 self.classname = "target_location";
343 self.message = self.netname;
346 string NearestLocation(vector p)
351 loc = findnearest(p, classname, "target_location", '1 1 1');
358 loc = findnearest(p, target, "###item###", '1 1 4');
365 string formatmessage(string msg)
376 break; // too many replacements
378 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
379 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
389 replacement = substring(msg, p, 2);
390 escape = substring(msg, p + 1, 1);
393 else if(escape == "\\")
395 else if(escape == "n")
397 else if(escape == "a")
398 replacement = ftos(floor(self.armorvalue));
399 else if(escape == "h")
400 replacement = ftos(floor(self.health));
401 else if(escape == "l")
402 replacement = NearestLocation(self.origin);
403 else if(escape == "y")
404 replacement = NearestLocation(self.cursor_trace_endpos);
405 else if(escape == "d")
406 replacement = NearestLocation(self.death_origin);
407 else if(escape == "w")
412 wep = self.switchweapon;
415 replacement = W_Name(wep);
417 else if(escape == "W")
419 if(self.items & IT_SHELLS) replacement = "shells";
420 else if(self.items & IT_NAILS) replacement = "bullets";
421 else if(self.items & IT_ROCKETS) replacement = "rockets";
422 else if(self.items & IT_CELLS) replacement = "cells";
423 else replacement = "batteries"; // ;)
425 else if(escape == "x")
427 replacement = self.cursor_trace_ent.netname;
428 if(!replacement || !self.cursor_trace_ent)
429 replacement = "nothing";
431 else if(escape == "p")
433 if(self.last_selected_player)
434 replacement = self.last_selected_player.netname;
436 replacement = "(nobody)";
438 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
439 p = p + strlen(replacement);
450 >0: receives a cvar from name=argv(f) value=argv(f+1)
452 void GetCvars_handleString(string thisname, float f, .string field, string name)
457 strunzone(self.field);
458 self.field = string_null;
465 strunzone(self.field);
466 self.field = strzone(argv(f + 1));
470 stuffcmd(self, strcat("sendcvar ", name, "\n"));
472 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
474 GetCvars_handleString(thisname, f, field, name);
475 if(f >= 0) // also initialize to the fitting value for "" when sending cvars out
479 s = func(strcat1(self.field));
482 strunzone(self.field);
483 self.field = strzone(s);
487 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
495 self.field = stof(argv(f + 1));
498 stuffcmd(self, strcat("sendcvar ", name, "\n"));
500 string W_FixWeaponOrder_ForceComplete(string s);
501 string W_FixWeaponOrder_AllowIncomplete(string s);
502 float w_getbestweapon(entity e);
503 void GetCvars(float f)
507 s = strcat1(argv(f));
508 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
509 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
510 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
511 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
512 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
513 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
514 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
515 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
516 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
517 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
518 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
519 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
520 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
521 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
522 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
523 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
524 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
525 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
526 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
527 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
528 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
530 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
533 if(s == "cl_weaponpriority")
534 self.switchweapon = w_getbestweapon(self);
538 float fexists(string f)
541 fh = fopen(f, FILE_READ);
548 void backtrace(string msg)
551 dev = cvar("developer");
552 cvar_set("developer", "1");
554 dprint("--- CUT HERE ---\nWARNING: ");
557 remove(world); // isn't there any better way to cause a backtrace?
558 dprint("\n--- CUT UNTIL HERE ---\n");
559 cvar_set("developer", ftos(dev));
562 string Team_ColorCode(float teamid)
564 if(teamid == COLOR_TEAM1)
566 else if(teamid == COLOR_TEAM2)
568 else if(teamid == COLOR_TEAM3)
570 else if(teamid == COLOR_TEAM4)
575 string Team_ColorName(float t)
577 // fixme: Search for team entities and get their .netname's!
588 string Team_ColorNameLowerCase(float t)
590 // fixme: Search for team entities and get their .netname's!
602 #define CENTERPRIO_POINT 1
603 #define CENTERPRIO_SPAM 2
604 #define CENTERPRIO_VOTE 4
605 #define CENTERPRIO_NORMAL 5
606 #define CENTERPRIO_SHIELDING 7
607 #define CENTERPRIO_MAPVOTE 9
608 #define CENTERPRIO_IDLEKICK 50
609 #define CENTERPRIO_ADMIN 99
610 .float centerprint_priority;
611 .float centerprint_expires;
612 void centerprint_atprio(entity e, float prio, string s)
614 if(intermission_running)
615 if(prio < CENTERPRIO_MAPVOTE)
617 if(time > e.centerprint_expires)
618 e.centerprint_priority = 0;
619 if(prio >= e.centerprint_priority)
621 e.centerprint_priority = prio;
622 if(timeoutStatus == 2)
623 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
625 e.centerprint_expires = time + e.cvar_scr_centertime;
626 centerprint_builtin(e, s);
629 void centerprint_expire(entity e, float prio)
631 if(prio == e.centerprint_priority)
633 e.centerprint_priority = 0;
634 centerprint_builtin(e, "");
637 void centerprint(entity e, string s)
639 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
642 // decolorizes and team colors the player name when needed
643 string playername(entity p)
646 if(teams_matter && !intermission_running && p.classname == "player")
648 t = Team_ColorCode(p.team);
649 return strcat(t, strdecolorize(p.netname));
655 vector randompos(vector m1, vector m2)
659 v_x = m2_x * random() + m1_x;
660 v_y = m2_y * random() + m1_y;
661 v_z = m2_z * random() + m1_z;
665 float g_pickup_shells;
666 float g_pickup_shells_max;
667 float g_pickup_nails;
668 float g_pickup_nails_max;
669 float g_pickup_rockets;
670 float g_pickup_rockets_max;
671 float g_pickup_cells;
672 float g_pickup_cells_max;
674 float g_pickup_fuel_jetpack;
675 float g_pickup_fuel_max;
676 float g_pickup_armorsmall;
677 float g_pickup_armorsmall_max;
678 float g_pickup_armormedium;
679 float g_pickup_armormedium_max;
680 float g_pickup_armorbig;
681 float g_pickup_armorbig_max;
682 float g_pickup_armorlarge;
683 float g_pickup_armorlarge_max;
684 float g_pickup_healthsmall;
685 float g_pickup_healthsmall_max;
686 float g_pickup_healthmedium;
687 float g_pickup_healthmedium_max;
688 float g_pickup_healthlarge;
689 float g_pickup_healthlarge_max;
690 float g_pickup_healthmega;
691 float g_pickup_healthmega_max;
693 string g_weaponarena_list;
694 float g_weaponspeedfactor;
695 float g_weapondamagefactor;
699 float start_ammo_shells;
700 float start_ammo_nails;
701 float start_ammo_rockets;
702 float start_ammo_cells;
703 float start_ammo_fuel;
705 float start_armorvalue;
706 float warmup_start_weapons;
707 float warmup_start_ammo_shells;
708 float warmup_start_ammo_nails;
709 float warmup_start_ammo_rockets;
710 float warmup_start_ammo_cells;
711 float warmup_start_ammo_fuel;
712 float warmup_start_health;
713 float warmup_start_armorvalue;
716 entity get_weaponinfo(float w);
718 float NixNex_CanChooseWeapon(float wpn);
719 void readplayerstartcvars()
725 // initialize starting values for players
728 start_ammo_shells = 0;
729 start_ammo_nails = 0;
730 start_ammo_rockets = 0;
731 start_ammo_cells = 0;
732 start_health = cvar("g_balance_health_start");
733 start_armorvalue = cvar("g_balance_armor_start");
736 s = cvar_string("g_weaponarena");
742 g_weaponarena_list = "All Weapons";
743 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
745 e = get_weaponinfo(j);
746 g_weaponarena |= e.weapons;
747 weapon_action(e.weapon, WR_PRECACHE);
752 g_weaponarena_list = "Most Weapons";
753 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
755 e = get_weaponinfo(j);
756 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
758 g_weaponarena |= e.weapons;
759 weapon_action(e.weapon, WR_PRECACHE);
765 g_weaponarena_list = "No Weapons";
766 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
770 t = tokenize_console(s);
771 g_weaponarena_list = "";
772 for(i = 0; i < t; ++i)
775 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
777 e = get_weaponinfo(j);
780 g_weaponarena |= e.weapons;
781 weapon_action(e.weapon, WR_PRECACHE);
782 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
788 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
791 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
797 // will be done later
798 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
799 if(NixNex_CanChooseWeapon(i))
800 weapon_action(i, WR_PRECACHE);
802 else if(g_weaponarena)
804 start_weapons = g_weaponarena;
805 start_ammo_rockets = 999;
806 start_ammo_shells = 999;
807 start_ammo_cells = 999;
808 start_ammo_nails = 999;
809 start_ammo_fuel = 999;
810 start_items |= IT_UNLIMITED_AMMO;
815 start_armorvalue = 0;
816 start_weapons = WEPBIT_MINSTANEX;
817 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
818 start_ammo_cells = cvar("g_minstagib_ammo_start");
819 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
820 start_ammo_fuel = cvar("g_start_ammo_fuel");
822 if(g_minstagib_invis_alpha <= 0)
823 g_minstagib_invis_alpha = -1;
829 start_ammo_shells = cvar("g_lms_start_ammo_shells");
830 start_ammo_nails = cvar("g_lms_start_ammo_nails");
831 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
832 start_ammo_cells = cvar("g_lms_start_ammo_cells");
833 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
834 start_health = cvar("g_lms_start_health");
835 start_armorvalue = cvar("g_lms_start_armor");
836 } else if (cvar("g_use_ammunition")) {
837 start_ammo_shells = cvar("g_start_ammo_shells");
838 start_ammo_nails = cvar("g_start_ammo_nails");
839 start_ammo_rockets = cvar("g_start_ammo_rockets");
840 start_ammo_cells = cvar("g_start_ammo_cells");
841 start_ammo_fuel = cvar("g_start_ammo_fuel");
843 start_ammo_shells = cvar("g_pickup_shells_max");
844 start_ammo_nails = cvar("g_pickup_nails_max");
845 start_ammo_rockets = cvar("g_pickup_rockets_max");
846 start_ammo_cells = cvar("g_pickup_cells_max");
847 start_ammo_fuel = cvar("g_pickup_fuel_max");
848 start_items |= IT_UNLIMITED_AMMO;
851 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
853 e = get_weaponinfo(i);
857 t = cvar(strcat("g_start_weapon_", e.netname));
859 if(t < 0) // "default" weapon selection
862 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
864 t = (i == WEP_LASER);
866 t = 0; // weapon is set a few lines later
868 t = (i == WEP_LASER || i == WEP_SHOTGUN);
869 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
870 t += (i == WEP_HOOK);
873 if(g_nexball && i == WEP_PORTO)
878 start_weapons |= e.weapons;
879 weapon_action(e.weapon, WR_PRECACHE);
886 warmup_start_ammo_shells = start_ammo_shells;
887 warmup_start_ammo_nails = start_ammo_nails;
888 warmup_start_ammo_rockets = start_ammo_rockets;
889 warmup_start_ammo_cells = start_ammo_cells;
890 warmup_start_health = start_health;
891 warmup_start_armorvalue = start_armorvalue;
892 warmup_start_weapons = start_weapons;
894 if(!g_weaponarena && !g_nixnex && !g_minstagib)
896 if(cvar("g_use_ammunition"))
898 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
899 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
900 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
901 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
903 warmup_start_health = cvar("g_warmup_start_health");
904 warmup_start_armorvalue = cvar("g_warmup_start_armor");
905 if(cvar("g_warmup_allguns"))
907 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
909 e = get_weaponinfo(i);
912 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
914 warmup_start_weapons |= e.weapons;
915 weapon_action(e.weapon, WR_PRECACHE);
922 if(g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
924 g_grappling_hook = 0; // these two can't coexist, as they use the same button
925 start_items |= IT_FUEL_REGEN;
926 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
930 start_items |= IT_JETPACK;
932 if(g_weapon_stay == 2)
934 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
935 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
936 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
937 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
938 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
939 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
940 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
941 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
942 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
943 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
946 start_ammo_shells = max(0, start_ammo_shells);
947 start_ammo_nails = max(0, start_ammo_nails);
948 start_ammo_cells = max(0, start_ammo_cells);
949 start_ammo_rockets = max(0, start_ammo_rockets);
950 start_ammo_fuel = max(0, start_ammo_fuel);
952 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
953 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
954 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
955 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
956 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
960 float g_bugrigs_planar_movement;
961 float g_bugrigs_planar_movement_car_jumping;
962 float g_bugrigs_reverse_spinning;
963 float g_bugrigs_reverse_speeding;
964 float g_bugrigs_reverse_stopping;
965 float g_bugrigs_air_steering;
966 float g_bugrigs_angle_smoothing;
967 float g_bugrigs_friction_floor;
968 float g_bugrigs_friction_brake;
969 float g_bugrigs_friction_air;
970 float g_bugrigs_accel;
971 float g_bugrigs_speed_ref;
972 float g_bugrigs_speed_pow;
973 float g_bugrigs_steer;
975 float g_touchexplode;
976 float g_touchexplode_radius;
977 float g_touchexplode_damage;
978 float g_touchexplode_edgedamage;
979 float g_touchexplode_force;
981 void readlevelcvars(void)
983 g_bugrigs = cvar("g_bugrigs");
984 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
985 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
986 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
987 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
988 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
989 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
990 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
991 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
992 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
993 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
994 g_bugrigs_accel = cvar("g_bugrigs_accel");
995 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
996 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
997 g_bugrigs_steer = cvar("g_bugrigs_steer");
999 g_touchexplode = cvar("g_touchexplode");
1000 g_touchexplode_radius = cvar("g_touchexplode_radius");
1001 g_touchexplode_damage = cvar("g_touchexplode_damage");
1002 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1003 g_touchexplode_force = cvar("g_touchexplode_force");
1005 sv_clones = cvar("sv_clones");
1006 sv_cheats = cvar("sv_cheats");
1007 sv_gentle = cvar("sv_gentle");
1008 sv_foginterval = cvar("sv_foginterval");
1009 g_cloaked = cvar("g_cloaked");
1010 g_jump_grunt = cvar("g_jump_grunt");
1011 g_footsteps = cvar("g_footsteps");
1012 g_grappling_hook = cvar("g_grappling_hook");
1013 g_jetpack = cvar("g_jetpack");
1014 g_laserguided_missile = cvar("g_laserguided_missile");
1015 g_midair = cvar("g_midair");
1016 g_minstagib = cvar("g_minstagib");
1017 g_nixnex = cvar("g_nixnex");
1018 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1019 g_norecoil = cvar("g_norecoil");
1020 g_vampire = cvar("g_vampire");
1021 g_bloodloss = cvar("g_bloodloss");
1022 sv_maxidle = cvar("sv_maxidle");
1023 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1024 sv_pogostick = cvar("sv_pogostick");
1025 sv_doublejump = cvar("sv_doublejump");
1026 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1027 g_ctf_reverse = cvar("g_ctf_reverse");
1029 inWarmupStage = cvar("g_warmup");
1030 g_warmup_limit = cvar("g_warmup_limit");
1031 g_warmup_allguns = cvar("g_warmup_allguns");
1032 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1034 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1035 inWarmupStage = 0; // these modes cannot work together, sorry
1037 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1038 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1039 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1040 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1041 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1042 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1043 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1044 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1045 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1046 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1047 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1048 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1050 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1051 if(g_nixnex) g_weaponarena = 0;
1054 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1055 g_weapondamagefactor = cvar("g_weapondamagefactor");
1057 g_pickup_shells = cvar("g_pickup_shells");
1058 g_pickup_shells_max = cvar("g_pickup_shells_max");
1059 g_pickup_nails = cvar("g_pickup_nails");
1060 g_pickup_nails_max = cvar("g_pickup_nails_max");
1061 g_pickup_rockets = cvar("g_pickup_rockets");
1062 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1063 g_pickup_cells = cvar("g_pickup_cells");
1064 g_pickup_cells_max = cvar("g_pickup_cells_max");
1065 g_pickup_fuel = cvar("g_pickup_fuel");
1066 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1067 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1068 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1069 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1070 g_pickup_armormedium = cvar("g_pickup_armormedium");
1071 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1072 g_pickup_armorbig = cvar("g_pickup_armorbig");
1073 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1074 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1075 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1076 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1077 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1078 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1079 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1080 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1081 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1082 g_pickup_healthmega = cvar("g_pickup_healthmega");
1083 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1085 g_pinata = cvar("g_pinata");
1087 g_weapon_stay = cvar("g_weapon_stay");
1088 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1091 if not(inWarmupStage)
1092 game_starttime = cvar("g_start_delay");
1094 readplayerstartcvars();
1098 // TODO sound pack system
1101 string precache_sound_builtin (string s) = #19;
1102 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1103 string precache_sound(string s)
1105 return precache_sound_builtin(strcat(soundpack, s));
1107 void play2(entity e, string filename)
1109 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1111 void sound(entity e, float chan, string samp, float vol, float atten)
1113 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1118 string precache_sound (string s) = #19;
1119 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1120 float precache_sound_index (string s) = #19;
1122 #define SND_VOLUME 1
1123 #define SND_ATTENUATION 2
1124 #define SND_LARGEENTITY 8
1125 #define SND_LARGESOUND 16
1127 float sound_allowed(float dest, entity e)
1129 // sounds from world may always pass
1134 else if(e.classname == "body")
1139 // sounds to self may always pass
1143 // sounds by players can be removed
1144 if(cvar("bot_sound_monopoly"))
1145 if(clienttype(e) == CLIENTTYPE_REAL)
1147 // anything else may pass
1151 void sound(entity e, float chan, string samp, float vol, float atten)
1153 if(!sound_allowed(MSG_BROADCAST, e))
1155 sound_builtin(e, chan, samp, vol, atten);
1157 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1161 if(!sound_allowed(dest, e))
1164 entno = num_for_edict(e);
1165 idx = precache_sound_index(samp);
1170 atten = floor(atten * 64);
1171 vol = floor(vol * 255);
1174 sflags |= SND_VOLUME;
1176 sflags |= SND_ATTENUATION;
1178 sflags |= SND_LARGEENTITY;
1180 sflags |= SND_LARGESOUND;
1182 WriteByte(dest, SVC_SOUND);
1183 WriteByte(dest, sflags);
1184 if(sflags & SND_VOLUME)
1185 WriteByte(dest, vol);
1186 if(sflags & SND_ATTENUATION)
1187 WriteByte(dest, atten);
1188 if(sflags & SND_LARGEENTITY)
1190 WriteShort(dest, entno);
1191 WriteByte(dest, chan);
1195 WriteShort(dest, entno * 8 + chan);
1197 if(sflags & SND_LARGESOUND)
1198 WriteShort(dest, idx);
1200 WriteByte(dest, idx);
1202 WriteCoord(dest, o_x);
1203 WriteCoord(dest, o_y);
1204 WriteCoord(dest, o_z);
1206 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1210 if(!sound_allowed(dest, e))
1213 o = e.origin + 0.5 * (e.mins + e.maxs);
1214 soundtoat(dest, e, o, chan, samp, vol, atten);
1216 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1218 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1220 void stopsoundto(float dest, entity e, float chan)
1224 if(!sound_allowed(dest, e))
1227 entno = num_for_edict(e);
1232 idx = precache_sound_index("misc/null.wav");
1233 sflags = SND_LARGEENTITY;
1235 sflags |= SND_LARGESOUND;
1236 WriteByte(dest, SVC_SOUND);
1237 WriteByte(dest, sflags);
1238 WriteShort(dest, entno);
1239 WriteByte(dest, chan);
1240 if(sflags & SND_LARGESOUND)
1241 WriteShort(dest, idx);
1243 WriteByte(dest, idx);
1244 WriteCoord(dest, e.origin_x);
1245 WriteCoord(dest, e.origin_y);
1246 WriteCoord(dest, e.origin_z);
1250 WriteByte(dest, SVC_STOPSOUND);
1251 WriteShort(dest, entno * 8 + chan);
1254 void stopsound(entity e, float chan)
1256 if(!sound_allowed(MSG_BROADCAST, e))
1259 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1260 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1263 void play2(entity e, string filename)
1265 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1267 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1270 .float announcetime;
1271 float announce(entity player, string msg)
1273 if(time > player.announcetime)
1274 if(clienttype(player) == CLIENTTYPE_REAL)
1276 player.announcetime = time + 0.8;
1282 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1283 float spamsound(entity e, float chan, string samp, float vol, float atten)
1285 if(!sound_allowed(MSG_BROADCAST, e))
1288 if(time > e.announcetime)
1290 e.announcetime = time;
1291 sound(e, chan, samp, vol, atten);
1297 void play2team(float t, string filename)
1301 if(cvar("bot_sound_monopoly"))
1304 FOR_EACH_REALPLAYER(head)
1307 play2(head, filename);
1311 void play2all(string samp)
1313 if(cvar("bot_sound_monopoly"))
1316 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1319 void PrecachePlayerSounds(string f);
1320 void precache_all_models(string pattern)
1322 float globhandle, i, n;
1325 globhandle = search_begin(pattern, TRUE, FALSE);
1328 n = search_getsize(globhandle);
1329 for(i = 0; i < n; ++i)
1331 //print(search_getfilename(globhandle, i), "\n");
1332 f = search_getfilename(globhandle, i);
1334 PrecachePlayerSounds(strcat(f, ".sounds"));
1336 search_end(globhandle);
1341 // gamemode related things
1342 precache_model ("models/misc/chatbubble.spr");
1343 precache_model ("models/misc/teambubble.spr");
1346 precache_model ("models/runematch/curse.mdl");
1347 precache_model ("models/runematch/rune.mdl");
1350 #ifdef TTURRETS_ENABLED
1351 if(cvar("g_turrets"))
1355 // Precache all player models if desired
1356 if (cvar("sv_precacheplayermodels"))
1358 PrecachePlayerSounds("sound/player/default.sounds");
1359 precache_all_models("models/player/*.zym");
1360 precache_all_models("models/player/*.dpm");
1361 precache_all_models("models/player/*.md3");
1362 precache_all_models("models/player/*.psk");
1363 //precache_model("models/player/carni.zym");
1364 //precache_model("models/player/crash.zym");
1365 //precache_model("models/player/grunt.zym");
1366 //precache_model("models/player/headhunter.zym");
1367 //precache_model("models/player/insurrectionist.zym");
1368 //precache_model("models/player/jeandarc.zym");
1369 //precache_model("models/player/lurk.zym");
1370 //precache_model("models/player/lycanthrope.zym");
1371 //precache_model("models/player/marine.zym");
1372 //precache_model("models/player/nexus.zym");
1373 //precache_model("models/player/pyria.zym");
1374 //precache_model("models/player/shock.zym");
1375 //precache_model("models/player/skadi.zym");
1376 //precache_model("models/player/specop.zym");
1377 //precache_model("models/player/visitant.zym");
1380 if(cvar("sv_defaultcharacter"))
1383 s = cvar_string("sv_defaultplayermodel_red");
1387 PrecachePlayerSounds(strcat(s, ".sounds"));
1389 s = cvar_string("sv_defaultplayermodel_blue");
1393 PrecachePlayerSounds(strcat(s, ".sounds"));
1395 s = cvar_string("sv_defaultplayermodel_yellow");
1399 PrecachePlayerSounds(strcat(s, ".sounds"));
1401 s = cvar_string("sv_defaultplayermodel_pink");
1405 PrecachePlayerSounds(strcat(s, ".sounds"));
1407 s = cvar_string("sv_defaultplayermodel");
1411 PrecachePlayerSounds(strcat(s, ".sounds"));
1417 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1418 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1421 // gore and miscellaneous sounds
1422 //precache_sound ("misc/h2ohit.wav");
1423 precache_model ("models/hook.md3");
1424 precache_sound ("misc/armorimpact.wav");
1425 precache_sound ("misc/bodyimpact1.wav");
1426 precache_sound ("misc/bodyimpact2.wav");
1427 precache_sound ("misc/gib.wav");
1428 precache_sound ("misc/gib_splat01.wav");
1429 precache_sound ("misc/gib_splat02.wav");
1430 precache_sound ("misc/gib_splat03.wav");
1431 precache_sound ("misc/gib_splat04.wav");
1432 precache_sound ("misc/hit.wav");
1433 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1434 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1435 precache_sound ("misc/null.wav");
1436 precache_sound ("misc/spawn.wav");
1437 precache_sound ("misc/talk.wav");
1438 precache_sound ("misc/teleport.wav");
1439 precache_sound ("misc/poweroff.wav");
1440 precache_sound ("player/lava.wav");
1441 precache_sound ("player/slime.wav");
1444 precache_sound ("misc/jetpack_fly.wav");
1446 // announcer sounds - male
1447 precache_sound ("announcer/male/electrobitch.wav");
1448 precache_sound ("announcer/male/airshot.wav");
1449 precache_sound ("announcer/male/03kills.wav");
1450 precache_sound ("announcer/male/05kills.wav");
1451 precache_sound ("announcer/male/10kills.wav");
1452 precache_sound ("announcer/male/15kills.wav");
1453 precache_sound ("announcer/male/20kills.wav");
1454 precache_sound ("announcer/male/25kills.wav");
1455 precache_sound ("announcer/male/30kills.wav");
1456 precache_sound ("announcer/male/botlike.wav");
1457 precache_sound ("announcer/male/yoda.wav");
1458 precache_sound ("announcer/male/amazing.wav");
1459 precache_sound ("announcer/male/awesome.wav");
1460 precache_sound ("announcer/male/headshot.wav");
1461 precache_sound ("announcer/male/impressive.wav");
1463 // announcer sounds - robotic
1464 precache_sound ("announcer/robotic/prepareforbattle.wav");
1465 precache_sound ("announcer/robotic/begin.wav");
1466 precache_sound ("announcer/robotic/timeoutcalled.wav");
1467 precache_sound ("announcer/robotic/1fragleft.wav");
1468 precache_sound ("announcer/robotic/2fragsleft.wav");
1469 precache_sound ("announcer/robotic/3fragsleft.wav");
1472 precache_sound ("announcer/robotic/lastsecond.wav");
1473 precache_sound ("announcer/robotic/narrowly.wav");
1476 precache_model ("models/sprites/1.spr32");
1477 precache_model ("models/sprites/2.spr32");
1478 precache_model ("models/sprites/3.spr32");
1479 precache_model ("models/sprites/4.spr32");
1480 precache_model ("models/sprites/5.spr32");
1481 precache_model ("models/sprites/6.spr32");
1482 precache_model ("models/sprites/7.spr32");
1483 precache_model ("models/sprites/8.spr32");
1484 precache_model ("models/sprites/9.spr32");
1485 precache_model ("models/sprites/10.spr32");
1486 precache_sound ("announcer/robotic/1.wav");
1487 precache_sound ("announcer/robotic/2.wav");
1488 precache_sound ("announcer/robotic/3.wav");
1489 precache_sound ("announcer/robotic/4.wav");
1490 precache_sound ("announcer/robotic/5.wav");
1491 precache_sound ("announcer/robotic/6.wav");
1492 precache_sound ("announcer/robotic/7.wav");
1493 precache_sound ("announcer/robotic/8.wav");
1494 precache_sound ("announcer/robotic/9.wav");
1495 precache_sound ("announcer/robotic/10.wav");
1497 // common weapon precaches
1498 precache_sound ("weapons/weapon_switch.wav");
1499 precache_sound ("weapons/weaponpickup.wav");
1500 precache_sound ("weapons/unavailable.wav");
1501 if(g_grappling_hook)
1503 precache_sound ("weapons/hook_fire.wav"); // hook
1504 precache_sound ("weapons/hook_impact.wav"); // hook
1507 if (cvar("sv_precacheweapons") || g_nixnex)
1509 //precache weapon models/sounds
1512 while (wep <= WEP_LAST)
1514 weapon_action(wep, WR_PRECACHE);
1519 precache_model("models/elaser.mdl");
1520 precache_model("models/laser.mdl");
1521 precache_model("models/ebomb.mdl");
1524 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1526 if (!self.noise && self.music) // quake 3 uses the music field
1527 self.noise = self.music;
1529 // plays music for the level if there is any
1532 precache_sound (self.noise);
1533 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1538 // sorry, but using \ in macros breaks line numbers
1539 #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
1540 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1541 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1543 vector ExactTriggerHit_mins;
1544 vector ExactTriggerHit_maxs;
1545 float ExactTriggerHit_Recurse()
1551 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1554 if(trace_ent == self)
1559 se.solid = SOLID_NOT;
1560 f = ExactTriggerHit_Recurse();
1566 float ExactTriggerHit()
1570 if not(self.modelindex)
1574 self.solid = SOLID_BSP;
1575 ExactTriggerHit_mins = other.absmin;
1576 ExactTriggerHit_maxs = other.absmax;
1577 f = ExactTriggerHit_Recurse();
1583 // WARNING: this kills the trace globals
1584 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1585 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1587 #define INITPRIO_FIRST 0
1588 #define INITPRIO_GAMETYPE 0
1589 #define INITPRIO_GAMETYPE_FALLBACK 1
1590 #define INITPRIO_CVARS 5
1591 #define INITPRIO_FINDTARGET 10
1592 #define INITPRIO_DROPTOFLOOR 20
1593 #define INITPRIO_SETLOCATION 90
1594 #define INITPRIO_LINKDOORS 91
1595 #define INITPRIO_LAST 99
1597 .void(void) initialize_entity;
1598 .float initialize_entity_order;
1599 .entity initialize_entity_next;
1600 entity initialize_entity_first;
1602 void make_safe_for_remove(entity e)
1604 if(e.initialize_entity)
1607 for(ent = initialize_entity_first; ent; )
1609 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1611 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1612 // skip it in linked list
1615 prev.initialize_entity_next = ent.initialize_entity_next;
1616 ent = prev.initialize_entity_next;
1620 initialize_entity_first = ent.initialize_entity_next;
1621 ent = initialize_entity_first;
1627 ent = ent.initialize_entity_next;
1633 void objerror(string s)
1635 make_safe_for_remove(self);
1636 objerror_builtin(s);
1639 void remove_unsafely(entity e)
1644 void remove_safely(entity e)
1646 make_safe_for_remove(e);
1650 void InitializeEntity(entity e, void(void) func, float order)
1654 if(!e || e.initialize_entity)
1656 // make a proxy initializer entity
1660 e.classname = "initialize_entity";
1664 e.initialize_entity = func;
1665 e.initialize_entity_order = order;
1667 cur = initialize_entity_first;
1670 if(!cur || cur.initialize_entity_order > order)
1672 // insert between prev and cur
1674 prev.initialize_entity_next = e;
1676 initialize_entity_first = e;
1677 e.initialize_entity_next = cur;
1681 cur = cur.initialize_entity_next;
1684 void InitializeEntitiesRun()
1687 startoflist = initialize_entity_first;
1688 initialize_entity_first = world;
1689 for(self = startoflist; self; )
1692 var void(void) func;
1693 e = self.initialize_entity_next;
1694 func = self.initialize_entity;
1695 self.initialize_entity_order = 0;
1696 self.initialize_entity = func_null;
1697 self.initialize_entity_next = world;
1698 if(self.classname == "initialize_entity")
1702 remove_builtin(self);
1705 //dprint("Delayed initialization: ", self.classname, "\n");
1711 .float uncustomizeentityforclient_set;
1712 .void(void) uncustomizeentityforclient;
1713 void(void) SUB_Nullpointer = #0;
1714 void UncustomizeEntitiesRun()
1718 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1719 self.uncustomizeentityforclient();
1722 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1724 e.customizeentityforclient = customizer;
1725 e.uncustomizeentityforclient = uncustomizer;
1726 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1730 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1733 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1737 if(e.classname == "")
1738 e.classname = "net_linked";
1740 if(e.model == "" || self.modelindex == 0)
1744 setmodel(e, "null");
1748 e.SendEntity = sendfunc;
1749 e.SendFlags = 0xFFFFFF;
1752 e.effects |= EF_NODEPTHTEST;
1756 e.nextthink = time + dt;
1757 e.think = SUB_Remove;
1761 void adaptor_think2touch()
1770 void adaptor_think2use()
1782 // deferred dropping
1783 void DropToFloor_Handler()
1785 droptofloor_builtin();
1786 self.dropped_origin = self.origin;
1791 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1796 float trace_hits_box_a0, trace_hits_box_a1;
1798 float trace_hits_box_1d(float end, float thmi, float thma)
1802 // just check if x is in range
1810 // do the trace with respect to x
1811 // 0 -> end has to stay in thmi -> thma
1812 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1813 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1814 if(trace_hits_box_a0 > trace_hits_box_a1)
1820 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1825 // now it is a trace from 0 to end
1827 trace_hits_box_a0 = 0;
1828 trace_hits_box_a1 = 1;
1830 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1832 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1834 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1840 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1842 return trace_hits_box(start, end, thmi - ma, thma - mi);
1845 float SUB_NoImpactCheck()
1847 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1849 if(other == world && self.size != '0 0 0')
1852 tic = self.velocity * sys_ticrate;
1853 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1854 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1855 if(trace_fraction >= 1)
1857 dprint("Odd... did not hit...?\n");
1859 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1861 dprint("Detected and prevented the sky-grapple bug.\n");
1869 #define SUB_OwnerCheck() (other && (other == self.owner))
1871 #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)
1873 float MAX_IPBAN_URIS = 16;
1875 float URI_GET_DISCARD = 0;
1876 float URI_GET_IPBAN = 1;
1877 float URI_GET_IPBAN_END = 16;
1879 void URI_Get_Callback(float id, float status, string data)
1881 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1883 dprint("\nEnd of data.\n");
1885 if(id == URI_GET_DISCARD)
1889 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1892 OnlineBanList_URI_Get_Callback(id, status, data);
1896 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1900 void print_to(entity e, string s)
1903 sprint(e, strcat(s, "\n"));
1922 for(i = 0; i < MapInfo_count; ++i)
1924 if(MapInfo_Get_ByID(i))
1926 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1929 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1930 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1938 for(i = 0; i < MapInfo_count; ++i)
1940 if(MapInfo_Get_ByID(i))
1942 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1945 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1946 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1951 MapInfo_ClearTemps();
1954 return "No records are available on this server.\n";
1956 return strcat("Records on this server:\n", s);
1959 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1962 vector start, org, delta, end, enddown, mstart;
1964 m = e.dphitcontentsmask;
1965 e.dphitcontentsmask = goodcontents | badcontents;
1968 delta = world.maxs - world.mins;
1970 for(i = 0; i < attempts; ++i)
1972 start_x = org_x + random() * delta_x;
1973 start_y = org_y + random() * delta_y;
1974 start_z = org_z + random() * delta_z;
1976 // rule 1: start inside world bounds, and outside
1977 // solid, and don't start from somewhere where you can
1978 // fall down to evil
1979 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1980 if(trace_fraction >= 1)
1982 if(trace_startsolid)
1984 if(trace_dphitcontents & badcontents)
1986 if(trace_dphitq3surfaceflags & badsurfaceflags)
1989 // rule 2: if we are too high, lower the point
1990 if(trace_fraction * delta_z > maxaboveground)
1991 start = trace_endpos + '0 0 1' * maxaboveground;
1992 enddown = trace_endpos;
1994 // rule 3: make sure we aren't outside the map. This only works
1995 // for somewhat well formed maps. A good rule of thumb is that
1996 // the map should have a convex outside hull.
1997 // these can be traceLINES as we already verified the starting box
1998 mstart = start + 0.5 * (e.mins + e.maxs);
1999 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2000 if(trace_fraction >= 1)
2002 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2003 if(trace_fraction >= 1)
2005 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2006 if(trace_fraction >= 1)
2008 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2009 if(trace_fraction >= 1)
2011 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2012 if(trace_fraction >= 1)
2015 // find a random vector to "look at"
2016 end_x = org_x + random() * delta_x;
2017 end_y = org_y + random() * delta_y;
2018 end_z = org_z + random() * delta_z;
2019 end = start + normalize(end - start) * vlen(delta);
2021 // rule 4: start TO end must not be too short
2022 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2023 if(trace_startsolid)
2025 if(trace_fraction < minviewdistance / vlen(delta))
2028 // rule 5: don't want to look at sky
2029 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2032 // rule 6: we must not end up in trigger_hurt
2033 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2035 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2042 e.dphitcontentsmask = m;
2046 setorigin(e, start);
2047 e.angles = vectoangles(end - start);
2048 dprint("Needed ", ftos(i + 1), " attempts\n");
2055 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2057 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2058 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2059 WriteShort(MSG_BROADCAST, effectno);
2060 WriteCoord(MSG_BROADCAST, start_x);
2061 WriteCoord(MSG_BROADCAST, start_y);
2062 WriteCoord(MSG_BROADCAST, start_z);
2063 WriteCoord(MSG_BROADCAST, end_x);
2064 WriteCoord(MSG_BROADCAST, end_y);
2065 WriteCoord(MSG_BROADCAST, end_z);
2066 WriteCoord(MSG_BROADCAST, end_dz);
2067 WriteShort(MSG_BROADCAST, spd / 16);
2070 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2073 vector vecxy, velxy;
2075 vecxy = end - start; vecxy_z = 0;
2076 velxy = vel; velxy_z = 0;
2078 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2080 trailparticles(world, effectno, start, end);
2084 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2085 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2088 string GetGametype(); // g_world.qc
2089 void write_recordmarker(entity pl, float tstart, float dt)
2091 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2093 // also write a marker into demo files for demotc-race-record-extractor to find
2096 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2097 " ", ftos(tstart), " ", ftos(dt), "\n"));
2100 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2104 if (cvar("g_shootfromeye"))
2117 else if (cvar("g_shootfromcenter"))
2122 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2137 void attach_sameorigin(entity e, entity to, string tag)
2139 vector org, t_forward, t_left, t_up, e_forward, e_up;
2146 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2147 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2148 t_forward = v_forward * tagscale;
2149 t_left = v_right * -tagscale;
2150 t_up = v_up * tagscale;
2152 e.origin_x = org * t_forward;
2153 e.origin_y = org * t_left;
2154 e.origin_z = org * t_up;
2156 // current forward and up directions
2157 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2158 e.angles_x = -e.angles_x;
2159 fixedmakevectors(e.angles);
2161 // untransform forward, up!
2162 e_forward_x = v_forward * t_forward;
2163 e_forward_y = v_forward * t_left;
2164 e_forward_z = v_forward * t_up;
2165 e_up_x = v_up * t_forward;
2166 e_up_y = v_up * t_left;
2167 e_up_z = v_up * t_up;
2169 e.angles = fixedvectoangles2(e_forward, e_up);
2170 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2171 e.angles_x = -e.angles_x;
2173 setattachment(e, to, tag);
2174 setorigin(e, e.origin);
2177 void detach_sameorigin(entity e)
2180 org = gettaginfo(e, 0);
2181 e.angles = fixedvectoangles2(v_forward, v_up);
2182 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2183 e.angles_x = -e.angles_x;
2185 setattachment(e, world, "");
2186 setorigin(e, e.origin);
2189 void follow_sameorigin(entity e, entity to)
2191 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2192 e.aiment = to; // make the hole follow bmodel
2193 e.punchangle = to.angles; // the original angles of bmodel
2194 e.view_ofs = e.origin - to.origin; // relative origin
2195 e.v_angle = e.angles - to.angles; // relative angles
2198 void unfollow_sameorigin(entity e)
2200 e.movetype = MOVETYPE_NONE;
2203 entity gettaginfo_relative_ent;
2204 vector gettaginfo_relative(entity e, float tag)
2206 if(!gettaginfo_relative_ent)
2208 gettaginfo_relative_ent = spawn();
2209 gettaginfo_relative_ent.effects = EF_NODRAW;
2211 gettaginfo_relative_ent.model = e.model;
2212 gettaginfo_relative_ent.modelindex = e.modelindex;
2213 gettaginfo_relative_ent.frame = e.frame;
2214 return gettaginfo(gettaginfo_relative_ent, tag);
2217 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2221 if(pl.soundentity.cnt & p)
2223 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2224 pl.soundentity.cnt |= p;
2227 void SoundEntity_StopSound(entity pl, float chan)
2231 if(pl.soundentity.cnt & p)
2233 stopsoundto(MSG_ALL, pl.soundentity, chan);
2234 pl.soundentity.cnt &~= p;
2238 void SoundEntity_Attach(entity pl)
2240 pl.soundentity = spawn();
2241 pl.soundentity.classname = "soundentity";
2242 pl.soundentity.owner = pl;
2243 setattachment(pl.soundentity, pl, "");
2244 setmodel(pl.soundentity, "null");
2247 void SoundEntity_Detach(entity pl)
2250 for(i = 0; i <= 7; ++i)
2251 SoundEntity_StopSound(pl, i);
2255 float ParseCommandPlayerSlotTarget_firsttoken;
2256 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2264 ParseCommandPlayerSlotTarget_firsttoken = -1;
2268 if(substring(argv(idx), 0, 1) == "#")
2270 s = substring(argv(idx), 1, -1);
2278 if(s == ftos(stof(s)))
2280 e = edict_num(stof(s));
2281 if(e.flags & FL_CLIENT)
2283 ParseCommandPlayerSlotTarget_firsttoken = idx;
2290 // it must be a nick name
2295 FOR_EACH_CLIENT(head)
2296 if(head.netname == s)
2303 ParseCommandPlayerSlotTarget_firsttoken = idx;
2307 s = strdecolorize(s);
2309 FOR_EACH_CLIENT(head)
2310 if(strdecolorize(head.netname) == s)
2317 ParseCommandPlayerSlotTarget_firsttoken = idx;