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");
529 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
530 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
532 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
535 if(s == "cl_weaponpriority")
536 self.switchweapon = w_getbestweapon(self);
540 float fexists(string f)
543 fh = fopen(f, FILE_READ);
550 void backtrace(string msg)
553 dev = cvar("developer");
554 cvar_set("developer", "1");
556 dprint("--- CUT HERE ---\nWARNING: ");
559 remove(world); // isn't there any better way to cause a backtrace?
560 dprint("\n--- CUT UNTIL HERE ---\n");
561 cvar_set("developer", ftos(dev));
564 string Team_ColorCode(float teamid)
566 if(teamid == COLOR_TEAM1)
568 else if(teamid == COLOR_TEAM2)
570 else if(teamid == COLOR_TEAM3)
572 else if(teamid == COLOR_TEAM4)
577 string Team_ColorName(float t)
579 // fixme: Search for team entities and get their .netname's!
590 string Team_ColorNameLowerCase(float t)
592 // fixme: Search for team entities and get their .netname's!
604 #define CENTERPRIO_POINT 1
605 #define CENTERPRIO_SPAM 2
606 #define CENTERPRIO_VOTE 4
607 #define CENTERPRIO_NORMAL 5
608 #define CENTERPRIO_SHIELDING 7
609 #define CENTERPRIO_MAPVOTE 9
610 #define CENTERPRIO_IDLEKICK 50
611 #define CENTERPRIO_ADMIN 99
612 .float centerprint_priority;
613 .float centerprint_expires;
614 void centerprint_atprio(entity e, float prio, string s)
616 if(intermission_running)
617 if(prio < CENTERPRIO_MAPVOTE)
619 if(time > e.centerprint_expires)
620 e.centerprint_priority = 0;
621 if(prio >= e.centerprint_priority)
623 e.centerprint_priority = prio;
624 if(timeoutStatus == 2)
625 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
627 e.centerprint_expires = time + e.cvar_scr_centertime;
628 centerprint_builtin(e, s);
631 void centerprint_expire(entity e, float prio)
633 if(prio == e.centerprint_priority)
635 e.centerprint_priority = 0;
636 centerprint_builtin(e, "");
639 void centerprint(entity e, string s)
641 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
644 // decolorizes and team colors the player name when needed
645 string playername(entity p)
648 if(teams_matter && !intermission_running && p.classname == "player")
650 t = Team_ColorCode(p.team);
651 return strcat(t, strdecolorize(p.netname));
657 vector randompos(vector m1, vector m2)
661 v_x = m2_x * random() + m1_x;
662 v_y = m2_y * random() + m1_y;
663 v_z = m2_z * random() + m1_z;
667 float g_pickup_shells;
668 float g_pickup_shells_max;
669 float g_pickup_nails;
670 float g_pickup_nails_max;
671 float g_pickup_rockets;
672 float g_pickup_rockets_max;
673 float g_pickup_cells;
674 float g_pickup_cells_max;
676 float g_pickup_fuel_jetpack;
677 float g_pickup_fuel_max;
678 float g_pickup_armorsmall;
679 float g_pickup_armorsmall_max;
680 float g_pickup_armormedium;
681 float g_pickup_armormedium_max;
682 float g_pickup_armorbig;
683 float g_pickup_armorbig_max;
684 float g_pickup_armorlarge;
685 float g_pickup_armorlarge_max;
686 float g_pickup_healthsmall;
687 float g_pickup_healthsmall_max;
688 float g_pickup_healthmedium;
689 float g_pickup_healthmedium_max;
690 float g_pickup_healthlarge;
691 float g_pickup_healthlarge_max;
692 float g_pickup_healthmega;
693 float g_pickup_healthmega_max;
695 string g_weaponarena_list;
696 float g_weaponspeedfactor;
697 float g_weapondamagefactor;
701 float start_ammo_shells;
702 float start_ammo_nails;
703 float start_ammo_rockets;
704 float start_ammo_cells;
705 float start_ammo_fuel;
707 float start_armorvalue;
708 float warmup_start_weapons;
709 float warmup_start_ammo_shells;
710 float warmup_start_ammo_nails;
711 float warmup_start_ammo_rockets;
712 float warmup_start_ammo_cells;
713 float warmup_start_ammo_fuel;
714 float warmup_start_health;
715 float warmup_start_armorvalue;
718 entity get_weaponinfo(float w);
720 float NixNex_CanChooseWeapon(float wpn);
721 void readplayerstartcvars()
727 // initialize starting values for players
730 start_ammo_shells = 0;
731 start_ammo_nails = 0;
732 start_ammo_rockets = 0;
733 start_ammo_cells = 0;
734 start_health = cvar("g_balance_health_start");
735 start_armorvalue = cvar("g_balance_armor_start");
738 s = cvar_string("g_weaponarena");
744 g_weaponarena_list = "All Weapons";
745 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
747 e = get_weaponinfo(j);
748 g_weaponarena |= e.weapons;
749 weapon_action(e.weapon, WR_PRECACHE);
754 g_weaponarena_list = "Most Weapons";
755 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
757 e = get_weaponinfo(j);
758 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
760 g_weaponarena |= e.weapons;
761 weapon_action(e.weapon, WR_PRECACHE);
767 g_weaponarena_list = "No Weapons";
768 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
772 t = tokenize_console(s);
773 g_weaponarena_list = "";
774 for(i = 0; i < t; ++i)
777 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
779 e = get_weaponinfo(j);
782 g_weaponarena |= e.weapons;
783 weapon_action(e.weapon, WR_PRECACHE);
784 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
790 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
793 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
799 // will be done later
800 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
801 if(NixNex_CanChooseWeapon(i))
802 weapon_action(i, WR_PRECACHE);
804 else if(g_weaponarena)
806 start_weapons = g_weaponarena;
807 start_ammo_rockets = 999;
808 start_ammo_shells = 999;
809 start_ammo_cells = 999;
810 start_ammo_nails = 999;
811 start_ammo_fuel = 999;
812 start_items |= IT_UNLIMITED_AMMO;
817 start_armorvalue = 0;
818 start_weapons = WEPBIT_MINSTANEX;
819 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
820 start_ammo_cells = cvar("g_minstagib_ammo_start");
821 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
822 start_ammo_fuel = cvar("g_start_ammo_fuel");
824 if(g_minstagib_invis_alpha <= 0)
825 g_minstagib_invis_alpha = -1;
831 start_ammo_shells = cvar("g_lms_start_ammo_shells");
832 start_ammo_nails = cvar("g_lms_start_ammo_nails");
833 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
834 start_ammo_cells = cvar("g_lms_start_ammo_cells");
835 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
836 start_health = cvar("g_lms_start_health");
837 start_armorvalue = cvar("g_lms_start_armor");
838 } else if (cvar("g_use_ammunition")) {
839 start_ammo_shells = cvar("g_start_ammo_shells");
840 start_ammo_nails = cvar("g_start_ammo_nails");
841 start_ammo_rockets = cvar("g_start_ammo_rockets");
842 start_ammo_cells = cvar("g_start_ammo_cells");
843 start_ammo_fuel = cvar("g_start_ammo_fuel");
845 start_ammo_shells = cvar("g_pickup_shells_max");
846 start_ammo_nails = cvar("g_pickup_nails_max");
847 start_ammo_rockets = cvar("g_pickup_rockets_max");
848 start_ammo_cells = cvar("g_pickup_cells_max");
849 start_ammo_fuel = cvar("g_pickup_fuel_max");
850 start_items |= IT_UNLIMITED_AMMO;
853 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
855 e = get_weaponinfo(i);
859 t = cvar(strcat("g_start_weapon_", e.netname));
861 if(t < 0) // "default" weapon selection
864 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
866 t = (i == WEP_LASER);
868 t = 0; // weapon is set a few lines later
870 t = (i == WEP_LASER || i == WEP_SHOTGUN);
871 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
872 t += (i == WEP_HOOK);
875 if(g_nexball && i == WEP_PORTO)
880 start_weapons |= e.weapons;
881 weapon_action(e.weapon, WR_PRECACHE);
888 warmup_start_ammo_shells = start_ammo_shells;
889 warmup_start_ammo_nails = start_ammo_nails;
890 warmup_start_ammo_rockets = start_ammo_rockets;
891 warmup_start_ammo_cells = start_ammo_cells;
892 warmup_start_health = start_health;
893 warmup_start_armorvalue = start_armorvalue;
894 warmup_start_weapons = start_weapons;
896 if(!g_weaponarena && !g_nixnex && !g_minstagib)
898 if(cvar("g_use_ammunition"))
900 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
901 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
902 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
903 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
905 warmup_start_health = cvar("g_warmup_start_health");
906 warmup_start_armorvalue = cvar("g_warmup_start_armor");
907 if(cvar("g_warmup_allguns"))
909 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
911 e = get_weaponinfo(i);
914 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
916 warmup_start_weapons |= e.weapons;
917 weapon_action(e.weapon, WR_PRECACHE);
924 if(g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
926 g_grappling_hook = 0; // these two can't coexist, as they use the same button
927 start_items |= IT_FUEL_REGEN;
928 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
932 start_items |= IT_JETPACK;
934 if(g_weapon_stay == 2)
936 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
937 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
938 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
939 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
940 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
941 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
942 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
943 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
944 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
945 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
948 start_ammo_shells = max(0, start_ammo_shells);
949 start_ammo_nails = max(0, start_ammo_nails);
950 start_ammo_cells = max(0, start_ammo_cells);
951 start_ammo_rockets = max(0, start_ammo_rockets);
952 start_ammo_fuel = max(0, start_ammo_fuel);
954 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
955 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
956 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
957 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
958 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
962 float g_bugrigs_planar_movement;
963 float g_bugrigs_planar_movement_car_jumping;
964 float g_bugrigs_reverse_spinning;
965 float g_bugrigs_reverse_speeding;
966 float g_bugrigs_reverse_stopping;
967 float g_bugrigs_air_steering;
968 float g_bugrigs_angle_smoothing;
969 float g_bugrigs_friction_floor;
970 float g_bugrigs_friction_brake;
971 float g_bugrigs_friction_air;
972 float g_bugrigs_accel;
973 float g_bugrigs_speed_ref;
974 float g_bugrigs_speed_pow;
975 float g_bugrigs_steer;
977 float g_touchexplode;
978 float g_touchexplode_radius;
979 float g_touchexplode_damage;
980 float g_touchexplode_edgedamage;
981 float g_touchexplode_force;
983 void readlevelcvars(void)
985 g_bugrigs = cvar("g_bugrigs");
986 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
987 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
988 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
989 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
990 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
991 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
992 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
993 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
994 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
995 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
996 g_bugrigs_accel = cvar("g_bugrigs_accel");
997 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
998 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
999 g_bugrigs_steer = cvar("g_bugrigs_steer");
1001 g_touchexplode = cvar("g_touchexplode");
1002 g_touchexplode_radius = cvar("g_touchexplode_radius");
1003 g_touchexplode_damage = cvar("g_touchexplode_damage");
1004 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1005 g_touchexplode_force = cvar("g_touchexplode_force");
1007 sv_clones = cvar("sv_clones");
1008 sv_cheats = cvar("sv_cheats");
1009 sv_gentle = cvar("sv_gentle");
1010 sv_foginterval = cvar("sv_foginterval");
1011 g_cloaked = cvar("g_cloaked");
1012 g_jump_grunt = cvar("g_jump_grunt");
1013 g_footsteps = cvar("g_footsteps");
1014 g_grappling_hook = cvar("g_grappling_hook");
1015 g_jetpack = cvar("g_jetpack");
1016 g_laserguided_missile = cvar("g_laserguided_missile");
1017 g_midair = cvar("g_midair");
1018 g_minstagib = cvar("g_minstagib");
1019 g_nixnex = cvar("g_nixnex");
1020 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1021 g_norecoil = cvar("g_norecoil");
1022 g_vampire = cvar("g_vampire");
1023 g_bloodloss = cvar("g_bloodloss");
1024 sv_maxidle = cvar("sv_maxidle");
1025 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1026 sv_pogostick = cvar("sv_pogostick");
1027 sv_doublejump = cvar("sv_doublejump");
1028 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1029 g_ctf_reverse = cvar("g_ctf_reverse");
1031 inWarmupStage = cvar("g_warmup");
1032 g_warmup_limit = cvar("g_warmup_limit");
1033 g_warmup_allguns = cvar("g_warmup_allguns");
1034 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1036 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1037 inWarmupStage = 0; // these modes cannot work together, sorry
1039 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1040 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1041 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1042 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1043 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1044 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1045 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1046 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1047 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1048 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1049 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1050 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1052 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1053 if(g_nixnex) g_weaponarena = 0;
1056 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1057 g_weapondamagefactor = cvar("g_weapondamagefactor");
1059 g_pickup_shells = cvar("g_pickup_shells");
1060 g_pickup_shells_max = cvar("g_pickup_shells_max");
1061 g_pickup_nails = cvar("g_pickup_nails");
1062 g_pickup_nails_max = cvar("g_pickup_nails_max");
1063 g_pickup_rockets = cvar("g_pickup_rockets");
1064 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1065 g_pickup_cells = cvar("g_pickup_cells");
1066 g_pickup_cells_max = cvar("g_pickup_cells_max");
1067 g_pickup_fuel = cvar("g_pickup_fuel");
1068 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1069 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1070 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1071 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1072 g_pickup_armormedium = cvar("g_pickup_armormedium");
1073 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1074 g_pickup_armorbig = cvar("g_pickup_armorbig");
1075 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1076 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1077 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1078 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1079 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1080 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1081 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1082 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1083 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1084 g_pickup_healthmega = cvar("g_pickup_healthmega");
1085 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1087 g_pinata = cvar("g_pinata");
1089 g_weapon_stay = cvar("g_weapon_stay");
1090 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1093 if not(inWarmupStage)
1094 game_starttime = cvar("g_start_delay");
1096 readplayerstartcvars();
1100 // TODO sound pack system
1103 string precache_sound_builtin (string s) = #19;
1104 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1105 string precache_sound(string s)
1107 return precache_sound_builtin(strcat(soundpack, s));
1109 void play2(entity e, string filename)
1111 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1113 void sound(entity e, float chan, string samp, float vol, float atten)
1115 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1120 string precache_sound (string s) = #19;
1121 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1122 float precache_sound_index (string s) = #19;
1124 #define SND_VOLUME 1
1125 #define SND_ATTENUATION 2
1126 #define SND_LARGEENTITY 8
1127 #define SND_LARGESOUND 16
1129 float sound_allowed(float dest, entity e)
1131 // sounds from world may always pass
1136 else if(e.classname == "body")
1141 // sounds to self may always pass
1145 // sounds by players can be removed
1146 if(cvar("bot_sound_monopoly"))
1147 if(clienttype(e) == CLIENTTYPE_REAL)
1149 // anything else may pass
1153 void sound(entity e, float chan, string samp, float vol, float atten)
1155 if(!sound_allowed(MSG_BROADCAST, e))
1157 sound_builtin(e, chan, samp, vol, atten);
1159 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1163 if(!sound_allowed(dest, e))
1166 entno = num_for_edict(e);
1167 idx = precache_sound_index(samp);
1172 atten = floor(atten * 64);
1173 vol = floor(vol * 255);
1176 sflags |= SND_VOLUME;
1178 sflags |= SND_ATTENUATION;
1180 sflags |= SND_LARGEENTITY;
1182 sflags |= SND_LARGESOUND;
1184 WriteByte(dest, SVC_SOUND);
1185 WriteByte(dest, sflags);
1186 if(sflags & SND_VOLUME)
1187 WriteByte(dest, vol);
1188 if(sflags & SND_ATTENUATION)
1189 WriteByte(dest, atten);
1190 if(sflags & SND_LARGEENTITY)
1192 WriteShort(dest, entno);
1193 WriteByte(dest, chan);
1197 WriteShort(dest, entno * 8 + chan);
1199 if(sflags & SND_LARGESOUND)
1200 WriteShort(dest, idx);
1202 WriteByte(dest, idx);
1204 WriteCoord(dest, o_x);
1205 WriteCoord(dest, o_y);
1206 WriteCoord(dest, o_z);
1208 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1212 if(!sound_allowed(dest, e))
1215 o = e.origin + 0.5 * (e.mins + e.maxs);
1216 soundtoat(dest, e, o, chan, samp, vol, atten);
1218 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1220 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1222 void stopsoundto(float dest, entity e, float chan)
1226 if(!sound_allowed(dest, e))
1229 entno = num_for_edict(e);
1234 idx = precache_sound_index("misc/null.wav");
1235 sflags = SND_LARGEENTITY;
1237 sflags |= SND_LARGESOUND;
1238 WriteByte(dest, SVC_SOUND);
1239 WriteByte(dest, sflags);
1240 WriteShort(dest, entno);
1241 WriteByte(dest, chan);
1242 if(sflags & SND_LARGESOUND)
1243 WriteShort(dest, idx);
1245 WriteByte(dest, idx);
1246 WriteCoord(dest, e.origin_x);
1247 WriteCoord(dest, e.origin_y);
1248 WriteCoord(dest, e.origin_z);
1252 WriteByte(dest, SVC_STOPSOUND);
1253 WriteShort(dest, entno * 8 + chan);
1256 void stopsound(entity e, float chan)
1258 if(!sound_allowed(MSG_BROADCAST, e))
1261 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1262 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1265 void play2(entity e, string filename)
1267 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1269 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1272 .float announcetime;
1273 float announce(entity player, string msg)
1275 if(time > player.announcetime)
1276 if(clienttype(player) == CLIENTTYPE_REAL)
1278 player.announcetime = time + 0.8;
1284 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1285 float spamsound(entity e, float chan, string samp, float vol, float atten)
1287 if(!sound_allowed(MSG_BROADCAST, e))
1290 if(time > e.announcetime)
1292 e.announcetime = time;
1293 sound(e, chan, samp, vol, atten);
1299 void play2team(float t, string filename)
1303 if(cvar("bot_sound_monopoly"))
1306 FOR_EACH_REALPLAYER(head)
1309 play2(head, filename);
1313 void play2all(string samp)
1315 if(cvar("bot_sound_monopoly"))
1318 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1321 void PrecachePlayerSounds(string f);
1322 void precache_all_models(string pattern)
1324 float globhandle, i, n;
1327 globhandle = search_begin(pattern, TRUE, FALSE);
1330 n = search_getsize(globhandle);
1331 for(i = 0; i < n; ++i)
1333 //print(search_getfilename(globhandle, i), "\n");
1334 f = search_getfilename(globhandle, i);
1336 PrecachePlayerSounds(strcat(f, ".sounds"));
1338 search_end(globhandle);
1343 // gamemode related things
1344 precache_model ("models/misc/chatbubble.spr");
1345 precache_model ("models/misc/teambubble.spr");
1348 precache_model ("models/runematch/curse.mdl");
1349 precache_model ("models/runematch/rune.mdl");
1352 #ifdef TTURRETS_ENABLED
1353 if(cvar("g_turrets"))
1357 // Precache all player models if desired
1358 if (cvar("sv_precacheplayermodels"))
1360 PrecachePlayerSounds("sound/player/default.sounds");
1361 precache_all_models("models/player/*.zym");
1362 precache_all_models("models/player/*.dpm");
1363 precache_all_models("models/player/*.md3");
1364 precache_all_models("models/player/*.psk");
1365 //precache_model("models/player/carni.zym");
1366 //precache_model("models/player/crash.zym");
1367 //precache_model("models/player/grunt.zym");
1368 //precache_model("models/player/headhunter.zym");
1369 //precache_model("models/player/insurrectionist.zym");
1370 //precache_model("models/player/jeandarc.zym");
1371 //precache_model("models/player/lurk.zym");
1372 //precache_model("models/player/lycanthrope.zym");
1373 //precache_model("models/player/marine.zym");
1374 //precache_model("models/player/nexus.zym");
1375 //precache_model("models/player/pyria.zym");
1376 //precache_model("models/player/shock.zym");
1377 //precache_model("models/player/skadi.zym");
1378 //precache_model("models/player/specop.zym");
1379 //precache_model("models/player/visitant.zym");
1382 if(cvar("sv_defaultcharacter"))
1385 s = cvar_string("sv_defaultplayermodel_red");
1389 PrecachePlayerSounds(strcat(s, ".sounds"));
1391 s = cvar_string("sv_defaultplayermodel_blue");
1395 PrecachePlayerSounds(strcat(s, ".sounds"));
1397 s = cvar_string("sv_defaultplayermodel_yellow");
1401 PrecachePlayerSounds(strcat(s, ".sounds"));
1403 s = cvar_string("sv_defaultplayermodel_pink");
1407 PrecachePlayerSounds(strcat(s, ".sounds"));
1409 s = cvar_string("sv_defaultplayermodel");
1413 PrecachePlayerSounds(strcat(s, ".sounds"));
1419 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1420 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1423 // gore and miscellaneous sounds
1424 //precache_sound ("misc/h2ohit.wav");
1425 precache_model ("models/hook.md3");
1426 precache_sound ("misc/armorimpact.wav");
1427 precache_sound ("misc/bodyimpact1.wav");
1428 precache_sound ("misc/bodyimpact2.wav");
1429 precache_sound ("misc/gib.wav");
1430 precache_sound ("misc/gib_splat01.wav");
1431 precache_sound ("misc/gib_splat02.wav");
1432 precache_sound ("misc/gib_splat03.wav");
1433 precache_sound ("misc/gib_splat04.wav");
1434 precache_sound ("misc/hit.wav");
1435 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1436 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1437 precache_sound ("misc/null.wav");
1438 precache_sound ("misc/spawn.wav");
1439 precache_sound ("misc/talk.wav");
1440 precache_sound ("misc/teleport.wav");
1441 precache_sound ("misc/poweroff.wav");
1442 precache_sound ("player/lava.wav");
1443 precache_sound ("player/slime.wav");
1446 precache_sound ("misc/jetpack_fly.wav");
1448 // announcer sounds - male
1449 precache_sound ("announcer/male/electrobitch.wav");
1450 precache_sound ("announcer/male/airshot.wav");
1451 precache_sound ("announcer/male/03kills.wav");
1452 precache_sound ("announcer/male/05kills.wav");
1453 precache_sound ("announcer/male/10kills.wav");
1454 precache_sound ("announcer/male/15kills.wav");
1455 precache_sound ("announcer/male/20kills.wav");
1456 precache_sound ("announcer/male/25kills.wav");
1457 precache_sound ("announcer/male/30kills.wav");
1458 precache_sound ("announcer/male/botlike.wav");
1459 precache_sound ("announcer/male/yoda.wav");
1460 precache_sound ("announcer/male/amazing.wav");
1461 precache_sound ("announcer/male/awesome.wav");
1462 precache_sound ("announcer/male/headshot.wav");
1463 precache_sound ("announcer/male/impressive.wav");
1465 // announcer sounds - robotic
1466 precache_sound ("announcer/robotic/prepareforbattle.wav");
1467 precache_sound ("announcer/robotic/begin.wav");
1468 precache_sound ("announcer/robotic/timeoutcalled.wav");
1469 precache_sound ("announcer/robotic/1fragleft.wav");
1470 precache_sound ("announcer/robotic/2fragsleft.wav");
1471 precache_sound ("announcer/robotic/3fragsleft.wav");
1474 precache_sound ("announcer/robotic/lastsecond.wav");
1475 precache_sound ("announcer/robotic/narrowly.wav");
1478 precache_model ("models/sprites/1.spr32");
1479 precache_model ("models/sprites/2.spr32");
1480 precache_model ("models/sprites/3.spr32");
1481 precache_model ("models/sprites/4.spr32");
1482 precache_model ("models/sprites/5.spr32");
1483 precache_model ("models/sprites/6.spr32");
1484 precache_model ("models/sprites/7.spr32");
1485 precache_model ("models/sprites/8.spr32");
1486 precache_model ("models/sprites/9.spr32");
1487 precache_model ("models/sprites/10.spr32");
1488 precache_sound ("announcer/robotic/1.wav");
1489 precache_sound ("announcer/robotic/2.wav");
1490 precache_sound ("announcer/robotic/3.wav");
1491 precache_sound ("announcer/robotic/4.wav");
1492 precache_sound ("announcer/robotic/5.wav");
1493 precache_sound ("announcer/robotic/6.wav");
1494 precache_sound ("announcer/robotic/7.wav");
1495 precache_sound ("announcer/robotic/8.wav");
1496 precache_sound ("announcer/robotic/9.wav");
1497 precache_sound ("announcer/robotic/10.wav");
1499 // common weapon precaches
1500 precache_sound ("weapons/weapon_switch.wav");
1501 precache_sound ("weapons/weaponpickup.wav");
1502 precache_sound ("weapons/unavailable.wav");
1503 if(g_grappling_hook)
1505 precache_sound ("weapons/hook_fire.wav"); // hook
1506 precache_sound ("weapons/hook_impact.wav"); // hook
1509 if (cvar("sv_precacheweapons") || g_nixnex)
1511 //precache weapon models/sounds
1514 while (wep <= WEP_LAST)
1516 weapon_action(wep, WR_PRECACHE);
1521 precache_model("models/elaser.mdl");
1522 precache_model("models/laser.mdl");
1523 precache_model("models/ebomb.mdl");
1526 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1528 if (!self.noise && self.music) // quake 3 uses the music field
1529 self.noise = self.music;
1531 // plays music for the level if there is any
1534 precache_sound (self.noise);
1535 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1540 // sorry, but using \ in macros breaks line numbers
1541 #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
1542 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1543 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1545 vector ExactTriggerHit_mins;
1546 vector ExactTriggerHit_maxs;
1547 float ExactTriggerHit_Recurse()
1553 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1556 if(trace_ent == self)
1561 se.solid = SOLID_NOT;
1562 f = ExactTriggerHit_Recurse();
1568 float ExactTriggerHit()
1572 if not(self.modelindex)
1576 self.solid = SOLID_BSP;
1577 ExactTriggerHit_mins = other.absmin;
1578 ExactTriggerHit_maxs = other.absmax;
1579 f = ExactTriggerHit_Recurse();
1585 // WARNING: this kills the trace globals
1586 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1587 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1589 #define INITPRIO_FIRST 0
1590 #define INITPRIO_GAMETYPE 0
1591 #define INITPRIO_GAMETYPE_FALLBACK 1
1592 #define INITPRIO_CVARS 5
1593 #define INITPRIO_FINDTARGET 10
1594 #define INITPRIO_DROPTOFLOOR 20
1595 #define INITPRIO_SETLOCATION 90
1596 #define INITPRIO_LINKDOORS 91
1597 #define INITPRIO_LAST 99
1599 .void(void) initialize_entity;
1600 .float initialize_entity_order;
1601 .entity initialize_entity_next;
1602 entity initialize_entity_first;
1604 void make_safe_for_remove(entity e)
1606 if(e.initialize_entity)
1609 for(ent = initialize_entity_first; ent; )
1611 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1613 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1614 // skip it in linked list
1617 prev.initialize_entity_next = ent.initialize_entity_next;
1618 ent = prev.initialize_entity_next;
1622 initialize_entity_first = ent.initialize_entity_next;
1623 ent = initialize_entity_first;
1629 ent = ent.initialize_entity_next;
1635 void objerror(string s)
1637 make_safe_for_remove(self);
1638 objerror_builtin(s);
1641 void remove_unsafely(entity e)
1646 void remove_safely(entity e)
1648 make_safe_for_remove(e);
1652 void InitializeEntity(entity e, void(void) func, float order)
1656 if(!e || e.initialize_entity)
1658 // make a proxy initializer entity
1662 e.classname = "initialize_entity";
1666 e.initialize_entity = func;
1667 e.initialize_entity_order = order;
1669 cur = initialize_entity_first;
1672 if(!cur || cur.initialize_entity_order > order)
1674 // insert between prev and cur
1676 prev.initialize_entity_next = e;
1678 initialize_entity_first = e;
1679 e.initialize_entity_next = cur;
1683 cur = cur.initialize_entity_next;
1686 void InitializeEntitiesRun()
1689 startoflist = initialize_entity_first;
1690 initialize_entity_first = world;
1691 for(self = startoflist; self; )
1694 var void(void) func;
1695 e = self.initialize_entity_next;
1696 func = self.initialize_entity;
1697 self.initialize_entity_order = 0;
1698 self.initialize_entity = func_null;
1699 self.initialize_entity_next = world;
1700 if(self.classname == "initialize_entity")
1704 remove_builtin(self);
1707 //dprint("Delayed initialization: ", self.classname, "\n");
1713 .float uncustomizeentityforclient_set;
1714 .void(void) uncustomizeentityforclient;
1715 void(void) SUB_Nullpointer = #0;
1716 void UncustomizeEntitiesRun()
1720 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1721 self.uncustomizeentityforclient();
1724 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1726 e.customizeentityforclient = customizer;
1727 e.uncustomizeentityforclient = uncustomizer;
1728 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1732 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1735 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1739 if(e.classname == "")
1740 e.classname = "net_linked";
1742 if(e.model == "" || self.modelindex == 0)
1746 setmodel(e, "null");
1750 e.SendEntity = sendfunc;
1751 e.SendFlags = 0xFFFFFF;
1754 e.effects |= EF_NODEPTHTEST;
1758 e.nextthink = time + dt;
1759 e.think = SUB_Remove;
1763 void adaptor_think2touch()
1772 void adaptor_think2use()
1784 // deferred dropping
1785 void DropToFloor_Handler()
1787 droptofloor_builtin();
1788 self.dropped_origin = self.origin;
1793 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1798 float trace_hits_box_a0, trace_hits_box_a1;
1800 float trace_hits_box_1d(float end, float thmi, float thma)
1804 // just check if x is in range
1812 // do the trace with respect to x
1813 // 0 -> end has to stay in thmi -> thma
1814 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1815 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1816 if(trace_hits_box_a0 > trace_hits_box_a1)
1822 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1827 // now it is a trace from 0 to end
1829 trace_hits_box_a0 = 0;
1830 trace_hits_box_a1 = 1;
1832 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1834 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1836 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1842 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1844 return trace_hits_box(start, end, thmi - ma, thma - mi);
1847 float SUB_NoImpactCheck()
1849 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1851 if(other == world && self.size != '0 0 0')
1854 tic = self.velocity * sys_ticrate;
1855 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1856 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1857 if(trace_fraction >= 1)
1859 dprint("Odd... did not hit...?\n");
1861 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1863 dprint("Detected and prevented the sky-grapple bug.\n");
1871 #define SUB_OwnerCheck() (other && (other == self.owner))
1873 #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)
1875 float MAX_IPBAN_URIS = 16;
1877 float URI_GET_DISCARD = 0;
1878 float URI_GET_IPBAN = 1;
1879 float URI_GET_IPBAN_END = 16;
1881 void URI_Get_Callback(float id, float status, string data)
1883 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1885 dprint("\nEnd of data.\n");
1887 if(id == URI_GET_DISCARD)
1891 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1894 OnlineBanList_URI_Get_Callback(id, status, data);
1898 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1902 void print_to(entity e, string s)
1905 sprint(e, strcat(s, "\n"));
1924 for(i = 0; i < MapInfo_count; ++i)
1926 if(MapInfo_Get_ByID(i))
1928 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1931 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1932 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1940 for(i = 0; i < MapInfo_count; ++i)
1942 if(MapInfo_Get_ByID(i))
1944 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1947 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1948 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1953 MapInfo_ClearTemps();
1956 return "No records are available on this server.\n";
1958 return strcat("Records on this server:\n", s);
1961 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1964 vector start, org, delta, end, enddown, mstart;
1966 m = e.dphitcontentsmask;
1967 e.dphitcontentsmask = goodcontents | badcontents;
1970 delta = world.maxs - world.mins;
1972 for(i = 0; i < attempts; ++i)
1974 start_x = org_x + random() * delta_x;
1975 start_y = org_y + random() * delta_y;
1976 start_z = org_z + random() * delta_z;
1978 // rule 1: start inside world bounds, and outside
1979 // solid, and don't start from somewhere where you can
1980 // fall down to evil
1981 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1982 if(trace_fraction >= 1)
1984 if(trace_startsolid)
1986 if(trace_dphitcontents & badcontents)
1988 if(trace_dphitq3surfaceflags & badsurfaceflags)
1991 // rule 2: if we are too high, lower the point
1992 if(trace_fraction * delta_z > maxaboveground)
1993 start = trace_endpos + '0 0 1' * maxaboveground;
1994 enddown = trace_endpos;
1996 // rule 3: make sure we aren't outside the map. This only works
1997 // for somewhat well formed maps. A good rule of thumb is that
1998 // the map should have a convex outside hull.
1999 // these can be traceLINES as we already verified the starting box
2000 mstart = start + 0.5 * (e.mins + e.maxs);
2001 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2002 if(trace_fraction >= 1)
2004 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2005 if(trace_fraction >= 1)
2007 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2008 if(trace_fraction >= 1)
2010 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2011 if(trace_fraction >= 1)
2013 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2014 if(trace_fraction >= 1)
2017 // find a random vector to "look at"
2018 end_x = org_x + random() * delta_x;
2019 end_y = org_y + random() * delta_y;
2020 end_z = org_z + random() * delta_z;
2021 end = start + normalize(end - start) * vlen(delta);
2023 // rule 4: start TO end must not be too short
2024 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2025 if(trace_startsolid)
2027 if(trace_fraction < minviewdistance / vlen(delta))
2030 // rule 5: don't want to look at sky
2031 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2034 // rule 6: we must not end up in trigger_hurt
2035 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2037 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2044 e.dphitcontentsmask = m;
2048 setorigin(e, start);
2049 e.angles = vectoangles(end - start);
2050 dprint("Needed ", ftos(i + 1), " attempts\n");
2057 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2059 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2060 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2061 WriteShort(MSG_BROADCAST, effectno);
2062 WriteCoord(MSG_BROADCAST, start_x);
2063 WriteCoord(MSG_BROADCAST, start_y);
2064 WriteCoord(MSG_BROADCAST, start_z);
2065 WriteCoord(MSG_BROADCAST, end_x);
2066 WriteCoord(MSG_BROADCAST, end_y);
2067 WriteCoord(MSG_BROADCAST, end_z);
2068 WriteCoord(MSG_BROADCAST, end_dz);
2069 WriteShort(MSG_BROADCAST, spd / 16);
2072 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2075 vector vecxy, velxy;
2077 vecxy = end - start; vecxy_z = 0;
2078 velxy = vel; velxy_z = 0;
2080 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2082 trailparticles(world, effectno, start, end);
2086 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2087 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2090 string GetGametype(); // g_world.qc
2091 void write_recordmarker(entity pl, float tstart, float dt)
2093 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2095 // also write a marker into demo files for demotc-race-record-extractor to find
2098 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2099 " ", ftos(tstart), " ", ftos(dt), "\n"));
2102 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2106 if (cvar("g_shootfromeye"))
2119 else if (cvar("g_shootfromcenter"))
2124 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2139 void attach_sameorigin(entity e, entity to, string tag)
2141 vector org, t_forward, t_left, t_up, e_forward, e_up;
2148 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2149 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2150 t_forward = v_forward * tagscale;
2151 t_left = v_right * -tagscale;
2152 t_up = v_up * tagscale;
2154 e.origin_x = org * t_forward;
2155 e.origin_y = org * t_left;
2156 e.origin_z = org * t_up;
2158 // current forward and up directions
2159 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2160 e.angles_x = -e.angles_x;
2161 fixedmakevectors(e.angles);
2163 // untransform forward, up!
2164 e_forward_x = v_forward * t_forward;
2165 e_forward_y = v_forward * t_left;
2166 e_forward_z = v_forward * t_up;
2167 e_up_x = v_up * t_forward;
2168 e_up_y = v_up * t_left;
2169 e_up_z = v_up * t_up;
2171 e.angles = fixedvectoangles2(e_forward, e_up);
2172 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2173 e.angles_x = -e.angles_x;
2175 setattachment(e, to, tag);
2176 setorigin(e, e.origin);
2179 void detach_sameorigin(entity e)
2182 org = gettaginfo(e, 0);
2183 e.angles = fixedvectoangles2(v_forward, v_up);
2184 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2185 e.angles_x = -e.angles_x;
2187 setattachment(e, world, "");
2188 setorigin(e, e.origin);
2191 void follow_sameorigin(entity e, entity to)
2193 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2194 e.aiment = to; // make the hole follow bmodel
2195 e.punchangle = to.angles; // the original angles of bmodel
2196 e.view_ofs = e.origin - to.origin; // relative origin
2197 e.v_angle = e.angles - to.angles; // relative angles
2200 void unfollow_sameorigin(entity e)
2202 e.movetype = MOVETYPE_NONE;
2205 entity gettaginfo_relative_ent;
2206 vector gettaginfo_relative(entity e, float tag)
2208 if(!gettaginfo_relative_ent)
2210 gettaginfo_relative_ent = spawn();
2211 gettaginfo_relative_ent.effects = EF_NODRAW;
2213 gettaginfo_relative_ent.model = e.model;
2214 gettaginfo_relative_ent.modelindex = e.modelindex;
2215 gettaginfo_relative_ent.frame = e.frame;
2216 return gettaginfo(gettaginfo_relative_ent, tag);
2219 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2223 if(pl.soundentity.cnt & p)
2225 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2226 pl.soundentity.cnt |= p;
2229 void SoundEntity_StopSound(entity pl, float chan)
2233 if(pl.soundentity.cnt & p)
2235 stopsoundto(MSG_ALL, pl.soundentity, chan);
2236 pl.soundentity.cnt &~= p;
2240 void SoundEntity_Attach(entity pl)
2242 pl.soundentity = spawn();
2243 pl.soundentity.classname = "soundentity";
2244 pl.soundentity.owner = pl;
2245 setattachment(pl.soundentity, pl, "");
2246 setmodel(pl.soundentity, "null");
2249 void SoundEntity_Detach(entity pl)
2252 for(i = 0; i <= 7; ++i)
2253 SoundEntity_StopSound(pl, i);
2257 float ParseCommandPlayerSlotTarget_firsttoken;
2258 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2266 ParseCommandPlayerSlotTarget_firsttoken = -1;
2270 if(substring(argv(idx), 0, 1) == "#")
2272 s = substring(argv(idx), 1, -1);
2280 if(s == ftos(stof(s)))
2282 e = edict_num(stof(s));
2283 if(e.flags & FL_CLIENT)
2285 ParseCommandPlayerSlotTarget_firsttoken = idx;
2292 // it must be a nick name
2297 FOR_EACH_CLIENT(head)
2298 if(head.netname == s)
2305 ParseCommandPlayerSlotTarget_firsttoken = idx;
2309 s = strdecolorize(s);
2311 FOR_EACH_CLIENT(head)
2312 if(strdecolorize(head.netname) == s)
2319 ParseCommandPlayerSlotTarget_firsttoken = idx;