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);
1046 if(cvar("g_invincible_projectiles"))
1047 MUTATOR_ADD(mutator_invincibleprojectiles);
1048 if(cvar("g_new_toys"))
1049 MUTATOR_ADD(mutator_new_toys);
1051 MUTATOR_ADD(mutator_nix);
1052 if(cvar("g_rocket_flying"))
1053 MUTATOR_ADD(mutator_rocketflying);
1054 if(cvar("g_vampire"))
1055 MUTATOR_ADD(mutator_vampire);
1056 if(cvar("g_superspectate"))
1057 MUTATOR_ADD(mutator_superspec);
1060 // is this a mutator? is this a mode?
1061 if(cvar("g_sandbox"))
1062 MUTATOR_ADD(sandbox);
1064 if(cvar("sv_allow_fullbright"))
1065 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1067 g_bugrigs = cvar("g_bugrigs");
1068 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1069 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1070 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1071 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1072 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1073 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1074 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1075 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1076 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1077 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1078 g_bugrigs_accel = cvar("g_bugrigs_accel");
1079 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1080 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1081 g_bugrigs_steer = cvar("g_bugrigs_steer");
1083 g_touchexplode = cvar("g_touchexplode");
1084 g_touchexplode_radius = cvar("g_touchexplode_radius");
1085 g_touchexplode_damage = cvar("g_touchexplode_damage");
1086 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1087 g_touchexplode_force = cvar("g_touchexplode_force");
1089 #ifdef ALLOW_FORCEMODELS
1090 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1093 sv_clones = cvar("sv_clones");
1094 sv_foginterval = cvar("sv_foginterval");
1095 g_cloaked = cvar("g_cloaked");
1097 g_cloaked = 1; // always enable cloak in CTS
1098 g_jump_grunt = cvar("g_jump_grunt");
1099 g_footsteps = cvar("g_footsteps");
1100 g_grappling_hook = cvar("g_grappling_hook");
1101 g_jetpack = cvar("g_jetpack");
1102 g_midair = cvar("g_midair");
1103 g_norecoil = cvar("g_norecoil");
1104 g_bloodloss = cvar("g_bloodloss");
1105 sv_maxidle = cvar("sv_maxidle");
1106 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1107 sv_autotaunt = cvar("sv_autotaunt");
1108 sv_taunt = cvar("sv_taunt");
1110 inWarmupStage = cvar("g_warmup");
1111 g_warmup_limit = cvar("g_warmup_limit");
1112 g_warmup_allguns = cvar("g_warmup_allguns");
1113 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1115 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1116 inWarmupStage = 0; // these modes cannot work together, sorry
1118 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1119 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1120 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1121 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1122 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1123 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1124 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1125 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1126 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1127 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1128 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1129 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1130 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1131 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1133 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1134 g_weaponratefactor = cvar("g_weaponratefactor");
1135 g_weapondamagefactor = cvar("g_weapondamagefactor");
1136 g_weaponforcefactor = cvar("g_weaponforcefactor");
1137 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1139 g_pickup_shells = cvar("g_pickup_shells");
1140 g_pickup_shells_max = cvar("g_pickup_shells_max");
1141 g_pickup_nails = cvar("g_pickup_nails");
1142 g_pickup_nails_max = cvar("g_pickup_nails_max");
1143 g_pickup_rockets = cvar("g_pickup_rockets");
1144 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1145 g_pickup_cells = cvar("g_pickup_cells");
1146 g_pickup_cells_max = cvar("g_pickup_cells_max");
1147 g_pickup_fuel = cvar("g_pickup_fuel");
1148 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1149 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1150 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1151 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1152 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1153 g_pickup_armormedium = cvar("g_pickup_armormedium");
1154 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1155 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1156 g_pickup_armorbig = cvar("g_pickup_armorbig");
1157 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1158 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1159 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1160 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1161 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1162 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1163 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1164 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1165 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1166 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1167 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1168 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1169 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1170 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1171 g_pickup_healthmega = cvar("g_pickup_healthmega");
1172 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1173 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1175 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1176 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1178 g_pinata = cvar("g_pinata");
1180 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1182 g_weapon_stay = cvar("g_weapon_stay");
1184 if not(inWarmupStage && !g_ca)
1185 game_starttime = cvar("g_start_delay");
1187 sv_pitch_min = cvar("sv_pitch_min");
1188 sv_pitch_max = cvar("sv_pitch_max");
1189 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1191 readplayerstartcvars();
1197 string precache_sound (string s) = #19;
1198 float precache_sound_index (string s) = #19;
1200 #define SND_VOLUME 1
1201 #define SND_ATTENUATION 2
1202 #define SND_LARGEENTITY 8
1203 #define SND_LARGESOUND 16
1205 float sound_allowed(float dest, entity e)
1207 // sounds from world may always pass
1210 if (e.classname == "body")
1212 else if (e.realowner && e.realowner != e)
1214 else if (e.owner && e.owner != e)
1219 // sounds to self may always pass
1220 if (dest == MSG_ONE)
1221 if (e == msg_entity)
1223 // sounds by players can be removed
1224 if (autocvar_bot_sound_monopoly)
1225 if (clienttype(e) == CLIENTTYPE_REAL)
1227 // anything else may pass
1231 #ifdef COMPAT_XON010_CHANNELS
1232 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1233 void sound(entity e, float chan, string samp, float vol, float atten)
1235 if (!sound_allowed(MSG_BROADCAST, e))
1237 builtin_sound(e, chan, samp, vol, atten);
1241 void sound(entity e, float chan, string samp, float vol, float atten)
1243 if (!sound_allowed(MSG_BROADCAST, e))
1245 sound7(e, chan, samp, vol, atten, 0, 0);
1249 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1253 if (!sound_allowed(dest, e))
1256 entno = num_for_edict(e);
1257 idx = precache_sound_index(samp);
1262 atten = floor(atten * 64);
1263 vol = floor(vol * 255);
1266 sflags |= SND_VOLUME;
1268 sflags |= SND_ATTENUATION;
1269 if (entno >= 8192 || chan < 0 || chan > 7)
1270 sflags |= SND_LARGEENTITY;
1272 sflags |= SND_LARGESOUND;
1274 WriteByte(dest, SVC_SOUND);
1275 WriteByte(dest, sflags);
1276 if (sflags & SND_VOLUME)
1277 WriteByte(dest, vol);
1278 if (sflags & SND_ATTENUATION)
1279 WriteByte(dest, atten);
1280 if (sflags & SND_LARGEENTITY)
1282 WriteShort(dest, entno);
1283 WriteByte(dest, chan);
1287 WriteShort(dest, entno * 8 + chan);
1289 if (sflags & SND_LARGESOUND)
1290 WriteShort(dest, idx);
1292 WriteByte(dest, idx);
1294 WriteCoord(dest, o_x);
1295 WriteCoord(dest, o_y);
1296 WriteCoord(dest, o_z);
1298 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1302 if (!sound_allowed(dest, e))
1305 o = e.origin + 0.5 * (e.mins + e.maxs);
1306 soundtoat(dest, e, o, chan, samp, vol, atten);
1308 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1310 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1312 void stopsoundto(float dest, entity e, float chan)
1316 if (!sound_allowed(dest, e))
1319 entno = num_for_edict(e);
1321 if (entno >= 8192 || chan < 0 || chan > 7)
1324 idx = precache_sound_index("misc/null.wav");
1325 sflags = SND_LARGEENTITY;
1327 sflags |= SND_LARGESOUND;
1328 WriteByte(dest, SVC_SOUND);
1329 WriteByte(dest, sflags);
1330 WriteShort(dest, entno);
1331 WriteByte(dest, chan);
1332 if (sflags & SND_LARGESOUND)
1333 WriteShort(dest, idx);
1335 WriteByte(dest, idx);
1336 WriteCoord(dest, e.origin_x);
1337 WriteCoord(dest, e.origin_y);
1338 WriteCoord(dest, e.origin_z);
1342 WriteByte(dest, SVC_STOPSOUND);
1343 WriteShort(dest, entno * 8 + chan);
1346 void stopsound(entity e, float chan)
1348 if (!sound_allowed(MSG_BROADCAST, e))
1351 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1352 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1355 void play2(entity e, string filename)
1357 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1359 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1362 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1364 float spamsound(entity e, float chan, string samp, float vol, float atten)
1366 if (!sound_allowed(MSG_BROADCAST, e))
1369 if (time > e.spamtime)
1372 sound(e, chan, samp, vol, atten);
1378 void play2team(float t, string filename)
1382 if (autocvar_bot_sound_monopoly)
1385 FOR_EACH_REALPLAYER(head)
1388 play2(head, filename);
1392 void play2all(string samp)
1394 if (autocvar_bot_sound_monopoly)
1397 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1400 void PrecachePlayerSounds(string f);
1401 void precache_playermodel(string m)
1403 float globhandle, i, n;
1406 if(substring(m, -9,5) == "_lod1")
1408 if(substring(m, -9,5) == "_lod2")
1411 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1414 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1418 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1421 n = search_getsize(globhandle);
1422 for (i = 0; i < n; ++i)
1424 //print(search_getfilename(globhandle, i), "\n");
1425 f = search_getfilename(globhandle, i);
1426 PrecachePlayerSounds(f);
1428 search_end(globhandle);
1430 void precache_all_playermodels(string pattern)
1432 float globhandle, i, n;
1435 globhandle = search_begin(pattern, TRUE, FALSE);
1438 n = search_getsize(globhandle);
1439 for (i = 0; i < n; ++i)
1441 //print(search_getfilename(globhandle, i), "\n");
1442 f = search_getfilename(globhandle, i);
1443 precache_playermodel(f);
1445 search_end(globhandle);
1450 // gamemode related things
1451 precache_model ("models/misc/chatbubble.spr");
1454 precache_model ("models/runematch/curse.mdl");
1455 precache_model ("models/runematch/rune.mdl");
1458 #ifdef TTURRETS_ENABLED
1459 if (autocvar_g_turrets)
1463 // Precache all player models if desired
1464 if (autocvar_sv_precacheplayermodels)
1466 PrecachePlayerSounds("sound/player/default.sounds");
1467 precache_all_playermodels("models/player/*.zym");
1468 precache_all_playermodels("models/player/*.dpm");
1469 precache_all_playermodels("models/player/*.md3");
1470 precache_all_playermodels("models/player/*.psk");
1471 precache_all_playermodels("models/player/*.iqm");
1474 if (autocvar_sv_defaultcharacter)
1477 s = autocvar_sv_defaultplayermodel_red;
1479 precache_playermodel(s);
1480 s = autocvar_sv_defaultplayermodel_blue;
1482 precache_playermodel(s);
1483 s = autocvar_sv_defaultplayermodel_yellow;
1485 precache_playermodel(s);
1486 s = autocvar_sv_defaultplayermodel_pink;
1488 precache_playermodel(s);
1489 s = autocvar_sv_defaultplayermodel;
1491 precache_playermodel(s);
1496 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1497 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1500 // gore and miscellaneous sounds
1501 //precache_sound ("misc/h2ohit.wav");
1502 precache_model ("models/hook.md3");
1503 precache_sound ("misc/armorimpact.wav");
1504 precache_sound ("misc/bodyimpact1.wav");
1505 precache_sound ("misc/bodyimpact2.wav");
1506 precache_sound ("misc/gib.wav");
1507 precache_sound ("misc/gib_splat01.wav");
1508 precache_sound ("misc/gib_splat02.wav");
1509 precache_sound ("misc/gib_splat03.wav");
1510 precache_sound ("misc/gib_splat04.wav");
1511 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1512 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1513 precache_sound ("misc/null.wav");
1514 precache_sound ("misc/spawn.wav");
1515 precache_sound ("misc/talk.wav");
1516 precache_sound ("misc/teleport.wav");
1517 precache_sound ("misc/poweroff.wav");
1518 precache_sound ("player/lava.wav");
1519 precache_sound ("player/slime.wav");
1521 precache_model ("models/sprites/0.spr32");
1522 precache_model ("models/sprites/1.spr32");
1523 precache_model ("models/sprites/2.spr32");
1524 precache_model ("models/sprites/3.spr32");
1525 precache_model ("models/sprites/4.spr32");
1526 precache_model ("models/sprites/5.spr32");
1527 precache_model ("models/sprites/6.spr32");
1528 precache_model ("models/sprites/7.spr32");
1529 precache_model ("models/sprites/8.spr32");
1530 precache_model ("models/sprites/9.spr32");
1531 precache_model ("models/sprites/10.spr32");
1533 // common weapon precaches
1534 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1535 precache_sound ("weapons/weapon_switch.wav");
1536 precache_sound ("weapons/weaponpickup.wav");
1537 precache_sound ("weapons/unavailable.wav");
1538 precache_sound ("weapons/dryfire.wav");
1539 if (g_grappling_hook)
1541 precache_sound ("weapons/hook_fire.wav"); // hook
1542 precache_sound ("weapons/hook_impact.wav"); // hook
1545 if(autocvar_sv_precacheweapons)
1547 //precache weapon models/sounds
1550 while (wep <= WEP_LAST)
1552 weapon_action(wep, WR_PRECACHE);
1557 precache_model("models/elaser.mdl");
1558 precache_model("models/laser.mdl");
1559 precache_model("models/ebomb.mdl");
1562 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1564 if (!self.noise && self.music) // quake 3 uses the music field
1565 self.noise = self.music;
1567 // plays music for the level if there is any
1570 precache_sound (self.noise);
1571 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1576 // WARNING: this kills the trace globals
1577 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1578 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1580 #define INITPRIO_FIRST 0
1581 #define INITPRIO_GAMETYPE 0
1582 #define INITPRIO_GAMETYPE_FALLBACK 1
1583 #define INITPRIO_FINDTARGET 10
1584 #define INITPRIO_DROPTOFLOOR 20
1585 #define INITPRIO_SETLOCATION 90
1586 #define INITPRIO_LINKDOORS 91
1587 #define INITPRIO_LAST 99
1589 .void(void) initialize_entity;
1590 .float initialize_entity_order;
1591 .entity initialize_entity_next;
1592 entity initialize_entity_first;
1594 void make_safe_for_remove(entity e)
1596 if (e.initialize_entity)
1598 entity ent, prev = world;
1599 for (ent = initialize_entity_first; ent; )
1601 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1603 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1604 // skip it in linked list
1607 prev.initialize_entity_next = ent.initialize_entity_next;
1608 ent = prev.initialize_entity_next;
1612 initialize_entity_first = ent.initialize_entity_next;
1613 ent = initialize_entity_first;
1619 ent = ent.initialize_entity_next;
1625 void objerror(string s)
1627 make_safe_for_remove(self);
1628 builtin_objerror(s);
1631 .float remove_except_protected_forbidden;
1632 void remove_except_protected(entity e)
1634 if(e.remove_except_protected_forbidden)
1635 error("not allowed to remove this at this point");
1639 void remove_unsafely(entity e)
1641 if(e.classname == "spike")
1642 error("Removing spikes is forbidden (crylink bug), please report");
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;
1673 if (!cur || cur.initialize_entity_order > order)
1675 // insert between prev and cur
1677 prev.initialize_entity_next = e;
1679 initialize_entity_first = e;
1680 e.initialize_entity_next = cur;
1684 cur = cur.initialize_entity_next;
1687 void InitializeEntitiesRun()
1690 startoflist = initialize_entity_first;
1691 initialize_entity_first = world;
1692 remove = remove_except_protected;
1693 for (self = startoflist; self; self = self.initialize_entity_next)
1695 self.remove_except_protected_forbidden = 1;
1697 for (self = startoflist; self; )
1700 var void(void) func;
1701 e = self.initialize_entity_next;
1702 func = self.initialize_entity;
1703 self.initialize_entity_order = 0;
1704 self.initialize_entity = func_null;
1705 self.initialize_entity_next = world;
1706 self.remove_except_protected_forbidden = 0;
1707 if (self.classname == "initialize_entity")
1711 builtin_remove(self);
1714 //dprint("Delayed initialization: ", self.classname, "\n");
1720 backtrace(strcat("Null function in: ", self.classname, "\n"));
1724 remove = remove_unsafely;
1727 .float uncustomizeentityforclient_set;
1728 .void(void) uncustomizeentityforclient;
1729 void UncustomizeEntitiesRun()
1733 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1734 self.uncustomizeentityforclient();
1737 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1739 e.customizeentityforclient = customizer;
1740 e.uncustomizeentityforclient = uncustomizer;
1741 e.uncustomizeentityforclient_set = !!uncustomizer;
1745 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1748 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1752 if (e.classname == "")
1753 e.classname = "net_linked";
1755 if (e.model == "" || self.modelindex == 0)
1759 setmodel(e, "null");
1763 e.SendEntity = sendfunc;
1764 e.SendFlags = 0xFFFFFF;
1767 e.effects |= EF_NODEPTHTEST;
1771 e.nextthink = time + dt;
1772 e.think = SUB_Remove;
1776 void adaptor_think2touch()
1785 void adaptor_think2use()
1797 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1799 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
1800 self.projectiledeathtype |= HITTYPE_SPLASH;
1801 adaptor_think2use();
1804 // deferred dropping
1805 void DropToFloor_Handler()
1807 builtin_droptofloor();
1808 self.dropped_origin = self.origin;
1813 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1818 float trace_hits_box_a0, trace_hits_box_a1;
1820 float trace_hits_box_1d(float end, float thmi, float thma)
1824 // just check if x is in range
1832 // do the trace with respect to x
1833 // 0 -> end has to stay in thmi -> thma
1834 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1835 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1836 if (trace_hits_box_a0 > trace_hits_box_a1)
1842 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1847 // now it is a trace from 0 to end
1849 trace_hits_box_a0 = 0;
1850 trace_hits_box_a1 = 1;
1852 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1854 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1856 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1862 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1864 return trace_hits_box(start, end, thmi - ma, thma - mi);
1867 float SUB_NoImpactCheck()
1869 // zero hitcontents = this is not the real impact, but either the
1870 // mirror-impact of something hitting the projectile instead of the
1871 // projectile hitting the something, or a touchareagrid one. Neither of
1872 // these stop the projectile from moving, so...
1873 if(trace_dphitcontents == 0)
1875 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1876 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)));
1879 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1881 if (other == world && self.size != '0 0 0')
1884 tic = self.velocity * sys_frametime;
1885 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1886 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1887 if (trace_fraction >= 1)
1889 dprint("Odd... did not hit...?\n");
1891 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1893 dprint("Detected and prevented the sky-grapple bug.\n");
1901 #define SUB_OwnerCheck() (other && (other == self.owner))
1903 void RemoveGrapplingHook(entity pl);
1904 void W_Crylink_Dequeue(entity e);
1905 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1907 if(SUB_OwnerCheck())
1909 if(SUB_NoImpactCheck())
1911 if(self.classname == "grapplinghook")
1912 RemoveGrapplingHook(self.realowner);
1913 else if(self.classname == "spike")
1915 W_Crylink_Dequeue(self);
1922 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1923 UpdateCSQCProjectile(self);
1926 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1928 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1929 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1931 void URI_Get_Callback(float id, float status, string data)
1933 if(url_URI_Get_Callback(id, status, data))
1937 else if (id == URI_GET_DISCARD)
1941 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1944 Curl_URI_Get_Callback(id, status, data);
1946 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1949 OnlineBanList_URI_Get_Callback(id, status, data);
1953 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1957 string uid2name(string myuid) {
1959 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1961 // FIXME remove this later after 0.6 release
1962 // convert old style broken records to correct style
1965 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1968 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1969 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1974 s = "^1Unregistered Player";
1978 float race_readTime(string map, float pos)
1986 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1989 string race_readUID(string map, float pos)
1997 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2000 float race_readPos(string map, float t) {
2002 for (i = 1; i <= RANKINGS_CNT; ++i)
2003 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2006 return 0; // pos is zero if unranked
2009 void race_writeTime(string map, float t, string myuid)
2018 newpos = race_readPos(map, t);
2020 float i, prevpos = 0;
2021 for(i = 1; i <= RANKINGS_CNT; ++i)
2023 if(race_readUID(map, i) == myuid)
2026 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2027 for (i = prevpos; i > newpos; --i) {
2028 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2029 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2031 } else { // player has no ranked record yet
2032 for (i = RANKINGS_CNT; i > newpos; --i) {
2033 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2034 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2038 // store new time itself
2039 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2040 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2043 string race_readName(string map, float pos)
2051 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2054 string race_placeName(float pos) {
2055 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2057 if(mod(pos, 10) == 1)
2058 return strcat(ftos(pos), "st");
2059 else if(mod(pos, 10) == 2)
2060 return strcat(ftos(pos), "nd");
2061 else if(mod(pos, 10) == 3)
2062 return strcat(ftos(pos), "rd");
2064 return strcat(ftos(pos), "th");
2067 return strcat(ftos(pos), "th");
2070 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2073 vector start, org, delta, end, enddown, mstart;
2076 m = e.dphitcontentsmask;
2077 e.dphitcontentsmask = goodcontents | badcontents;
2080 delta = world.maxs - world.mins;
2084 for (i = 0; i < attempts; ++i)
2086 start_x = org_x + random() * delta_x;
2087 start_y = org_y + random() * delta_y;
2088 start_z = org_z + random() * delta_z;
2090 // rule 1: start inside world bounds, and outside
2091 // solid, and don't start from somewhere where you can
2092 // fall down to evil
2093 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2094 if (trace_fraction >= 1)
2096 if (trace_startsolid)
2098 if (trace_dphitcontents & badcontents)
2100 if (trace_dphitq3surfaceflags & badsurfaceflags)
2103 // rule 2: if we are too high, lower the point
2104 if (trace_fraction * delta_z > maxaboveground)
2105 start = trace_endpos + '0 0 1' * maxaboveground;
2106 enddown = trace_endpos;
2108 // rule 3: make sure we aren't outside the map. This only works
2109 // for somewhat well formed maps. A good rule of thumb is that
2110 // the map should have a convex outside hull.
2111 // these can be traceLINES as we already verified the starting box
2112 mstart = start + 0.5 * (e.mins + e.maxs);
2113 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2114 if (trace_fraction >= 1)
2116 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2117 if (trace_fraction >= 1)
2119 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2120 if (trace_fraction >= 1)
2122 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2123 if (trace_fraction >= 1)
2125 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2126 if (trace_fraction >= 1)
2129 // rule 4: we must "see" some spawnpoint
2130 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2131 if(checkpvs(mstart, sp))
2135 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2136 if(checkpvs(mstart, sp))
2142 // find a random vector to "look at"
2143 end_x = org_x + random() * delta_x;
2144 end_y = org_y + random() * delta_y;
2145 end_z = org_z + random() * delta_z;
2146 end = start + normalize(end - start) * vlen(delta);
2148 // rule 4: start TO end must not be too short
2149 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2150 if (trace_startsolid)
2152 if (trace_fraction < minviewdistance / vlen(delta))
2155 // rule 5: don't want to look at sky
2156 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2159 // rule 6: we must not end up in trigger_hurt
2160 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2166 e.dphitcontentsmask = m;
2170 setorigin(e, start);
2171 e.angles = vectoangles(end - start);
2172 dprint("Needed ", ftos(i + 1), " attempts\n");
2179 float zcurveparticles_effectno;
2180 vector zcurveparticles_start;
2181 float zcurveparticles_spd;
2183 void endzcurveparticles()
2185 if(zcurveparticles_effectno)
2188 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2190 zcurveparticles_effectno = 0;
2193 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2195 spd = bound(0, floor(spd / 16), 32767);
2196 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2198 endzcurveparticles();
2199 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2200 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2201 WriteShort(MSG_BROADCAST, effectno);
2202 WriteCoord(MSG_BROADCAST, start_x);
2203 WriteCoord(MSG_BROADCAST, start_y);
2204 WriteCoord(MSG_BROADCAST, start_z);
2205 zcurveparticles_effectno = effectno;
2206 zcurveparticles_start = start;
2209 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2210 WriteCoord(MSG_BROADCAST, end_x);
2211 WriteCoord(MSG_BROADCAST, end_y);
2212 WriteCoord(MSG_BROADCAST, end_z);
2213 WriteCoord(MSG_BROADCAST, end_dz);
2214 zcurveparticles_spd = spd;
2217 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2220 vector vecxy, velxy;
2222 vecxy = end - start;
2227 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2229 endzcurveparticles();
2230 trailparticles(world, effectno, start, end);
2234 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2235 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2238 void write_recordmarker(entity pl, float tstart, float dt)
2240 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2242 // also write a marker into demo files for demotc-race-record-extractor to find
2245 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2246 " ", ftos(tstart), " ", ftos(dt), "\n"));
2249 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2262 if(allowcenter) // 2: allow center handedness
2275 if(allowcenter) // 2: allow center handedness
2291 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2296 if (autocvar_g_shootfromeye)
2300 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2301 else { vecs_y = 0; vecs_z -= 2; }
2309 else if (autocvar_g_shootfromcenter)
2314 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2324 else if (autocvar_g_shootfromclient)
2326 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2331 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2333 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2337 void attach_sameorigin(entity e, entity to, string tag)
2339 vector org, t_forward, t_left, t_up, e_forward, e_up;
2342 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2343 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2344 t_forward = v_forward * tagscale;
2345 t_left = v_right * -tagscale;
2346 t_up = v_up * tagscale;
2348 e.origin_x = org * t_forward;
2349 e.origin_y = org * t_left;
2350 e.origin_z = org * t_up;
2352 // current forward and up directions
2353 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2354 e.angles = AnglesTransform_FromVAngles(e.angles);
2356 e.angles = AnglesTransform_FromAngles(e.angles);
2357 fixedmakevectors(e.angles);
2359 // untransform forward, up!
2360 e_forward_x = v_forward * t_forward;
2361 e_forward_y = v_forward * t_left;
2362 e_forward_z = v_forward * t_up;
2363 e_up_x = v_up * t_forward;
2364 e_up_y = v_up * t_left;
2365 e_up_z = v_up * t_up;
2367 e.angles = fixedvectoangles2(e_forward, e_up);
2368 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2369 e.angles = AnglesTransform_ToVAngles(e.angles);
2371 e.angles = AnglesTransform_ToAngles(e.angles);
2373 setattachment(e, to, tag);
2374 setorigin(e, e.origin);
2377 void detach_sameorigin(entity e)
2380 org = gettaginfo(e, 0);
2381 e.angles = fixedvectoangles2(v_forward, v_up);
2382 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2383 e.angles = AnglesTransform_ToVAngles(e.angles);
2385 e.angles = AnglesTransform_ToAngles(e.angles);
2387 setattachment(e, world, "");
2388 setorigin(e, e.origin);
2391 void follow_sameorigin(entity e, entity to)
2393 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2394 e.aiment = to; // make the hole follow bmodel
2395 e.punchangle = to.angles; // the original angles of bmodel
2396 e.view_ofs = e.origin - to.origin; // relative origin
2397 e.v_angle = e.angles - to.angles; // relative angles
2400 void unfollow_sameorigin(entity e)
2402 e.movetype = MOVETYPE_NONE;
2405 entity gettaginfo_relative_ent;
2406 vector gettaginfo_relative(entity e, float tag)
2408 if (!gettaginfo_relative_ent)
2410 gettaginfo_relative_ent = spawn();
2411 gettaginfo_relative_ent.effects = EF_NODRAW;
2413 gettaginfo_relative_ent.model = e.model;
2414 gettaginfo_relative_ent.modelindex = e.modelindex;
2415 gettaginfo_relative_ent.frame = e.frame;
2416 return gettaginfo(gettaginfo_relative_ent, tag);
2421 float modeleffect_SendEntity(entity to, float sf)
2424 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2427 if(self.velocity != '0 0 0')
2429 if(self.angles != '0 0 0')
2431 if(self.avelocity != '0 0 0')
2434 WriteByte(MSG_ENTITY, f);
2435 WriteShort(MSG_ENTITY, self.modelindex);
2436 WriteByte(MSG_ENTITY, self.skin);
2437 WriteByte(MSG_ENTITY, self.frame);
2438 WriteCoord(MSG_ENTITY, self.origin_x);
2439 WriteCoord(MSG_ENTITY, self.origin_y);
2440 WriteCoord(MSG_ENTITY, self.origin_z);
2443 WriteCoord(MSG_ENTITY, self.velocity_x);
2444 WriteCoord(MSG_ENTITY, self.velocity_y);
2445 WriteCoord(MSG_ENTITY, self.velocity_z);
2449 WriteCoord(MSG_ENTITY, self.angles_x);
2450 WriteCoord(MSG_ENTITY, self.angles_y);
2451 WriteCoord(MSG_ENTITY, self.angles_z);
2455 WriteCoord(MSG_ENTITY, self.avelocity_x);
2456 WriteCoord(MSG_ENTITY, self.avelocity_y);
2457 WriteCoord(MSG_ENTITY, self.avelocity_z);
2459 WriteShort(MSG_ENTITY, self.scale * 256.0);
2460 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2461 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2462 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2463 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2468 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)
2473 e.classname = "modeleffect";
2481 e.teleport_time = t1;
2485 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2489 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2492 sz = max(e.scale, e.scale2);
2493 setsize(e, e.mins * sz, e.maxs * sz);
2494 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2497 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2499 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2502 float randombit(float bits)
2504 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2513 for(f = 1; f <= bits; f *= 2)
2522 r = (r - 1) / (n - 1);
2529 float randombits(float bits, float k, float error_return)
2533 while(k > 0 && bits != r)
2535 r += randombit(bits - r);
2544 void randombit_test(float bits, float iter)
2548 print(ftos(randombit(bits)), "\n");
2553 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2555 if(halflifedist > 0)
2556 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2557 else if(halflifedist < 0)
2558 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2567 #define cvar_string_normal builtin_cvar_string
2568 #define cvar_normal builtin_cvar
2570 string cvar_string_normal(string n)
2572 if not(cvar_type(n) & 1)
2573 backtrace(strcat("Attempt to access undefined cvar: ", n));
2574 return builtin_cvar_string(n);
2577 float cvar_normal(string n)
2579 return stof(cvar_string_normal(n));
2582 #define cvar_set_normal builtin_cvar_set
2590 oself.think = SUB_Remove;
2591 oself.nextthink = time;
2597 Execute func() after time + fdelay.
2598 self when func is executed = self when defer is called
2600 void defer(float fdelay, void() func)
2607 e.think = defer_think;
2608 e.nextthink = time + fdelay;
2611 .string aiment_classname;
2612 .float aiment_deadflag;
2613 void SetMovetypeFollow(entity ent, entity e)
2615 // FIXME this may not be warpzone aware
2616 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2617 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.
2618 ent.aiment = e; // make the hole follow bmodel
2619 ent.punchangle = e.angles; // the original angles of bmodel
2620 ent.view_ofs = ent.origin - e.origin; // relative origin
2621 ent.v_angle = ent.angles - e.angles; // relative angles
2622 ent.aiment_classname = strzone(e.classname);
2623 ent.aiment_deadflag = e.deadflag;
2625 void UnsetMovetypeFollow(entity ent)
2627 ent.movetype = MOVETYPE_FLY;
2628 PROJECTILE_MAKETRIGGER(ent);
2631 float LostMovetypeFollow(entity ent)
2634 if(ent.movetype != MOVETYPE_FOLLOW)
2640 if(ent.aiment.classname != ent.aiment_classname)
2642 if(ent.aiment.deadflag != ent.aiment_deadflag)
2648 float isPushable(entity e)
2657 case "droppedweapon":
2658 case "keepawayball":
2659 case "nexball_basketball":
2660 case "nexball_football":
2662 case "bullet": // antilagged bullets can't hit this either
2665 if (e.projectiledeathtype)
2670 void dedicated_print(string input) // print(), but only print if the server is not local
2672 if not(server_is_local) { print(input); }