1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
9 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
11 void crosshair_trace_plusvisibletriggers(entity pl)
15 first = findchainfloat(solid, SOLID_TRIGGER);
17 for (e = first; e; e = e.chain)
23 for (e = first; e; e = e.chain)
24 e.solid = SOLID_TRIGGER;
26 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
27 void WarpZone_crosshair_trace(entity pl)
29 WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
32 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
33 void() spawnpoint_use;
36 string admin_name(void)
38 if(autocvar_sv_adminnick != "")
39 return autocvar_sv_adminnick;
41 return "SERVER ADMIN";
44 float DistributeEvenly_amount;
45 float DistributeEvenly_totalweight;
46 void DistributeEvenly_Init(float amount, float totalweight)
48 if (DistributeEvenly_amount)
50 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
51 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
54 DistributeEvenly_amount = 0;
56 DistributeEvenly_amount = amount;
57 DistributeEvenly_totalweight = totalweight;
59 float DistributeEvenly_Get(float weight)
64 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
65 DistributeEvenly_totalweight -= weight;
66 DistributeEvenly_amount -= f;
69 float DistributeEvenly_GetRandomized(float weight)
74 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
75 DistributeEvenly_totalweight -= weight;
76 DistributeEvenly_amount -= f;
80 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
83 string STR_PLAYER = "player";
84 string STR_SPECTATOR = "spectator";
85 string STR_OBSERVER = "observer";
88 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
89 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
90 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
91 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
93 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
94 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
95 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
96 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
97 #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if(v.classname != STR_PLAYER)
98 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
101 #define CENTER_OR_VIEWOFS(ent) (ent.origin + ((ent.classname == STR_PLAYER) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
103 // copies a string to a tempstring (so one can strunzone it)
104 string strcat1(string s) = #115; // FRIK_FILE
109 void bcenterprint(string s)
111 // TODO replace by MSG_ALL (would show it to spectators too, though)?
113 FOR_EACH_PLAYER(head)
114 if (clienttype(head) == CLIENTTYPE_REAL)
115 centerprint(head, s);
118 void GameLogEcho(string s)
123 if (autocvar_sv_eventlog_files)
128 matches = autocvar_sv_eventlog_files_counter + 1;
129 cvar_set("sv_eventlog_files_counter", ftos(matches));
132 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
133 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
134 logfile = fopen(fn, FILE_APPEND);
135 fputs(logfile, ":logversion:3\n");
139 if (autocvar_sv_eventlog_files_timestamps)
140 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
142 fputs(logfile, strcat(s, "\n"));
145 if (autocvar_sv_eventlog_console)
154 // will be opened later
159 if (logfile_open && logfile >= 0)
166 float spawnpoint_nag;
167 void relocate_spawnpoint()
169 // nudge off the floor
170 setorigin(self, self.origin + '0 0 1');
172 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
173 if (trace_startsolid)
179 if (!move_out_of_solid(self))
180 objerror("could not get out of solid at all!");
181 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
182 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
183 print(" ", ftos(self.origin_y - o_y));
184 print(" ", ftos(self.origin_z - o_z), "'\n");
185 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
188 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
194 self.mins = self.maxs = '0 0 0';
195 objerror("player spawn point in solid, mapper sucks!\n");
200 self.use = spawnpoint_use;
201 self.team_saved = self.team;
205 if (have_team_spawns != 0)
207 have_team_spawns = 1;
208 have_team_spawns_forteam[self.team] = 1;
210 if (autocvar_r_showbboxes)
212 // show where spawnpoints point at too
213 makevectors(self.angles);
216 e.classname = "info_player_foo";
217 setorigin(e, self.origin + v_forward * 24);
218 setsize(e, '-8 -8 -8', '8 8 8');
219 e.solid = SOLID_TRIGGER;
223 #define strstr strstrofs
225 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
226 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
227 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
228 // BE CONSTANT OR strzoneD!
229 float strstr(string haystack, string needle, float offset)
233 len = strlen(needle);
234 endpos = strlen(haystack) - len;
235 while(offset <= endpos)
237 found = substring(haystack, offset, len);
246 float NUM_NEAREST_ENTITIES = 4;
247 entity nearest_entity[NUM_NEAREST_ENTITIES];
248 float nearest_length[NUM_NEAREST_ENTITIES];
249 entity findnearest(vector point, .string field, string value, vector axismod)
260 localhead = find(world, field, value);
263 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
264 dist = localhead.oldorigin;
266 dist = localhead.origin;
268 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
271 for (i = 0; i < num_nearest; ++i)
273 if (len < nearest_length[i])
277 // now i tells us where to insert at
278 // INSERTION SORT! YOU'VE SEEN IT! RUN!
279 if (i < NUM_NEAREST_ENTITIES)
281 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
283 nearest_length[j + 1] = nearest_length[j];
284 nearest_entity[j + 1] = nearest_entity[j];
286 nearest_length[i] = len;
287 nearest_entity[i] = localhead;
288 if (num_nearest < NUM_NEAREST_ENTITIES)
289 num_nearest = num_nearest + 1;
292 localhead = find(localhead, field, value);
295 // now use the first one from our list that we can see
296 for (i = 0; i < num_nearest; ++i)
298 traceline(point, nearest_entity[i].origin, TRUE, world);
299 if (trace_fraction == 1)
303 dprint("Nearest point (");
304 dprint(nearest_entity[0].netname);
305 dprint(") is not visible, using a visible one.\n");
307 return nearest_entity[i];
311 if (num_nearest == 0)
314 dprint("Not seeing any location point, using nearest as fallback.\n");
316 dprint("Candidates were: ");
317 for(j = 0; j < num_nearest; ++j)
321 dprint(nearest_entity[j].netname);
326 return nearest_entity[0];
329 void spawnfunc_target_location()
331 self.classname = "target_location";
332 // location name in netname
333 // eventually support: count, teamgame selectors, line of sight?
336 void spawnfunc_info_location()
338 self.classname = "target_location";
339 self.message = self.netname;
342 string NearestLocation(vector p)
347 loc = findnearest(p, classname, "target_location", '1 1 1');
354 loc = findnearest(p, target, "###item###", '1 1 4');
361 string formatmessage(string msg)
372 WarpZone_crosshair_trace(self);
373 cursor = trace_endpos;
374 cursor_ent = trace_ent;
378 break; // too many replacements
381 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
382 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
395 replacement = substring(msg, p, 2);
396 escape = substring(msg, p + 1, 1);
400 else if (escape == "\\")
402 else if (escape == "n")
404 else if (escape == "a")
405 replacement = ftos(floor(self.armorvalue));
406 else if (escape == "h")
407 replacement = ftos(floor(self.health));
408 else if (escape == "l")
409 replacement = NearestLocation(self.origin);
410 else if (escape == "y")
411 replacement = NearestLocation(cursor);
412 else if (escape == "d")
413 replacement = NearestLocation(self.death_origin);
414 else if (escape == "w") {
418 wep = self.switchweapon;
421 replacement = W_Name(wep);
422 } else if (escape == "W") {
423 if (self.items & IT_SHELLS) replacement = "shells";
424 else if (self.items & IT_NAILS) replacement = "bullets";
425 else if (self.items & IT_ROCKETS) replacement = "rockets";
426 else if (self.items & IT_CELLS) replacement = "cells";
427 else replacement = "batteries"; // ;)
428 } else if (escape == "x") {
429 replacement = cursor_ent.netname;
430 if (replacement == "" || !cursor_ent)
431 replacement = "nothing";
432 } else if (escape == "s")
433 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
434 else if (escape == "S")
435 replacement = ftos(vlen(self.velocity));
437 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
438 p = p + strlen(replacement);
443 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
444 return (value == 0) ? FALSE : TRUE;
453 >0: receives a cvar from name=argv(f) value=argv(f+1)
455 void GetCvars_handleString(string thisname, float f, .string field, string name)
460 strunzone(self.field);
461 self.field = string_null;
465 if (thisname == name)
468 strunzone(self.field);
469 self.field = strzone(argv(f + 1));
473 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
475 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
477 GetCvars_handleString(thisname, f, field, name);
478 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
479 if (thisname == name)
482 s = func(strcat1(self.field));
485 strunzone(self.field);
486 self.field = strzone(s);
490 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
497 if (thisname == name)
498 self.field = stof(argv(f + 1));
501 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
503 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
510 if (thisname == name)
514 self.field = stof(argv(f + 1));
523 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
526 float w_getbestweapon(entity e);
527 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
530 o = W_FixWeaponOrder_ForceComplete(wo);
531 if(self.weaponorder_byimpulse)
533 strunzone(self.weaponorder_byimpulse);
534 self.weaponorder_byimpulse = string_null;
536 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
539 void GetCvars(float f)
541 string s = string_null;
544 s = strcat1(argv(f));
548 MUTATOR_CALLHOOK(GetCvars);
549 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
550 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
551 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
552 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
553 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
554 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
555 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
556 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
557 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
558 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
559 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
560 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
561 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
562 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
563 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
564 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
565 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
566 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
567 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
568 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
569 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
570 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
571 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
573 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
574 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
576 #ifdef ALLOW_FORCEMODELS
577 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
578 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
580 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
581 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
582 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
583 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
584 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
586 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
589 if (s == "cl_weaponpriority")
590 self.switchweapon = w_getbestweapon(self);
591 if (s == "cl_allow_uidtracking")
592 PlayerStats_AddPlayer(self);
596 void backtrace(string msg)
599 dev = autocvar_developer;
600 war = autocvar_prvm_backtraceforwarnings;
601 cvar_set("developer", "1");
602 cvar_set("prvm_backtraceforwarnings", "1");
604 print("--- CUT HERE ---\nWARNING: ");
607 remove(world); // isn't there any better way to cause a backtrace?
608 print("\n--- CUT UNTIL HERE ---\n");
609 cvar_set("developer", ftos(dev));
610 cvar_set("prvm_backtraceforwarnings", ftos(war));
613 // decolorizes and team colors the player name when needed
614 string playername(entity p)
617 if (teamplay && !intermission_running && p.classname == "player")
619 t = Team_ColorCode(p.team);
620 return strcat(t, strdecolorize(p.netname));
626 vector randompos(vector m1, vector m2)
630 v_x = m2_x * random() + m1_x;
631 v_y = m2_y * random() + m1_y;
632 v_z = m2_z * random() + m1_z;
636 //#NO AUTOCVARS START
638 float g_pickup_shells;
639 float g_pickup_shells_max;
640 float g_pickup_nails;
641 float g_pickup_nails_max;
642 float g_pickup_rockets;
643 float g_pickup_rockets_max;
644 float g_pickup_cells;
645 float g_pickup_cells_max;
647 float g_pickup_fuel_jetpack;
648 float g_pickup_fuel_max;
649 float g_pickup_armorsmall;
650 float g_pickup_armorsmall_max;
651 float g_pickup_armorsmall_anyway;
652 float g_pickup_armormedium;
653 float g_pickup_armormedium_max;
654 float g_pickup_armormedium_anyway;
655 float g_pickup_armorbig;
656 float g_pickup_armorbig_max;
657 float g_pickup_armorbig_anyway;
658 float g_pickup_armorlarge;
659 float g_pickup_armorlarge_max;
660 float g_pickup_armorlarge_anyway;
661 float g_pickup_healthsmall;
662 float g_pickup_healthsmall_max;
663 float g_pickup_healthsmall_anyway;
664 float g_pickup_healthmedium;
665 float g_pickup_healthmedium_max;
666 float g_pickup_healthmedium_anyway;
667 float g_pickup_healthlarge;
668 float g_pickup_healthlarge_max;
669 float g_pickup_healthlarge_anyway;
670 float g_pickup_healthmega;
671 float g_pickup_healthmega_max;
672 float g_pickup_healthmega_anyway;
673 float g_pickup_ammo_anyway;
674 float g_pickup_weapons_anyway;
676 WEPSET_DECLARE_A(g_weaponarena_weapons);
677 float g_weaponarena_random;
678 float g_weaponarena_random_with_laser;
679 string g_weaponarena_list;
680 float g_weaponspeedfactor;
681 float g_weaponratefactor;
682 float g_weapondamagefactor;
683 float g_weaponforcefactor;
684 float g_weaponspreadfactor;
686 WEPSET_DECLARE_A(start_weapons);
687 WEPSET_DECLARE_A(start_weapons_default);
688 WEPSET_DECLARE_A(start_weapons_defaultmask);
690 float start_ammo_shells;
691 float start_ammo_nails;
692 float start_ammo_rockets;
693 float start_ammo_cells;
694 float start_ammo_fuel;
696 float start_armorvalue;
697 WEPSET_DECLARE_A(warmup_start_weapons);
698 WEPSET_DECLARE_A(warmup_start_weapons_default);
699 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
700 float warmup_start_ammo_shells;
701 float warmup_start_ammo_nails;
702 float warmup_start_ammo_rockets;
703 float warmup_start_ammo_cells;
704 float warmup_start_ammo_fuel;
705 float warmup_start_health;
706 float warmup_start_armorvalue;
709 entity get_weaponinfo(float w);
711 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
713 var float i = weaponinfo.weapon;
719 if (g_lms || g_ca || allguns)
721 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
727 d = (i == WEP_SHOTGUN);
729 d = 0; // weapon is set a few lines later
731 d = (i == WEP_LASER || i == WEP_SHOTGUN);
733 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
734 d |= (i == WEP_HOOK);
735 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
738 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
740 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
745 // 4: is set by default?
754 void readplayerstartcvars()
760 // initialize starting values for players
761 WEPSET_CLEAR_A(start_weapons);
762 WEPSET_CLEAR_A(start_weapons_default);
763 WEPSET_CLEAR_A(start_weapons_defaultmask);
765 start_ammo_shells = 0;
766 start_ammo_nails = 0;
767 start_ammo_rockets = 0;
768 start_ammo_cells = 0;
769 start_health = cvar("g_balance_health_start");
770 start_armorvalue = cvar("g_balance_armor_start");
773 WEPSET_CLEAR_A(g_weaponarena_weapons);
775 s = cvar_string("g_weaponarena");
776 if (s == "0" || s == "")
782 if (s == "0" || s == "")
788 // forcibly turn off weaponarena
793 g_weaponarena_list = "All Weapons";
794 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
796 e = get_weaponinfo(j);
797 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
798 WEPSET_OR_AW(g_weaponarena_weapons, j);
801 else if (s == "most")
804 g_weaponarena_list = "Most Weapons";
805 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
807 e = get_weaponinfo(j);
808 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
809 if (e.spawnflags & WEP_FLAG_NORMAL)
810 WEPSET_OR_AW(g_weaponarena_weapons, j);
813 else if (s == "none")
816 g_weaponarena_list = "No Weapons";
821 t = tokenize_console(s);
822 g_weaponarena_list = "";
823 for (i = 0; i < t; ++i)
826 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
828 e = get_weaponinfo(j);
831 WEPSET_OR_AW(g_weaponarena_weapons, j);
832 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
838 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
841 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
845 g_weaponarena_random = cvar("g_weaponarena_random");
847 g_weaponarena_random = 0;
848 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
852 g_minstagib = 0; // incompatible
853 g_pinata = 0; // incompatible
854 g_weapon_stay = 0; // incompatible
855 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
857 start_items |= IT_UNLIMITED_AMMO;
859 else if (g_minstagib)
861 g_pinata = 0; // incompatible
862 g_weapon_stay = 0; // incompatible
863 g_bloodloss = 0; // incompatible
865 start_armorvalue = 0;
866 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
867 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
868 start_items |= IT_UNLIMITED_SUPERWEAPONS;
870 if (g_minstagib_invis_alpha <= 0)
871 g_minstagib_invis_alpha = -1;
875 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
877 e = get_weaponinfo(i);
878 float w = want_weapon("g_start_weapon_", e, FALSE);
880 WEPSET_OR_AW(start_weapons, i);
882 WEPSET_OR_AW(start_weapons_default, i);
884 WEPSET_OR_AW(start_weapons_defaultmask, i);
888 if(!cvar("g_use_ammunition"))
889 start_items |= IT_UNLIMITED_AMMO;
891 if(cvar("g_nexball"))
892 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
896 start_ammo_cells = cvar("g_minstagib_ammo_start");
897 start_ammo_fuel = cvar("g_start_ammo_fuel");
899 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
901 start_ammo_rockets = 999;
902 start_ammo_shells = 999;
903 start_ammo_cells = 999;
904 start_ammo_nails = 999;
905 start_ammo_fuel = 999;
911 start_ammo_shells = cvar("g_lms_start_ammo_shells");
912 start_ammo_nails = cvar("g_lms_start_ammo_nails");
913 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
914 start_ammo_cells = cvar("g_lms_start_ammo_cells");
915 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
919 start_ammo_shells = cvar("g_start_ammo_shells");
920 start_ammo_nails = cvar("g_start_ammo_nails");
921 start_ammo_rockets = cvar("g_start_ammo_rockets");
922 start_ammo_cells = cvar("g_start_ammo_cells");
923 start_ammo_fuel = cvar("g_start_ammo_fuel");
929 start_health = cvar("g_lms_start_health");
930 start_armorvalue = cvar("g_lms_start_armor");
935 warmup_start_ammo_shells = start_ammo_shells;
936 warmup_start_ammo_nails = start_ammo_nails;
937 warmup_start_ammo_rockets = start_ammo_rockets;
938 warmup_start_ammo_cells = start_ammo_cells;
939 warmup_start_ammo_fuel = start_ammo_fuel;
940 warmup_start_health = start_health;
941 warmup_start_armorvalue = start_armorvalue;
942 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
943 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
944 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
946 if (!g_weaponarena && !g_minstagib && !g_ca)
948 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
949 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
950 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
951 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
952 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
953 warmup_start_health = cvar("g_warmup_start_health");
954 warmup_start_armorvalue = cvar("g_warmup_start_armor");
955 WEPSET_CLEAR_A(warmup_start_weapons);
956 WEPSET_CLEAR_A(warmup_start_weapons_default);
957 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
958 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
960 e = get_weaponinfo(i);
961 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
963 WEPSET_OR_AW(warmup_start_weapons, i);
965 WEPSET_OR_AW(warmup_start_weapons_default, i);
967 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
973 start_items |= IT_JETPACK;
975 MUTATOR_CALLHOOK(SetStartItems);
977 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
979 g_grappling_hook = 0; // these two can't coexist, as they use the same button
980 start_items |= IT_FUEL_REGEN;
981 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
982 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
985 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
987 e = get_weaponinfo(i);
988 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
989 weapon_action(i, WR_PRECACHE);
992 start_ammo_shells = max(0, start_ammo_shells);
993 start_ammo_nails = max(0, start_ammo_nails);
994 start_ammo_cells = max(0, start_ammo_cells);
995 start_ammo_rockets = max(0, start_ammo_rockets);
996 start_ammo_fuel = max(0, start_ammo_fuel);
998 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
999 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1000 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1001 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1002 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1006 float g_bugrigs_planar_movement;
1007 float g_bugrigs_planar_movement_car_jumping;
1008 float g_bugrigs_reverse_spinning;
1009 float g_bugrigs_reverse_speeding;
1010 float g_bugrigs_reverse_stopping;
1011 float g_bugrigs_air_steering;
1012 float g_bugrigs_angle_smoothing;
1013 float g_bugrigs_friction_floor;
1014 float g_bugrigs_friction_brake;
1015 float g_bugrigs_friction_air;
1016 float g_bugrigs_accel;
1017 float g_bugrigs_speed_ref;
1018 float g_bugrigs_speed_pow;
1019 float g_bugrigs_steer;
1021 float g_touchexplode;
1022 float g_touchexplode_radius;
1023 float g_touchexplode_damage;
1024 float g_touchexplode_edgedamage;
1025 float g_touchexplode_force;
1032 float sv_pitch_fixyaw;
1034 string GetGametype(); // g_world.qc
1035 void readlevelcvars(void)
1037 g_minstagib = cvar("g_minstagib");
1039 // load ALL the mutators
1040 if(cvar("g_dodging"))
1041 MUTATOR_ADD(mutator_dodging);
1042 if(cvar("g_spawn_near_teammate"))
1043 MUTATOR_ADD(mutator_spawn_near_teammate);
1044 if(cvar("g_physical_items"))
1045 MUTATOR_ADD(mutator_physical_items);
1048 if(cvar("g_invincible_projectiles"))
1049 MUTATOR_ADD(mutator_invincibleprojectiles);
1050 if(cvar("g_new_toys"))
1051 MUTATOR_ADD(mutator_new_toys);
1053 MUTATOR_ADD(mutator_nix);
1054 if(cvar("g_rocket_flying"))
1055 MUTATOR_ADD(mutator_rocketflying);
1056 if(cvar("g_vampire"))
1057 MUTATOR_ADD(mutator_vampire);
1058 if(cvar("g_superspectate"))
1059 MUTATOR_ADD(mutator_superspec);
1062 // is this a mutator? is this a mode?
1063 if(cvar("g_sandbox"))
1064 MUTATOR_ADD(sandbox);
1066 if(cvar("sv_allow_fullbright"))
1067 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1069 g_bugrigs = cvar("g_bugrigs");
1070 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1071 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1072 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1073 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1074 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1075 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1076 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1077 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1078 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1079 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1080 g_bugrigs_accel = cvar("g_bugrigs_accel");
1081 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1082 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1083 g_bugrigs_steer = cvar("g_bugrigs_steer");
1085 g_touchexplode = cvar("g_touchexplode");
1086 g_touchexplode_radius = cvar("g_touchexplode_radius");
1087 g_touchexplode_damage = cvar("g_touchexplode_damage");
1088 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1089 g_touchexplode_force = cvar("g_touchexplode_force");
1091 #ifdef ALLOW_FORCEMODELS
1092 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1095 sv_clones = cvar("sv_clones");
1096 sv_foginterval = cvar("sv_foginterval");
1097 g_cloaked = cvar("g_cloaked");
1099 g_cloaked = 1; // always enable cloak in CTS
1100 g_jump_grunt = cvar("g_jump_grunt");
1101 g_footsteps = cvar("g_footsteps");
1102 g_grappling_hook = cvar("g_grappling_hook");
1103 g_jetpack = cvar("g_jetpack");
1104 g_midair = cvar("g_midair");
1105 g_norecoil = cvar("g_norecoil");
1106 g_bloodloss = cvar("g_bloodloss");
1107 sv_maxidle = cvar("sv_maxidle");
1108 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1109 sv_autotaunt = cvar("sv_autotaunt");
1110 sv_taunt = cvar("sv_taunt");
1112 inWarmupStage = cvar("g_warmup");
1113 g_warmup_limit = cvar("g_warmup_limit");
1114 g_warmup_allguns = cvar("g_warmup_allguns");
1115 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1117 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1118 inWarmupStage = 0; // these modes cannot work together, sorry
1120 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1121 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1122 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1123 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1124 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1125 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1126 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1127 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1128 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1129 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1130 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1131 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1132 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1133 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1135 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1136 g_weaponratefactor = cvar("g_weaponratefactor");
1137 g_weapondamagefactor = cvar("g_weapondamagefactor");
1138 g_weaponforcefactor = cvar("g_weaponforcefactor");
1139 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1141 g_pickup_shells = cvar("g_pickup_shells");
1142 g_pickup_shells_max = cvar("g_pickup_shells_max");
1143 g_pickup_nails = cvar("g_pickup_nails");
1144 g_pickup_nails_max = cvar("g_pickup_nails_max");
1145 g_pickup_rockets = cvar("g_pickup_rockets");
1146 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1147 g_pickup_cells = cvar("g_pickup_cells");
1148 g_pickup_cells_max = cvar("g_pickup_cells_max");
1149 g_pickup_fuel = cvar("g_pickup_fuel");
1150 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1151 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1152 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1153 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1154 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1155 g_pickup_armormedium = cvar("g_pickup_armormedium");
1156 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1157 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1158 g_pickup_armorbig = cvar("g_pickup_armorbig");
1159 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1160 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1161 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1162 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1163 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1164 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1165 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1166 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1167 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1168 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1169 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1170 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1171 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1172 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1173 g_pickup_healthmega = cvar("g_pickup_healthmega");
1174 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1175 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1177 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1178 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1180 g_pinata = cvar("g_pinata");
1182 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1184 g_weapon_stay = cvar("g_weapon_stay");
1186 if not(inWarmupStage && !g_ca)
1187 game_starttime = cvar("g_start_delay");
1189 sv_pitch_min = cvar("sv_pitch_min");
1190 sv_pitch_max = cvar("sv_pitch_max");
1191 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1193 readplayerstartcvars();
1199 string precache_sound (string s) = #19;
1200 float precache_sound_index (string s) = #19;
1202 #define SND_VOLUME 1
1203 #define SND_ATTENUATION 2
1204 #define SND_LARGEENTITY 8
1205 #define SND_LARGESOUND 16
1207 float sound_allowed(float dest, entity e)
1209 // sounds from world may always pass
1212 if (e.classname == "body")
1214 else if (e.realowner && e.realowner != e)
1216 else if (e.owner && e.owner != e)
1221 // sounds to self may always pass
1222 if (dest == MSG_ONE)
1223 if (e == msg_entity)
1225 // sounds by players can be removed
1226 if (autocvar_bot_sound_monopoly)
1227 if (clienttype(e) == CLIENTTYPE_REAL)
1229 // anything else may pass
1233 #ifdef COMPAT_XON010_CHANNELS
1234 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1235 void sound(entity e, float chan, string samp, float vol, float atten)
1237 if (!sound_allowed(MSG_BROADCAST, e))
1239 builtin_sound(e, chan, samp, vol, atten);
1243 void sound(entity e, float chan, string samp, float vol, float atten)
1245 if (!sound_allowed(MSG_BROADCAST, e))
1247 sound7(e, chan, samp, vol, atten, 0, 0);
1251 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1255 if (!sound_allowed(dest, e))
1258 entno = num_for_edict(e);
1259 idx = precache_sound_index(samp);
1264 atten = floor(atten * 64);
1265 vol = floor(vol * 255);
1268 sflags |= SND_VOLUME;
1270 sflags |= SND_ATTENUATION;
1271 if (entno >= 8192 || chan < 0 || chan > 7)
1272 sflags |= SND_LARGEENTITY;
1274 sflags |= SND_LARGESOUND;
1276 WriteByte(dest, SVC_SOUND);
1277 WriteByte(dest, sflags);
1278 if (sflags & SND_VOLUME)
1279 WriteByte(dest, vol);
1280 if (sflags & SND_ATTENUATION)
1281 WriteByte(dest, atten);
1282 if (sflags & SND_LARGEENTITY)
1284 WriteShort(dest, entno);
1285 WriteByte(dest, chan);
1289 WriteShort(dest, entno * 8 + chan);
1291 if (sflags & SND_LARGESOUND)
1292 WriteShort(dest, idx);
1294 WriteByte(dest, idx);
1296 WriteCoord(dest, o_x);
1297 WriteCoord(dest, o_y);
1298 WriteCoord(dest, o_z);
1300 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1304 if (!sound_allowed(dest, e))
1307 o = e.origin + 0.5 * (e.mins + e.maxs);
1308 soundtoat(dest, e, o, chan, samp, vol, atten);
1310 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1312 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1314 void stopsoundto(float dest, entity e, float chan)
1318 if (!sound_allowed(dest, e))
1321 entno = num_for_edict(e);
1323 if (entno >= 8192 || chan < 0 || chan > 7)
1326 idx = precache_sound_index("misc/null.wav");
1327 sflags = SND_LARGEENTITY;
1329 sflags |= SND_LARGESOUND;
1330 WriteByte(dest, SVC_SOUND);
1331 WriteByte(dest, sflags);
1332 WriteShort(dest, entno);
1333 WriteByte(dest, chan);
1334 if (sflags & SND_LARGESOUND)
1335 WriteShort(dest, idx);
1337 WriteByte(dest, idx);
1338 WriteCoord(dest, e.origin_x);
1339 WriteCoord(dest, e.origin_y);
1340 WriteCoord(dest, e.origin_z);
1344 WriteByte(dest, SVC_STOPSOUND);
1345 WriteShort(dest, entno * 8 + chan);
1348 void stopsound(entity e, float chan)
1350 if (!sound_allowed(MSG_BROADCAST, e))
1353 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1354 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1357 void play2(entity e, string filename)
1359 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1361 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1364 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1366 float spamsound(entity e, float chan, string samp, float vol, float atten)
1368 if (!sound_allowed(MSG_BROADCAST, e))
1371 if (time > e.spamtime)
1374 sound(e, chan, samp, vol, atten);
1380 void play2team(float t, string filename)
1384 if (autocvar_bot_sound_monopoly)
1387 FOR_EACH_REALPLAYER(head)
1390 play2(head, filename);
1394 void play2all(string samp)
1396 if (autocvar_bot_sound_monopoly)
1399 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1402 void PrecachePlayerSounds(string f);
1403 void precache_playermodel(string m)
1405 float globhandle, i, n;
1408 if(substring(m, -9,5) == "_lod1")
1410 if(substring(m, -9,5) == "_lod2")
1413 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1416 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1420 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1423 n = search_getsize(globhandle);
1424 for (i = 0; i < n; ++i)
1426 //print(search_getfilename(globhandle, i), "\n");
1427 f = search_getfilename(globhandle, i);
1428 PrecachePlayerSounds(f);
1430 search_end(globhandle);
1432 void precache_all_playermodels(string pattern)
1434 float globhandle, i, n;
1437 globhandle = search_begin(pattern, TRUE, FALSE);
1440 n = search_getsize(globhandle);
1441 for (i = 0; i < n; ++i)
1443 //print(search_getfilename(globhandle, i), "\n");
1444 f = search_getfilename(globhandle, i);
1445 precache_playermodel(f);
1447 search_end(globhandle);
1452 // gamemode related things
1453 precache_model ("models/misc/chatbubble.spr");
1456 precache_model ("models/runematch/curse.mdl");
1457 precache_model ("models/runematch/rune.mdl");
1460 #ifdef TTURRETS_ENABLED
1461 if (autocvar_g_turrets)
1465 // Precache all player models if desired
1466 if (autocvar_sv_precacheplayermodels)
1468 PrecachePlayerSounds("sound/player/default.sounds");
1469 precache_all_playermodels("models/player/*.zym");
1470 precache_all_playermodels("models/player/*.dpm");
1471 precache_all_playermodels("models/player/*.md3");
1472 precache_all_playermodels("models/player/*.psk");
1473 precache_all_playermodels("models/player/*.iqm");
1476 if (autocvar_sv_defaultcharacter)
1479 s = autocvar_sv_defaultplayermodel_red;
1481 precache_playermodel(s);
1482 s = autocvar_sv_defaultplayermodel_blue;
1484 precache_playermodel(s);
1485 s = autocvar_sv_defaultplayermodel_yellow;
1487 precache_playermodel(s);
1488 s = autocvar_sv_defaultplayermodel_pink;
1490 precache_playermodel(s);
1491 s = autocvar_sv_defaultplayermodel;
1493 precache_playermodel(s);
1498 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1499 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1502 // gore and miscellaneous sounds
1503 //precache_sound ("misc/h2ohit.wav");
1504 precache_model ("models/hook.md3");
1505 precache_sound ("misc/armorimpact.wav");
1506 precache_sound ("misc/bodyimpact1.wav");
1507 precache_sound ("misc/bodyimpact2.wav");
1508 precache_sound ("misc/gib.wav");
1509 precache_sound ("misc/gib_splat01.wav");
1510 precache_sound ("misc/gib_splat02.wav");
1511 precache_sound ("misc/gib_splat03.wav");
1512 precache_sound ("misc/gib_splat04.wav");
1513 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1514 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1515 precache_sound ("misc/null.wav");
1516 precache_sound ("misc/spawn.wav");
1517 precache_sound ("misc/talk.wav");
1518 precache_sound ("misc/teleport.wav");
1519 precache_sound ("misc/poweroff.wav");
1520 precache_sound ("player/lava.wav");
1521 precache_sound ("player/slime.wav");
1523 precache_model ("models/sprites/0.spr32");
1524 precache_model ("models/sprites/1.spr32");
1525 precache_model ("models/sprites/2.spr32");
1526 precache_model ("models/sprites/3.spr32");
1527 precache_model ("models/sprites/4.spr32");
1528 precache_model ("models/sprites/5.spr32");
1529 precache_model ("models/sprites/6.spr32");
1530 precache_model ("models/sprites/7.spr32");
1531 precache_model ("models/sprites/8.spr32");
1532 precache_model ("models/sprites/9.spr32");
1533 precache_model ("models/sprites/10.spr32");
1535 // common weapon precaches
1536 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1537 precache_sound ("weapons/weapon_switch.wav");
1538 precache_sound ("weapons/weaponpickup.wav");
1539 precache_sound ("weapons/unavailable.wav");
1540 precache_sound ("weapons/dryfire.wav");
1541 if (g_grappling_hook)
1543 precache_sound ("weapons/hook_fire.wav"); // hook
1544 precache_sound ("weapons/hook_impact.wav"); // hook
1547 if(autocvar_sv_precacheweapons)
1549 //precache weapon models/sounds
1552 while (wep <= WEP_LAST)
1554 weapon_action(wep, WR_PRECACHE);
1559 precache_model("models/elaser.mdl");
1560 precache_model("models/laser.mdl");
1561 precache_model("models/ebomb.mdl");
1564 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1566 if (!self.noise && self.music) // quake 3 uses the music field
1567 self.noise = self.music;
1569 // plays music for the level if there is any
1572 precache_sound (self.noise);
1573 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1578 // WARNING: this kills the trace globals
1579 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1580 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1582 #define INITPRIO_FIRST 0
1583 #define INITPRIO_GAMETYPE 0
1584 #define INITPRIO_GAMETYPE_FALLBACK 1
1585 #define INITPRIO_FINDTARGET 10
1586 #define INITPRIO_DROPTOFLOOR 20
1587 #define INITPRIO_SETLOCATION 90
1588 #define INITPRIO_LINKDOORS 91
1589 #define INITPRIO_LAST 99
1591 .void(void) initialize_entity;
1592 .float initialize_entity_order;
1593 .entity initialize_entity_next;
1594 entity initialize_entity_first;
1596 void make_safe_for_remove(entity e)
1598 if (e.initialize_entity)
1600 entity ent, prev = world;
1601 for (ent = initialize_entity_first; ent; )
1603 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1605 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1606 // skip it in linked list
1609 prev.initialize_entity_next = ent.initialize_entity_next;
1610 ent = prev.initialize_entity_next;
1614 initialize_entity_first = ent.initialize_entity_next;
1615 ent = initialize_entity_first;
1621 ent = ent.initialize_entity_next;
1627 void objerror(string s)
1629 make_safe_for_remove(self);
1630 builtin_objerror(s);
1633 .float remove_except_protected_forbidden;
1634 void remove_except_protected(entity e)
1636 if(e.remove_except_protected_forbidden)
1637 error("not allowed to remove this at this point");
1641 void remove_unsafely(entity e)
1643 if(e.classname == "spike")
1644 error("Removing spikes is forbidden (crylink bug), please report");
1648 void remove_safely(entity e)
1650 make_safe_for_remove(e);
1654 void InitializeEntity(entity e, void(void) func, float order)
1658 if (!e || e.initialize_entity)
1660 // make a proxy initializer entity
1664 e.classname = "initialize_entity";
1668 e.initialize_entity = func;
1669 e.initialize_entity_order = order;
1671 cur = initialize_entity_first;
1675 if (!cur || cur.initialize_entity_order > order)
1677 // insert between prev and cur
1679 prev.initialize_entity_next = e;
1681 initialize_entity_first = e;
1682 e.initialize_entity_next = cur;
1686 cur = cur.initialize_entity_next;
1689 void InitializeEntitiesRun()
1692 startoflist = initialize_entity_first;
1693 initialize_entity_first = world;
1694 remove = remove_except_protected;
1695 for (self = startoflist; self; self = self.initialize_entity_next)
1697 self.remove_except_protected_forbidden = 1;
1699 for (self = startoflist; self; )
1702 var void(void) func;
1703 e = self.initialize_entity_next;
1704 func = self.initialize_entity;
1705 self.initialize_entity_order = 0;
1706 self.initialize_entity = func_null;
1707 self.initialize_entity_next = world;
1708 self.remove_except_protected_forbidden = 0;
1709 if (self.classname == "initialize_entity")
1713 builtin_remove(self);
1716 //dprint("Delayed initialization: ", self.classname, "\n");
1722 backtrace(strcat("Null function in: ", self.classname, "\n"));
1726 remove = remove_unsafely;
1729 .float uncustomizeentityforclient_set;
1730 .void(void) uncustomizeentityforclient;
1731 void UncustomizeEntitiesRun()
1735 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1736 self.uncustomizeentityforclient();
1739 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1741 e.customizeentityforclient = customizer;
1742 e.uncustomizeentityforclient = uncustomizer;
1743 e.uncustomizeentityforclient_set = !!uncustomizer;
1747 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1750 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1754 if (e.classname == "")
1755 e.classname = "net_linked";
1757 if (e.model == "" || self.modelindex == 0)
1761 setmodel(e, "null");
1765 e.SendEntity = sendfunc;
1766 e.SendFlags = 0xFFFFFF;
1769 e.effects |= EF_NODEPTHTEST;
1773 e.nextthink = time + dt;
1774 e.think = SUB_Remove;
1778 void adaptor_think2touch()
1787 void adaptor_think2use()
1799 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1801 if not(self.flags & FL_ONGROUND) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING
1802 self.projectiledeathtype |= HITTYPE_SPLASH;
1803 adaptor_think2use();
1806 // deferred dropping
1807 void DropToFloor_Handler()
1809 builtin_droptofloor();
1810 self.dropped_origin = self.origin;
1815 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1820 float trace_hits_box_a0, trace_hits_box_a1;
1822 float trace_hits_box_1d(float end, float thmi, float thma)
1826 // just check if x is in range
1834 // do the trace with respect to x
1835 // 0 -> end has to stay in thmi -> thma
1836 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1837 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1838 if (trace_hits_box_a0 > trace_hits_box_a1)
1844 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1849 // now it is a trace from 0 to end
1851 trace_hits_box_a0 = 0;
1852 trace_hits_box_a1 = 1;
1854 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1856 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1858 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1864 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1866 return trace_hits_box(start, end, thmi - ma, thma - mi);
1869 float SUB_NoImpactCheck()
1871 // zero hitcontents = this is not the real impact, but either the
1872 // mirror-impact of something hitting the projectile instead of the
1873 // projectile hitting the something, or a touchareagrid one. Neither of
1874 // these stop the projectile from moving, so...
1875 if(trace_dphitcontents == 0)
1877 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1878 dprint(sprintf(_("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Profectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.origin)));
1881 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1883 if (other == world && self.size != '0 0 0')
1886 tic = self.velocity * sys_frametime;
1887 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1888 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1889 if (trace_fraction >= 1)
1891 dprint("Odd... did not hit...?\n");
1893 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1895 dprint("Detected and prevented the sky-grapple bug.\n");
1903 #define SUB_OwnerCheck() (other && (other == self.owner))
1905 void RemoveGrapplingHook(entity pl);
1906 void W_Crylink_Dequeue(entity e);
1907 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1909 if(SUB_OwnerCheck())
1911 if(SUB_NoImpactCheck())
1913 if(self.classname == "grapplinghook")
1914 RemoveGrapplingHook(self.realowner);
1915 else if(self.classname == "spike")
1917 W_Crylink_Dequeue(self);
1924 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1925 UpdateCSQCProjectile(self);
1928 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1930 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1931 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1933 void URI_Get_Callback(float id, float status, string data)
1935 if(url_URI_Get_Callback(id, status, data))
1939 else if (id == URI_GET_DISCARD)
1943 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1946 Curl_URI_Get_Callback(id, status, data);
1948 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1951 OnlineBanList_URI_Get_Callback(id, status, data);
1955 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1959 string uid2name(string myuid) {
1961 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1963 // FIXME remove this later after 0.6 release
1964 // convert old style broken records to correct style
1967 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1970 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1971 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1976 s = "^1Unregistered Player";
1980 float race_readTime(string map, float pos)
1988 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1991 string race_readUID(string map, float pos)
1999 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2002 float race_readPos(string map, float t) {
2004 for (i = 1; i <= RANKINGS_CNT; ++i)
2005 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2008 return 0; // pos is zero if unranked
2011 void race_writeTime(string map, float t, string myuid)
2020 newpos = race_readPos(map, t);
2022 float i, prevpos = 0;
2023 for(i = 1; i <= RANKINGS_CNT; ++i)
2025 if(race_readUID(map, i) == myuid)
2028 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2029 for (i = prevpos; i > newpos; --i) {
2030 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2031 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2033 } else { // player has no ranked record yet
2034 for (i = RANKINGS_CNT; i > newpos; --i) {
2035 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2036 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2040 // store new time itself
2041 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2042 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2045 string race_readName(string map, float pos)
2053 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2056 string race_placeName(float pos) {
2057 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2059 if(mod(pos, 10) == 1)
2060 return strcat(ftos(pos), "st");
2061 else if(mod(pos, 10) == 2)
2062 return strcat(ftos(pos), "nd");
2063 else if(mod(pos, 10) == 3)
2064 return strcat(ftos(pos), "rd");
2066 return strcat(ftos(pos), "th");
2069 return strcat(ftos(pos), "th");
2072 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2075 vector start, org, delta, end, enddown, mstart;
2078 m = e.dphitcontentsmask;
2079 e.dphitcontentsmask = goodcontents | badcontents;
2082 delta = world.maxs - world.mins;
2086 for (i = 0; i < attempts; ++i)
2088 start_x = org_x + random() * delta_x;
2089 start_y = org_y + random() * delta_y;
2090 start_z = org_z + random() * delta_z;
2092 // rule 1: start inside world bounds, and outside
2093 // solid, and don't start from somewhere where you can
2094 // fall down to evil
2095 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2096 if (trace_fraction >= 1)
2098 if (trace_startsolid)
2100 if (trace_dphitcontents & badcontents)
2102 if (trace_dphitq3surfaceflags & badsurfaceflags)
2105 // rule 2: if we are too high, lower the point
2106 if (trace_fraction * delta_z > maxaboveground)
2107 start = trace_endpos + '0 0 1' * maxaboveground;
2108 enddown = trace_endpos;
2110 // rule 3: make sure we aren't outside the map. This only works
2111 // for somewhat well formed maps. A good rule of thumb is that
2112 // the map should have a convex outside hull.
2113 // these can be traceLINES as we already verified the starting box
2114 mstart = start + 0.5 * (e.mins + e.maxs);
2115 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2116 if (trace_fraction >= 1)
2118 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2119 if (trace_fraction >= 1)
2121 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2122 if (trace_fraction >= 1)
2124 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2125 if (trace_fraction >= 1)
2127 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2128 if (trace_fraction >= 1)
2131 // rule 4: we must "see" some spawnpoint
2132 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2133 if(checkpvs(mstart, sp))
2137 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2138 if(checkpvs(mstart, sp))
2144 // find a random vector to "look at"
2145 end_x = org_x + random() * delta_x;
2146 end_y = org_y + random() * delta_y;
2147 end_z = org_z + random() * delta_z;
2148 end = start + normalize(end - start) * vlen(delta);
2150 // rule 4: start TO end must not be too short
2151 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2152 if (trace_startsolid)
2154 if (trace_fraction < minviewdistance / vlen(delta))
2157 // rule 5: don't want to look at sky
2158 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2161 // rule 6: we must not end up in trigger_hurt
2162 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2168 e.dphitcontentsmask = m;
2172 setorigin(e, start);
2173 e.angles = vectoangles(end - start);
2174 dprint("Needed ", ftos(i + 1), " attempts\n");
2181 float zcurveparticles_effectno;
2182 vector zcurveparticles_start;
2183 float zcurveparticles_spd;
2185 void endzcurveparticles()
2187 if(zcurveparticles_effectno)
2190 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2192 zcurveparticles_effectno = 0;
2195 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2197 spd = bound(0, floor(spd / 16), 32767);
2198 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2200 endzcurveparticles();
2201 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2202 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2203 WriteShort(MSG_BROADCAST, effectno);
2204 WriteCoord(MSG_BROADCAST, start_x);
2205 WriteCoord(MSG_BROADCAST, start_y);
2206 WriteCoord(MSG_BROADCAST, start_z);
2207 zcurveparticles_effectno = effectno;
2208 zcurveparticles_start = start;
2211 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2212 WriteCoord(MSG_BROADCAST, end_x);
2213 WriteCoord(MSG_BROADCAST, end_y);
2214 WriteCoord(MSG_BROADCAST, end_z);
2215 WriteCoord(MSG_BROADCAST, end_dz);
2216 zcurveparticles_spd = spd;
2219 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2222 vector vecxy, velxy;
2224 vecxy = end - start;
2229 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2231 endzcurveparticles();
2232 trailparticles(world, effectno, start, end);
2236 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2237 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2240 void write_recordmarker(entity pl, float tstart, float dt)
2242 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2244 // also write a marker into demo files for demotc-race-record-extractor to find
2247 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2248 " ", ftos(tstart), " ", ftos(dt), "\n"));
2251 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2264 if(allowcenter) // 2: allow center handedness
2277 if(allowcenter) // 2: allow center handedness
2293 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2298 if (autocvar_g_shootfromeye)
2302 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2303 else { vecs_y = 0; vecs_z -= 2; }
2311 else if (autocvar_g_shootfromcenter)
2316 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2326 else if (autocvar_g_shootfromclient)
2328 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2333 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2335 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2339 void attach_sameorigin(entity e, entity to, string tag)
2341 vector org, t_forward, t_left, t_up, e_forward, e_up;
2344 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2345 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2346 t_forward = v_forward * tagscale;
2347 t_left = v_right * -tagscale;
2348 t_up = v_up * tagscale;
2350 e.origin_x = org * t_forward;
2351 e.origin_y = org * t_left;
2352 e.origin_z = org * t_up;
2354 // current forward and up directions
2355 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2356 e.angles = AnglesTransform_FromVAngles(e.angles);
2358 e.angles = AnglesTransform_FromAngles(e.angles);
2359 fixedmakevectors(e.angles);
2361 // untransform forward, up!
2362 e_forward_x = v_forward * t_forward;
2363 e_forward_y = v_forward * t_left;
2364 e_forward_z = v_forward * t_up;
2365 e_up_x = v_up * t_forward;
2366 e_up_y = v_up * t_left;
2367 e_up_z = v_up * t_up;
2369 e.angles = fixedvectoangles2(e_forward, e_up);
2370 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2371 e.angles = AnglesTransform_ToVAngles(e.angles);
2373 e.angles = AnglesTransform_ToAngles(e.angles);
2375 setattachment(e, to, tag);
2376 setorigin(e, e.origin);
2379 void detach_sameorigin(entity e)
2382 org = gettaginfo(e, 0);
2383 e.angles = fixedvectoangles2(v_forward, v_up);
2384 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2385 e.angles = AnglesTransform_ToVAngles(e.angles);
2387 e.angles = AnglesTransform_ToAngles(e.angles);
2389 setattachment(e, world, "");
2390 setorigin(e, e.origin);
2393 void follow_sameorigin(entity e, entity to)
2395 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2396 e.aiment = to; // make the hole follow bmodel
2397 e.punchangle = to.angles; // the original angles of bmodel
2398 e.view_ofs = e.origin - to.origin; // relative origin
2399 e.v_angle = e.angles - to.angles; // relative angles
2402 void unfollow_sameorigin(entity e)
2404 e.movetype = MOVETYPE_NONE;
2407 entity gettaginfo_relative_ent;
2408 vector gettaginfo_relative(entity e, float tag)
2410 if (!gettaginfo_relative_ent)
2412 gettaginfo_relative_ent = spawn();
2413 gettaginfo_relative_ent.effects = EF_NODRAW;
2415 gettaginfo_relative_ent.model = e.model;
2416 gettaginfo_relative_ent.modelindex = e.modelindex;
2417 gettaginfo_relative_ent.frame = e.frame;
2418 return gettaginfo(gettaginfo_relative_ent, tag);
2423 float modeleffect_SendEntity(entity to, float sf)
2426 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2429 if(self.velocity != '0 0 0')
2431 if(self.angles != '0 0 0')
2433 if(self.avelocity != '0 0 0')
2436 WriteByte(MSG_ENTITY, f);
2437 WriteShort(MSG_ENTITY, self.modelindex);
2438 WriteByte(MSG_ENTITY, self.skin);
2439 WriteByte(MSG_ENTITY, self.frame);
2440 WriteCoord(MSG_ENTITY, self.origin_x);
2441 WriteCoord(MSG_ENTITY, self.origin_y);
2442 WriteCoord(MSG_ENTITY, self.origin_z);
2445 WriteCoord(MSG_ENTITY, self.velocity_x);
2446 WriteCoord(MSG_ENTITY, self.velocity_y);
2447 WriteCoord(MSG_ENTITY, self.velocity_z);
2451 WriteCoord(MSG_ENTITY, self.angles_x);
2452 WriteCoord(MSG_ENTITY, self.angles_y);
2453 WriteCoord(MSG_ENTITY, self.angles_z);
2457 WriteCoord(MSG_ENTITY, self.avelocity_x);
2458 WriteCoord(MSG_ENTITY, self.avelocity_y);
2459 WriteCoord(MSG_ENTITY, self.avelocity_z);
2461 WriteShort(MSG_ENTITY, self.scale * 256.0);
2462 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2463 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2464 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2465 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2470 void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
2475 e.classname = "modeleffect";
2483 e.teleport_time = t1;
2487 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2491 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2494 sz = max(e.scale, e.scale2);
2495 setsize(e, e.mins * sz, e.maxs * sz);
2496 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2499 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2501 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2504 float randombit(float bits)
2506 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2515 for(f = 1; f <= bits; f *= 2)
2524 r = (r - 1) / (n - 1);
2531 float randombits(float bits, float k, float error_return)
2535 while(k > 0 && bits != r)
2537 r += randombit(bits - r);
2546 void randombit_test(float bits, float iter)
2550 print(ftos(randombit(bits)), "\n");
2555 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2557 if(halflifedist > 0)
2558 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2559 else if(halflifedist < 0)
2560 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2569 #define cvar_string_normal builtin_cvar_string
2570 #define cvar_normal builtin_cvar
2572 string cvar_string_normal(string n)
2574 if not(cvar_type(n) & 1)
2575 backtrace(strcat("Attempt to access undefined cvar: ", n));
2576 return builtin_cvar_string(n);
2579 float cvar_normal(string n)
2581 return stof(cvar_string_normal(n));
2584 #define cvar_set_normal builtin_cvar_set
2592 oself.think = SUB_Remove;
2593 oself.nextthink = time;
2599 Execute func() after time + fdelay.
2600 self when func is executed = self when defer is called
2602 void defer(float fdelay, void() func)
2609 e.think = defer_think;
2610 e.nextthink = time + fdelay;
2613 .string aiment_classname;
2614 .float aiment_deadflag;
2615 void SetMovetypeFollow(entity ent, entity e)
2617 // FIXME this may not be warpzone aware
2618 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2619 ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
2620 ent.aiment = e; // make the hole follow bmodel
2621 ent.punchangle = e.angles; // the original angles of bmodel
2622 ent.view_ofs = ent.origin - e.origin; // relative origin
2623 ent.v_angle = ent.angles - e.angles; // relative angles
2624 ent.aiment_classname = strzone(e.classname);
2625 ent.aiment_deadflag = e.deadflag;
2627 void UnsetMovetypeFollow(entity ent)
2629 ent.movetype = MOVETYPE_FLY;
2630 PROJECTILE_MAKETRIGGER(ent);
2633 float LostMovetypeFollow(entity ent)
2636 if(ent.movetype != MOVETYPE_FOLLOW)
2642 if(ent.aiment.classname != ent.aiment_classname)
2644 if(ent.aiment.deadflag != ent.aiment_deadflag)
2650 float isPushable(entity e)
2659 case "droppedweapon":
2660 case "keepawayball":
2661 case "nexball_basketball":
2662 case "nexball_football":
2664 case "bullet": // antilagged bullets can't hit this either
2667 if (e.projectiledeathtype)
2672 void dedicated_print(string input) // print(), but only print if the server is not local
2674 if not(server_is_local) { print(input); }