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;
35 string ColoredTeamName(float t);
37 string admin_name(void)
39 if(autocvar_sv_adminnick != "")
40 return autocvar_sv_adminnick;
42 return "SERVER ADMIN";
45 float DistributeEvenly_amount;
46 float DistributeEvenly_totalweight;
47 void DistributeEvenly_Init(float amount, float totalweight)
49 if (DistributeEvenly_amount)
51 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
52 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
55 DistributeEvenly_amount = 0;
57 DistributeEvenly_amount = amount;
58 DistributeEvenly_totalweight = totalweight;
60 float DistributeEvenly_Get(float weight)
65 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
66 DistributeEvenly_totalweight -= weight;
67 DistributeEvenly_amount -= f;
70 float DistributeEvenly_GetRandomized(float weight)
75 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
76 DistributeEvenly_totalweight -= weight;
77 DistributeEvenly_amount -= f;
81 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
84 string STR_PLAYER = "player";
85 string STR_SPECTATOR = "spectator";
86 string STR_OBSERVER = "observer";
89 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
90 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
91 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
92 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
94 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
95 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
96 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
97 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
98 #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if(v.classname != STR_PLAYER)
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
102 #define CENTER_OR_VIEWOFS(ent) (ent.origin + ((ent.classname == STR_PLAYER) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
104 // copies a string to a tempstring (so one can strunzone it)
105 string strcat1(string s) = #115; // FRIK_FILE
110 void bcenterprint(string s)
112 // TODO replace by MSG_ALL (would show it to spectators too, though)?
114 FOR_EACH_PLAYER(head)
115 if (clienttype(head) == CLIENTTYPE_REAL)
116 centerprint(head, s);
119 void GameLogEcho(string s)
124 if (autocvar_sv_eventlog_files)
129 matches = autocvar_sv_eventlog_files_counter + 1;
130 cvar_set("sv_eventlog_files_counter", ftos(matches));
133 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
134 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
135 logfile = fopen(fn, FILE_APPEND);
136 fputs(logfile, ":logversion:3\n");
140 if (autocvar_sv_eventlog_files_timestamps)
141 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
143 fputs(logfile, strcat(s, "\n"));
146 if (autocvar_sv_eventlog_console)
155 // will be opened later
160 if (logfile_open && logfile >= 0)
167 #define strstr strstrofs
169 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
170 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
171 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
172 // BE CONSTANT OR strzoneD!
173 float strstr(string haystack, string needle, float offset)
177 len = strlen(needle);
178 endpos = strlen(haystack) - len;
179 while(offset <= endpos)
181 found = substring(haystack, offset, len);
190 float NUM_NEAREST_ENTITIES = 4;
191 entity nearest_entity[NUM_NEAREST_ENTITIES];
192 float nearest_length[NUM_NEAREST_ENTITIES];
193 entity findnearest(vector point, .string field, string value, vector axismod)
204 localhead = find(world, field, value);
207 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
208 dist = localhead.oldorigin;
210 dist = localhead.origin;
212 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
215 for (i = 0; i < num_nearest; ++i)
217 if (len < nearest_length[i])
221 // now i tells us where to insert at
222 // INSERTION SORT! YOU'VE SEEN IT! RUN!
223 if (i < NUM_NEAREST_ENTITIES)
225 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
227 nearest_length[j + 1] = nearest_length[j];
228 nearest_entity[j + 1] = nearest_entity[j];
230 nearest_length[i] = len;
231 nearest_entity[i] = localhead;
232 if (num_nearest < NUM_NEAREST_ENTITIES)
233 num_nearest = num_nearest + 1;
236 localhead = find(localhead, field, value);
239 // now use the first one from our list that we can see
240 for (i = 0; i < num_nearest; ++i)
242 traceline(point, nearest_entity[i].origin, TRUE, world);
243 if (trace_fraction == 1)
247 dprint("Nearest point (");
248 dprint(nearest_entity[0].netname);
249 dprint(") is not visible, using a visible one.\n");
251 return nearest_entity[i];
255 if (num_nearest == 0)
258 dprint("Not seeing any location point, using nearest as fallback.\n");
260 dprint("Candidates were: ");
261 for(j = 0; j < num_nearest; ++j)
265 dprint(nearest_entity[j].netname);
270 return nearest_entity[0];
273 void spawnfunc_target_location()
275 self.classname = "target_location";
276 // location name in netname
277 // eventually support: count, teamgame selectors, line of sight?
280 void spawnfunc_info_location()
282 self.classname = "target_location";
283 self.message = self.netname;
286 string NearestLocation(vector p)
291 loc = findnearest(p, classname, "target_location", '1 1 1');
298 loc = findnearest(p, target, "###item###", '1 1 4');
305 string formatmessage(string msg)
316 WarpZone_crosshair_trace(self);
317 cursor = trace_endpos;
318 cursor_ent = trace_ent;
322 break; // too many replacements
325 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
326 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
339 replacement = substring(msg, p, 2);
340 escape = substring(msg, p + 1, 1);
344 else if (escape == "\\")
346 else if (escape == "n")
348 else if (escape == "a")
349 replacement = ftos(floor(self.armorvalue));
350 else if (escape == "h")
351 replacement = ftos(floor(self.health));
352 else if (escape == "l")
353 replacement = NearestLocation(self.origin);
354 else if (escape == "y")
355 replacement = NearestLocation(cursor);
356 else if (escape == "d")
357 replacement = NearestLocation(self.death_origin);
358 else if (escape == "w") {
362 wep = self.switchweapon;
365 replacement = W_Name(wep);
366 } else if (escape == "W") {
367 if (self.items & IT_SHELLS) replacement = "shells";
368 else if (self.items & IT_NAILS) replacement = "bullets";
369 else if (self.items & IT_ROCKETS) replacement = "rockets";
370 else if (self.items & IT_CELLS) replacement = "cells";
371 else replacement = "batteries"; // ;)
372 } else if (escape == "x") {
373 replacement = cursor_ent.netname;
374 if (replacement == "" || !cursor_ent)
375 replacement = "nothing";
376 } else if (escape == "s")
377 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
378 else if (escape == "S")
379 replacement = ftos(vlen(self.velocity));
381 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
382 p = p + strlen(replacement);
387 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
388 return (value == 0) ? FALSE : TRUE;
397 >0: receives a cvar from name=argv(f) value=argv(f+1)
399 void GetCvars_handleString(string thisname, float f, .string field, string name)
404 strunzone(self.field);
405 self.field = string_null;
409 if (thisname == name)
412 strunzone(self.field);
413 self.field = strzone(argv(f + 1));
417 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
419 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
421 GetCvars_handleString(thisname, f, field, name);
422 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
423 if (thisname == name)
426 s = func(strcat1(self.field));
429 strunzone(self.field);
430 self.field = strzone(s);
434 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
441 if (thisname == name)
442 self.field = stof(argv(f + 1));
445 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
447 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
454 if (thisname == name)
458 self.field = stof(argv(f + 1));
467 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
470 float w_getbestweapon(entity e);
471 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
474 o = W_FixWeaponOrder_ForceComplete(wo);
475 if(self.weaponorder_byimpulse)
477 strunzone(self.weaponorder_byimpulse);
478 self.weaponorder_byimpulse = string_null;
480 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
483 void GetCvars(float f)
485 string s = string_null;
488 s = strcat1(argv(f));
492 MUTATOR_CALLHOOK(GetCvars);
493 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
494 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
495 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
496 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
497 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
498 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
499 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
500 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
501 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
502 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
503 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
504 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
505 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
506 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
507 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
508 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
509 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
510 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
511 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
512 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
513 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
514 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
515 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
517 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
518 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
520 #ifdef ALLOW_FORCEMODELS
521 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
522 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
524 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
525 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
526 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
527 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
528 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
530 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
533 if (s == "cl_weaponpriority")
534 self.switchweapon = w_getbestweapon(self);
535 if (s == "cl_allow_uidtracking")
536 PlayerStats_AddPlayer(self);
540 void backtrace(string msg)
543 dev = autocvar_developer;
544 war = autocvar_prvm_backtraceforwarnings;
545 cvar_set("developer", "1");
546 cvar_set("prvm_backtraceforwarnings", "1");
548 print("--- CUT HERE ---\nWARNING: ");
551 remove(world); // isn't there any better way to cause a backtrace?
552 print("\n--- CUT UNTIL HERE ---\n");
553 cvar_set("developer", ftos(dev));
554 cvar_set("prvm_backtraceforwarnings", ftos(war));
557 string Team_ColorCode(float teamid)
559 if (teamid == COLOR_TEAM1)
561 else if (teamid == COLOR_TEAM2)
563 else if (teamid == COLOR_TEAM3)
565 else if (teamid == COLOR_TEAM4)
571 string Team_ColorName(float t)
573 // fixme: Search for team entities and get their .netname's!
574 if (t == COLOR_TEAM1)
576 if (t == COLOR_TEAM2)
578 if (t == COLOR_TEAM3)
580 if (t == COLOR_TEAM4)
585 string Team_ColorNameLowerCase(float t)
587 // fixme: Search for team entities and get their .netname's!
588 if (t == COLOR_TEAM1)
590 if (t == COLOR_TEAM2)
592 if (t == COLOR_TEAM3)
594 if (t == COLOR_TEAM4)
599 float ColourToNumber(string team_colour)
601 if (team_colour == "red")
604 if (team_colour == "blue")
607 if (team_colour == "yellow")
610 if (team_colour == "pink")
613 if (team_colour == "auto")
619 float NumberToTeamNumber(float number)
636 // decolorizes and team colors the player name when needed
637 string playername(entity p)
640 if (teamplay && !intermission_running && p.classname == "player")
642 t = Team_ColorCode(p.team);
643 return strcat(t, strdecolorize(p.netname));
649 vector randompos(vector m1, vector m2)
653 v_x = m2_x * random() + m1_x;
654 v_y = m2_y * random() + m1_y;
655 v_z = m2_z * random() + m1_z;
659 //#NO AUTOCVARS START
661 float g_pickup_shells;
662 float g_pickup_shells_max;
663 float g_pickup_nails;
664 float g_pickup_nails_max;
665 float g_pickup_rockets;
666 float g_pickup_rockets_max;
667 float g_pickup_cells;
668 float g_pickup_cells_max;
670 float g_pickup_fuel_jetpack;
671 float g_pickup_fuel_max;
672 float g_pickup_armorsmall;
673 float g_pickup_armorsmall_max;
674 float g_pickup_armorsmall_anyway;
675 float g_pickup_armormedium;
676 float g_pickup_armormedium_max;
677 float g_pickup_armormedium_anyway;
678 float g_pickup_armorbig;
679 float g_pickup_armorbig_max;
680 float g_pickup_armorbig_anyway;
681 float g_pickup_armorlarge;
682 float g_pickup_armorlarge_max;
683 float g_pickup_armorlarge_anyway;
684 float g_pickup_healthsmall;
685 float g_pickup_healthsmall_max;
686 float g_pickup_healthsmall_anyway;
687 float g_pickup_healthmedium;
688 float g_pickup_healthmedium_max;
689 float g_pickup_healthmedium_anyway;
690 float g_pickup_healthlarge;
691 float g_pickup_healthlarge_max;
692 float g_pickup_healthlarge_anyway;
693 float g_pickup_healthmega;
694 float g_pickup_healthmega_max;
695 float g_pickup_healthmega_anyway;
696 float g_pickup_ammo_anyway;
697 float g_pickup_weapons_anyway;
699 WEPSET_DECLARE_A(g_weaponarena_weapons);
700 float g_weaponarena_random;
701 float g_weaponarena_random_with_laser;
702 string g_weaponarena_list;
703 float g_weaponspeedfactor;
704 float g_weaponratefactor;
705 float g_weapondamagefactor;
706 float g_weaponforcefactor;
707 float g_weaponspreadfactor;
709 WEPSET_DECLARE_A(start_weapons);
710 WEPSET_DECLARE_A(start_weapons_default);
711 WEPSET_DECLARE_A(start_weapons_defaultmask);
713 float start_ammo_shells;
714 float start_ammo_nails;
715 float start_ammo_rockets;
716 float start_ammo_cells;
717 float start_ammo_fuel;
719 float start_armorvalue;
720 WEPSET_DECLARE_A(warmup_start_weapons);
721 WEPSET_DECLARE_A(warmup_start_weapons_default);
722 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
723 float warmup_start_ammo_shells;
724 float warmup_start_ammo_nails;
725 float warmup_start_ammo_rockets;
726 float warmup_start_ammo_cells;
727 float warmup_start_ammo_fuel;
728 float warmup_start_health;
729 float warmup_start_armorvalue;
732 entity get_weaponinfo(float w);
734 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
736 var float i = weaponinfo.weapon;
742 if (g_lms || g_ca || allguns)
744 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
750 d = (i == WEP_SHOTGUN);
752 d = 0; // weapon is set a few lines later
754 d = (i == WEP_LASER || i == WEP_SHOTGUN);
756 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
757 d |= (i == WEP_HOOK);
758 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
761 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
763 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
768 // 4: is set by default?
777 void readplayerstartcvars()
783 // initialize starting values for players
784 WEPSET_CLEAR_A(start_weapons);
785 WEPSET_CLEAR_A(start_weapons_default);
786 WEPSET_CLEAR_A(start_weapons_defaultmask);
788 start_ammo_shells = 0;
789 start_ammo_nails = 0;
790 start_ammo_rockets = 0;
791 start_ammo_cells = 0;
792 start_health = cvar("g_balance_health_start");
793 start_armorvalue = cvar("g_balance_armor_start");
796 WEPSET_CLEAR_A(g_weaponarena_weapons);
798 s = cvar_string("g_weaponarena");
799 if (s == "0" || s == "")
805 if (s == "0" || s == "")
811 // forcibly turn off weaponarena
816 g_weaponarena_list = "All Weapons";
817 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
819 e = get_weaponinfo(j);
820 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
821 WEPSET_OR_AW(g_weaponarena_weapons, j);
824 else if (s == "most")
827 g_weaponarena_list = "Most Weapons";
828 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
830 e = get_weaponinfo(j);
831 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
832 if (e.spawnflags & WEP_FLAG_NORMAL)
833 WEPSET_OR_AW(g_weaponarena_weapons, j);
836 else if (s == "none")
839 g_weaponarena_list = "No Weapons";
844 t = tokenize_console(s);
845 g_weaponarena_list = "";
846 for (i = 0; i < t; ++i)
849 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
851 e = get_weaponinfo(j);
854 WEPSET_OR_AW(g_weaponarena_weapons, j);
855 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
861 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
864 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
868 g_weaponarena_random = cvar("g_weaponarena_random");
870 g_weaponarena_random = 0;
871 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
875 g_minstagib = 0; // incompatible
876 g_pinata = 0; // incompatible
877 g_weapon_stay = 0; // incompatible
878 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
880 start_items |= IT_UNLIMITED_AMMO;
882 else if (g_minstagib)
884 g_pinata = 0; // incompatible
885 g_weapon_stay = 0; // incompatible
886 g_bloodloss = 0; // incompatible
888 start_armorvalue = 0;
889 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
890 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
891 start_items |= IT_UNLIMITED_SUPERWEAPONS;
893 if (g_minstagib_invis_alpha <= 0)
894 g_minstagib_invis_alpha = -1;
898 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
900 e = get_weaponinfo(i);
901 float w = want_weapon("g_start_weapon_", e, FALSE);
903 WEPSET_OR_AW(start_weapons, i);
905 WEPSET_OR_AW(start_weapons_default, i);
907 WEPSET_OR_AW(start_weapons_defaultmask, i);
911 if(!cvar("g_use_ammunition"))
912 start_items |= IT_UNLIMITED_AMMO;
914 if(cvar("g_nexball"))
915 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
919 start_ammo_cells = cvar("g_minstagib_ammo_start");
920 start_ammo_fuel = cvar("g_start_ammo_fuel");
922 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
924 start_ammo_rockets = 999;
925 start_ammo_shells = 999;
926 start_ammo_cells = 999;
927 start_ammo_nails = 999;
928 start_ammo_fuel = 999;
934 start_ammo_shells = cvar("g_lms_start_ammo_shells");
935 start_ammo_nails = cvar("g_lms_start_ammo_nails");
936 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
937 start_ammo_cells = cvar("g_lms_start_ammo_cells");
938 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
942 start_ammo_shells = cvar("g_start_ammo_shells");
943 start_ammo_nails = cvar("g_start_ammo_nails");
944 start_ammo_rockets = cvar("g_start_ammo_rockets");
945 start_ammo_cells = cvar("g_start_ammo_cells");
946 start_ammo_fuel = cvar("g_start_ammo_fuel");
952 start_health = cvar("g_lms_start_health");
953 start_armorvalue = cvar("g_lms_start_armor");
958 warmup_start_ammo_shells = start_ammo_shells;
959 warmup_start_ammo_nails = start_ammo_nails;
960 warmup_start_ammo_rockets = start_ammo_rockets;
961 warmup_start_ammo_cells = start_ammo_cells;
962 warmup_start_ammo_fuel = start_ammo_fuel;
963 warmup_start_health = start_health;
964 warmup_start_armorvalue = start_armorvalue;
965 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
966 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
967 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
969 if (!g_weaponarena && !g_minstagib && !g_ca)
971 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
972 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
973 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
974 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
975 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
976 warmup_start_health = cvar("g_warmup_start_health");
977 warmup_start_armorvalue = cvar("g_warmup_start_armor");
978 WEPSET_CLEAR_A(warmup_start_weapons);
979 WEPSET_CLEAR_A(warmup_start_weapons_default);
980 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
981 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
983 e = get_weaponinfo(i);
984 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
986 WEPSET_OR_AW(warmup_start_weapons, i);
988 WEPSET_OR_AW(warmup_start_weapons_default, i);
990 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
996 start_items |= IT_JETPACK;
998 MUTATOR_CALLHOOK(SetStartItems);
1000 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
1002 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1003 start_items |= IT_FUEL_REGEN;
1004 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1005 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1008 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1010 e = get_weaponinfo(i);
1011 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
1012 weapon_action(i, WR_PRECACHE);
1015 start_ammo_shells = max(0, start_ammo_shells);
1016 start_ammo_nails = max(0, start_ammo_nails);
1017 start_ammo_cells = max(0, start_ammo_cells);
1018 start_ammo_rockets = max(0, start_ammo_rockets);
1019 start_ammo_fuel = max(0, start_ammo_fuel);
1021 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1022 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1023 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1024 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1025 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1029 float g_bugrigs_planar_movement;
1030 float g_bugrigs_planar_movement_car_jumping;
1031 float g_bugrigs_reverse_spinning;
1032 float g_bugrigs_reverse_speeding;
1033 float g_bugrigs_reverse_stopping;
1034 float g_bugrigs_air_steering;
1035 float g_bugrigs_angle_smoothing;
1036 float g_bugrigs_friction_floor;
1037 float g_bugrigs_friction_brake;
1038 float g_bugrigs_friction_air;
1039 float g_bugrigs_accel;
1040 float g_bugrigs_speed_ref;
1041 float g_bugrigs_speed_pow;
1042 float g_bugrigs_steer;
1044 float g_touchexplode;
1045 float g_touchexplode_radius;
1046 float g_touchexplode_damage;
1047 float g_touchexplode_edgedamage;
1048 float g_touchexplode_force;
1055 float sv_pitch_fixyaw;
1057 string GetGametype(); // g_world.qc
1058 void readlevelcvars(void)
1060 g_minstagib = cvar("g_minstagib");
1062 // load ALL the mutators
1063 if(cvar("g_dodging"))
1064 MUTATOR_ADD(mutator_dodging);
1065 if(cvar("g_spawn_near_teammate"))
1066 MUTATOR_ADD(mutator_spawn_near_teammate);
1067 if(cvar("g_physical_items"))
1068 MUTATOR_ADD(mutator_physical_items);
1071 if(cvar("g_invincible_projectiles"))
1072 MUTATOR_ADD(mutator_invincibleprojectiles);
1073 if(cvar("g_new_toys"))
1074 MUTATOR_ADD(mutator_new_toys);
1076 MUTATOR_ADD(mutator_nix);
1077 if(cvar("g_rocket_flying"))
1078 MUTATOR_ADD(mutator_rocketflying);
1079 if(cvar("g_vampire"))
1080 MUTATOR_ADD(mutator_vampire);
1081 if(cvar("g_superspectate"))
1082 MUTATOR_ADD(mutator_superspec);
1085 // is this a mutator? is this a mode?
1086 if(cvar("g_sandbox"))
1087 MUTATOR_ADD(sandbox);
1089 if(cvar("sv_allow_fullbright"))
1090 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1092 g_bugrigs = cvar("g_bugrigs");
1093 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1094 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1095 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1096 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1097 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1098 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1099 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1100 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1101 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1102 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1103 g_bugrigs_accel = cvar("g_bugrigs_accel");
1104 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1105 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1106 g_bugrigs_steer = cvar("g_bugrigs_steer");
1108 g_touchexplode = cvar("g_touchexplode");
1109 g_touchexplode_radius = cvar("g_touchexplode_radius");
1110 g_touchexplode_damage = cvar("g_touchexplode_damage");
1111 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1112 g_touchexplode_force = cvar("g_touchexplode_force");
1114 #ifdef ALLOW_FORCEMODELS
1115 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1118 sv_clones = cvar("sv_clones");
1119 sv_gentle = cvar("sv_gentle");
1120 sv_foginterval = cvar("sv_foginterval");
1121 g_cloaked = cvar("g_cloaked");
1123 g_cloaked = 1; // always enable cloak in CTS
1124 g_jump_grunt = cvar("g_jump_grunt");
1125 g_footsteps = cvar("g_footsteps");
1126 g_grappling_hook = cvar("g_grappling_hook");
1127 g_jetpack = cvar("g_jetpack");
1128 g_midair = cvar("g_midair");
1129 g_norecoil = cvar("g_norecoil");
1130 g_bloodloss = cvar("g_bloodloss");
1131 sv_maxidle = cvar("sv_maxidle");
1132 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1133 sv_autotaunt = cvar("sv_autotaunt");
1134 sv_taunt = cvar("sv_taunt");
1136 inWarmupStage = cvar("g_warmup");
1137 g_warmup_limit = cvar("g_warmup_limit");
1138 g_warmup_allguns = cvar("g_warmup_allguns");
1139 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1141 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1142 inWarmupStage = 0; // these modes cannot work together, sorry
1144 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1145 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1146 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1147 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1148 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1149 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1150 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1151 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1152 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1153 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1154 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1155 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1156 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1157 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1159 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1160 g_weaponratefactor = cvar("g_weaponratefactor");
1161 g_weapondamagefactor = cvar("g_weapondamagefactor");
1162 g_weaponforcefactor = cvar("g_weaponforcefactor");
1163 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1165 g_pickup_shells = cvar("g_pickup_shells");
1166 g_pickup_shells_max = cvar("g_pickup_shells_max");
1167 g_pickup_nails = cvar("g_pickup_nails");
1168 g_pickup_nails_max = cvar("g_pickup_nails_max");
1169 g_pickup_rockets = cvar("g_pickup_rockets");
1170 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1171 g_pickup_cells = cvar("g_pickup_cells");
1172 g_pickup_cells_max = cvar("g_pickup_cells_max");
1173 g_pickup_fuel = cvar("g_pickup_fuel");
1174 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1175 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1176 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1177 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1178 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1179 g_pickup_armormedium = cvar("g_pickup_armormedium");
1180 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1181 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1182 g_pickup_armorbig = cvar("g_pickup_armorbig");
1183 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1184 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1185 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1186 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1187 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1188 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1189 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1190 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1191 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1192 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1193 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1194 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1195 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1196 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1197 g_pickup_healthmega = cvar("g_pickup_healthmega");
1198 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1199 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1201 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1202 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1204 g_pinata = cvar("g_pinata");
1206 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1208 g_weapon_stay = cvar("g_weapon_stay");
1210 if not(inWarmupStage && !g_ca)
1211 game_starttime = cvar("g_start_delay");
1213 sv_pitch_min = cvar("sv_pitch_min");
1214 sv_pitch_max = cvar("sv_pitch_max");
1215 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1217 readplayerstartcvars();
1223 string precache_sound (string s) = #19;
1224 float precache_sound_index (string s) = #19;
1226 #define SND_VOLUME 1
1227 #define SND_ATTENUATION 2
1228 #define SND_LARGEENTITY 8
1229 #define SND_LARGESOUND 16
1231 float sound_allowed(float dest, entity e)
1233 // sounds from world may always pass
1236 if (e.classname == "body")
1238 else if (e.realowner && e.realowner != e)
1240 else if (e.owner && e.owner != e)
1245 // sounds to self may always pass
1246 if (dest == MSG_ONE)
1247 if (e == msg_entity)
1249 // sounds by players can be removed
1250 if (autocvar_bot_sound_monopoly)
1251 if (clienttype(e) == CLIENTTYPE_REAL)
1253 // anything else may pass
1257 #ifdef COMPAT_XON010_CHANNELS
1258 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1259 void sound(entity e, float chan, string samp, float vol, float atten)
1261 if (!sound_allowed(MSG_BROADCAST, e))
1263 builtin_sound(e, chan, samp, vol, atten);
1267 void sound(entity e, float chan, string samp, float vol, float atten)
1269 if (!sound_allowed(MSG_BROADCAST, e))
1271 sound7(e, chan, samp, vol, atten, 0, 0);
1275 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1279 if (!sound_allowed(dest, e))
1282 entno = num_for_edict(e);
1283 idx = precache_sound_index(samp);
1288 atten = floor(atten * 64);
1289 vol = floor(vol * 255);
1292 sflags |= SND_VOLUME;
1294 sflags |= SND_ATTENUATION;
1295 if (entno >= 8192 || chan < 0 || chan > 7)
1296 sflags |= SND_LARGEENTITY;
1298 sflags |= SND_LARGESOUND;
1300 WriteByte(dest, SVC_SOUND);
1301 WriteByte(dest, sflags);
1302 if (sflags & SND_VOLUME)
1303 WriteByte(dest, vol);
1304 if (sflags & SND_ATTENUATION)
1305 WriteByte(dest, atten);
1306 if (sflags & SND_LARGEENTITY)
1308 WriteShort(dest, entno);
1309 WriteByte(dest, chan);
1313 WriteShort(dest, entno * 8 + chan);
1315 if (sflags & SND_LARGESOUND)
1316 WriteShort(dest, idx);
1318 WriteByte(dest, idx);
1320 WriteCoord(dest, o_x);
1321 WriteCoord(dest, o_y);
1322 WriteCoord(dest, o_z);
1324 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1328 if (!sound_allowed(dest, e))
1331 o = e.origin + 0.5 * (e.mins + e.maxs);
1332 soundtoat(dest, e, o, chan, samp, vol, atten);
1334 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1336 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1338 void stopsoundto(float dest, entity e, float chan)
1342 if (!sound_allowed(dest, e))
1345 entno = num_for_edict(e);
1347 if (entno >= 8192 || chan < 0 || chan > 7)
1350 idx = precache_sound_index("misc/null.wav");
1351 sflags = SND_LARGEENTITY;
1353 sflags |= SND_LARGESOUND;
1354 WriteByte(dest, SVC_SOUND);
1355 WriteByte(dest, sflags);
1356 WriteShort(dest, entno);
1357 WriteByte(dest, chan);
1358 if (sflags & SND_LARGESOUND)
1359 WriteShort(dest, idx);
1361 WriteByte(dest, idx);
1362 WriteCoord(dest, e.origin_x);
1363 WriteCoord(dest, e.origin_y);
1364 WriteCoord(dest, e.origin_z);
1368 WriteByte(dest, SVC_STOPSOUND);
1369 WriteShort(dest, entno * 8 + chan);
1372 void stopsound(entity e, float chan)
1374 if (!sound_allowed(MSG_BROADCAST, e))
1377 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1378 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1381 void play2(entity e, string filename)
1383 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1385 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1388 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1390 float spamsound(entity e, float chan, string samp, float vol, float atten)
1392 if (!sound_allowed(MSG_BROADCAST, e))
1395 if (time > e.spamtime)
1398 sound(e, chan, samp, vol, atten);
1404 void play2team(float t, string filename)
1408 if (autocvar_bot_sound_monopoly)
1411 FOR_EACH_REALPLAYER(head)
1414 play2(head, filename);
1418 void play2all(string samp)
1420 if (autocvar_bot_sound_monopoly)
1423 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1426 void PrecachePlayerSounds(string f);
1427 void precache_playermodel(string m)
1429 float globhandle, i, n;
1432 if(substring(m, -9,5) == "_lod1")
1434 if(substring(m, -9,5) == "_lod2")
1437 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1440 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1444 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1447 n = search_getsize(globhandle);
1448 for (i = 0; i < n; ++i)
1450 //print(search_getfilename(globhandle, i), "\n");
1451 f = search_getfilename(globhandle, i);
1452 PrecachePlayerSounds(f);
1454 search_end(globhandle);
1456 void precache_all_playermodels(string pattern)
1458 float globhandle, i, n;
1461 globhandle = search_begin(pattern, TRUE, FALSE);
1464 n = search_getsize(globhandle);
1465 for (i = 0; i < n; ++i)
1467 //print(search_getfilename(globhandle, i), "\n");
1468 f = search_getfilename(globhandle, i);
1469 precache_playermodel(f);
1471 search_end(globhandle);
1476 // gamemode related things
1477 precache_model ("models/misc/chatbubble.spr");
1480 precache_model ("models/runematch/curse.mdl");
1481 precache_model ("models/runematch/rune.mdl");
1484 #ifdef TTURRETS_ENABLED
1485 if (autocvar_g_turrets)
1489 // Precache all player models if desired
1490 if (autocvar_sv_precacheplayermodels)
1492 PrecachePlayerSounds("sound/player/default.sounds");
1493 precache_all_playermodels("models/player/*.zym");
1494 precache_all_playermodels("models/player/*.dpm");
1495 precache_all_playermodels("models/player/*.md3");
1496 precache_all_playermodels("models/player/*.psk");
1497 precache_all_playermodels("models/player/*.iqm");
1500 if (autocvar_sv_defaultcharacter)
1503 s = autocvar_sv_defaultplayermodel_red;
1505 precache_playermodel(s);
1506 s = autocvar_sv_defaultplayermodel_blue;
1508 precache_playermodel(s);
1509 s = autocvar_sv_defaultplayermodel_yellow;
1511 precache_playermodel(s);
1512 s = autocvar_sv_defaultplayermodel_pink;
1514 precache_playermodel(s);
1515 s = autocvar_sv_defaultplayermodel;
1517 precache_playermodel(s);
1522 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1523 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1526 // gore and miscellaneous sounds
1527 //precache_sound ("misc/h2ohit.wav");
1528 precache_model ("models/hook.md3");
1529 precache_sound ("misc/armorimpact.wav");
1530 precache_sound ("misc/bodyimpact1.wav");
1531 precache_sound ("misc/bodyimpact2.wav");
1532 precache_sound ("misc/gib.wav");
1533 precache_sound ("misc/gib_splat01.wav");
1534 precache_sound ("misc/gib_splat02.wav");
1535 precache_sound ("misc/gib_splat03.wav");
1536 precache_sound ("misc/gib_splat04.wav");
1537 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1538 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1539 precache_sound ("misc/null.wav");
1540 precache_sound ("misc/spawn.wav");
1541 precache_sound ("misc/talk.wav");
1542 precache_sound ("misc/teleport.wav");
1543 precache_sound ("misc/poweroff.wav");
1544 precache_sound ("player/lava.wav");
1545 precache_sound ("player/slime.wav");
1547 precache_model ("models/sprites/0.spr32");
1548 precache_model ("models/sprites/1.spr32");
1549 precache_model ("models/sprites/2.spr32");
1550 precache_model ("models/sprites/3.spr32");
1551 precache_model ("models/sprites/4.spr32");
1552 precache_model ("models/sprites/5.spr32");
1553 precache_model ("models/sprites/6.spr32");
1554 precache_model ("models/sprites/7.spr32");
1555 precache_model ("models/sprites/8.spr32");
1556 precache_model ("models/sprites/9.spr32");
1557 precache_model ("models/sprites/10.spr32");
1559 // common weapon precaches
1560 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1561 precache_sound ("weapons/weapon_switch.wav");
1562 precache_sound ("weapons/weaponpickup.wav");
1563 precache_sound ("weapons/unavailable.wav");
1564 precache_sound ("weapons/dryfire.wav");
1565 if (g_grappling_hook)
1567 precache_sound ("weapons/hook_fire.wav"); // hook
1568 precache_sound ("weapons/hook_impact.wav"); // hook
1571 if(autocvar_sv_precacheweapons)
1573 //precache weapon models/sounds
1576 while (wep <= WEP_LAST)
1578 weapon_action(wep, WR_PRECACHE);
1583 precache_model("models/elaser.mdl");
1584 precache_model("models/laser.mdl");
1585 precache_model("models/ebomb.mdl");
1588 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1590 if (!self.noise && self.music) // quake 3 uses the music field
1591 self.noise = self.music;
1593 // plays music for the level if there is any
1596 precache_sound (self.noise);
1597 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1602 // sorry, but using \ in macros breaks line numbers
1603 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
1604 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1605 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1608 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1610 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
1613 WRITESPECTATABLE_MSG_ONE({
1614 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1615 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1616 WriteByte(MSG_ONE, id);
1617 WriteString(MSG_ONE, s);
1618 if (id != 0 && s != "")
1620 WriteByte(MSG_ONE, duration);
1621 WriteByte(MSG_ONE, countdown_num);
1626 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1628 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1630 // WARNING: this kills the trace globals
1631 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1632 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1634 #define INITPRIO_FIRST 0
1635 #define INITPRIO_GAMETYPE 0
1636 #define INITPRIO_GAMETYPE_FALLBACK 1
1637 #define INITPRIO_FINDTARGET 10
1638 #define INITPRIO_DROPTOFLOOR 20
1639 #define INITPRIO_SETLOCATION 90
1640 #define INITPRIO_LINKDOORS 91
1641 #define INITPRIO_LAST 99
1643 .void(void) initialize_entity;
1644 .float initialize_entity_order;
1645 .entity initialize_entity_next;
1646 entity initialize_entity_first;
1648 void make_safe_for_remove(entity e)
1650 if (e.initialize_entity)
1652 entity ent, prev = world;
1653 for (ent = initialize_entity_first; ent; )
1655 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1657 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1658 // skip it in linked list
1661 prev.initialize_entity_next = ent.initialize_entity_next;
1662 ent = prev.initialize_entity_next;
1666 initialize_entity_first = ent.initialize_entity_next;
1667 ent = initialize_entity_first;
1673 ent = ent.initialize_entity_next;
1679 void objerror(string s)
1681 make_safe_for_remove(self);
1682 builtin_objerror(s);
1685 .float remove_except_protected_forbidden;
1686 void remove_except_protected(entity e)
1688 if(e.remove_except_protected_forbidden)
1689 error("not allowed to remove this at this point");
1693 void remove_unsafely(entity e)
1695 if(e.classname == "spike")
1696 error("Removing spikes is forbidden (crylink bug), please report");
1700 void remove_safely(entity e)
1702 make_safe_for_remove(e);
1706 void InitializeEntity(entity e, void(void) func, float order)
1710 if (!e || e.initialize_entity)
1712 // make a proxy initializer entity
1716 e.classname = "initialize_entity";
1720 e.initialize_entity = func;
1721 e.initialize_entity_order = order;
1723 cur = initialize_entity_first;
1727 if (!cur || cur.initialize_entity_order > order)
1729 // insert between prev and cur
1731 prev.initialize_entity_next = e;
1733 initialize_entity_first = e;
1734 e.initialize_entity_next = cur;
1738 cur = cur.initialize_entity_next;
1741 void InitializeEntitiesRun()
1744 startoflist = initialize_entity_first;
1745 initialize_entity_first = world;
1746 remove = remove_except_protected;
1747 for (self = startoflist; self; self = self.initialize_entity_next)
1749 self.remove_except_protected_forbidden = 1;
1751 for (self = startoflist; self; )
1754 var void(void) func;
1755 e = self.initialize_entity_next;
1756 func = self.initialize_entity;
1757 self.initialize_entity_order = 0;
1758 self.initialize_entity = func_null;
1759 self.initialize_entity_next = world;
1760 self.remove_except_protected_forbidden = 0;
1761 if (self.classname == "initialize_entity")
1765 builtin_remove(self);
1768 //dprint("Delayed initialization: ", self.classname, "\n");
1774 backtrace(strcat("Null function in: ", self.classname, "\n"));
1778 remove = remove_unsafely;
1781 .float uncustomizeentityforclient_set;
1782 .void(void) uncustomizeentityforclient;
1783 void UncustomizeEntitiesRun()
1787 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1788 self.uncustomizeentityforclient();
1791 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1793 e.customizeentityforclient = customizer;
1794 e.uncustomizeentityforclient = uncustomizer;
1795 e.uncustomizeentityforclient_set = !!uncustomizer;
1799 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1802 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1806 if (e.classname == "")
1807 e.classname = "net_linked";
1809 if (e.model == "" || self.modelindex == 0)
1813 setmodel(e, "null");
1817 e.SendEntity = sendfunc;
1818 e.SendFlags = 0xFFFFFF;
1821 e.effects |= EF_NODEPTHTEST;
1825 e.nextthink = time + dt;
1826 e.think = SUB_Remove;
1830 void adaptor_think2touch()
1839 void adaptor_think2use()
1851 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1853 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
1854 self.projectiledeathtype |= HITTYPE_SPLASH;
1855 adaptor_think2use();
1858 // deferred dropping
1859 void DropToFloor_Handler()
1861 builtin_droptofloor();
1862 self.dropped_origin = self.origin;
1867 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1872 float trace_hits_box_a0, trace_hits_box_a1;
1874 float trace_hits_box_1d(float end, float thmi, float thma)
1878 // just check if x is in range
1886 // do the trace with respect to x
1887 // 0 -> end has to stay in thmi -> thma
1888 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1889 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1890 if (trace_hits_box_a0 > trace_hits_box_a1)
1896 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1901 // now it is a trace from 0 to end
1903 trace_hits_box_a0 = 0;
1904 trace_hits_box_a1 = 1;
1906 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1908 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1910 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1916 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1918 return trace_hits_box(start, end, thmi - ma, thma - mi);
1921 float SUB_NoImpactCheck()
1923 // zero hitcontents = this is not the real impact, but either the
1924 // mirror-impact of something hitting the projectile instead of the
1925 // projectile hitting the something, or a touchareagrid one. Neither of
1926 // these stop the projectile from moving, so...
1927 if(trace_dphitcontents == 0)
1929 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1930 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)));
1933 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1935 if (other == world && self.size != '0 0 0')
1938 tic = self.velocity * sys_frametime;
1939 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1940 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1941 if (trace_fraction >= 1)
1943 dprint("Odd... did not hit...?\n");
1945 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1947 dprint("Detected and prevented the sky-grapple bug.\n");
1955 #define SUB_OwnerCheck() (other && (other == self.owner))
1957 void RemoveGrapplingHook(entity pl);
1958 void W_Crylink_Dequeue(entity e);
1959 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1961 if(SUB_OwnerCheck())
1963 if(SUB_NoImpactCheck())
1965 if(self.classname == "grapplinghook")
1966 RemoveGrapplingHook(self.realowner);
1967 else if(self.classname == "spike")
1969 W_Crylink_Dequeue(self);
1976 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1977 UpdateCSQCProjectile(self);
1980 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1982 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1983 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1985 void URI_Get_Callback(float id, float status, string data)
1987 if(url_URI_Get_Callback(id, status, data))
1991 else if (id == URI_GET_DISCARD)
1995 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1998 Curl_URI_Get_Callback(id, status, data);
2000 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2003 OnlineBanList_URI_Get_Callback(id, status, data);
2007 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2011 string uid2name(string myuid) {
2013 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2015 // FIXME remove this later after 0.6 release
2016 // convert old style broken records to correct style
2019 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2022 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2023 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2028 s = "^1Unregistered Player";
2032 float race_readTime(string map, float pos)
2040 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2043 string race_readUID(string map, float pos)
2051 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2054 float race_readPos(string map, float t) {
2056 for (i = 1; i <= RANKINGS_CNT; ++i)
2057 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2060 return 0; // pos is zero if unranked
2063 void race_writeTime(string map, float t, string myuid)
2072 newpos = race_readPos(map, t);
2074 float i, prevpos = 0;
2075 for(i = 1; i <= RANKINGS_CNT; ++i)
2077 if(race_readUID(map, i) == myuid)
2080 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2081 for (i = prevpos; i > newpos; --i) {
2082 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2083 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2085 } else { // player has no ranked record yet
2086 for (i = RANKINGS_CNT; i > newpos; --i) {
2087 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2088 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2092 // store new time itself
2093 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2094 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2097 string race_readName(string map, float pos)
2105 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2108 string race_placeName(float pos) {
2109 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2111 if(mod(pos, 10) == 1)
2112 return strcat(ftos(pos), "st");
2113 else if(mod(pos, 10) == 2)
2114 return strcat(ftos(pos), "nd");
2115 else if(mod(pos, 10) == 3)
2116 return strcat(ftos(pos), "rd");
2118 return strcat(ftos(pos), "th");
2121 return strcat(ftos(pos), "th");
2124 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2127 vector start, org, delta, end, enddown, mstart;
2130 m = e.dphitcontentsmask;
2131 e.dphitcontentsmask = goodcontents | badcontents;
2134 delta = world.maxs - world.mins;
2138 for (i = 0; i < attempts; ++i)
2140 start_x = org_x + random() * delta_x;
2141 start_y = org_y + random() * delta_y;
2142 start_z = org_z + random() * delta_z;
2144 // rule 1: start inside world bounds, and outside
2145 // solid, and don't start from somewhere where you can
2146 // fall down to evil
2147 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2148 if (trace_fraction >= 1)
2150 if (trace_startsolid)
2152 if (trace_dphitcontents & badcontents)
2154 if (trace_dphitq3surfaceflags & badsurfaceflags)
2157 // rule 2: if we are too high, lower the point
2158 if (trace_fraction * delta_z > maxaboveground)
2159 start = trace_endpos + '0 0 1' * maxaboveground;
2160 enddown = trace_endpos;
2162 // rule 3: make sure we aren't outside the map. This only works
2163 // for somewhat well formed maps. A good rule of thumb is that
2164 // the map should have a convex outside hull.
2165 // these can be traceLINES as we already verified the starting box
2166 mstart = start + 0.5 * (e.mins + e.maxs);
2167 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2168 if (trace_fraction >= 1)
2170 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2171 if (trace_fraction >= 1)
2173 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2174 if (trace_fraction >= 1)
2176 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2177 if (trace_fraction >= 1)
2179 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2180 if (trace_fraction >= 1)
2183 // rule 4: we must "see" some spawnpoint
2184 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2185 if(checkpvs(mstart, sp))
2189 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2190 if(checkpvs(mstart, sp))
2196 // find a random vector to "look at"
2197 end_x = org_x + random() * delta_x;
2198 end_y = org_y + random() * delta_y;
2199 end_z = org_z + random() * delta_z;
2200 end = start + normalize(end - start) * vlen(delta);
2202 // rule 4: start TO end must not be too short
2203 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2204 if (trace_startsolid)
2206 if (trace_fraction < minviewdistance / vlen(delta))
2209 // rule 5: don't want to look at sky
2210 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2213 // rule 6: we must not end up in trigger_hurt
2214 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2220 e.dphitcontentsmask = m;
2224 setorigin(e, start);
2225 e.angles = vectoangles(end - start);
2226 dprint("Needed ", ftos(i + 1), " attempts\n");
2233 float zcurveparticles_effectno;
2234 vector zcurveparticles_start;
2235 float zcurveparticles_spd;
2237 void endzcurveparticles()
2239 if(zcurveparticles_effectno)
2242 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2244 zcurveparticles_effectno = 0;
2247 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2249 spd = bound(0, floor(spd / 16), 32767);
2250 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2252 endzcurveparticles();
2253 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2254 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2255 WriteShort(MSG_BROADCAST, effectno);
2256 WriteCoord(MSG_BROADCAST, start_x);
2257 WriteCoord(MSG_BROADCAST, start_y);
2258 WriteCoord(MSG_BROADCAST, start_z);
2259 zcurveparticles_effectno = effectno;
2260 zcurveparticles_start = start;
2263 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2264 WriteCoord(MSG_BROADCAST, end_x);
2265 WriteCoord(MSG_BROADCAST, end_y);
2266 WriteCoord(MSG_BROADCAST, end_z);
2267 WriteCoord(MSG_BROADCAST, end_dz);
2268 zcurveparticles_spd = spd;
2271 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2274 vector vecxy, velxy;
2276 vecxy = end - start;
2281 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2283 endzcurveparticles();
2284 trailparticles(world, effectno, start, end);
2288 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2289 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2292 void write_recordmarker(entity pl, float tstart, float dt)
2294 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2296 // also write a marker into demo files for demotc-race-record-extractor to find
2299 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2300 " ", ftos(tstart), " ", ftos(dt), "\n"));
2303 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2316 if(allowcenter) // 2: allow center handedness
2329 if(allowcenter) // 2: allow center handedness
2345 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2350 if (autocvar_g_shootfromeye)
2354 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2355 else { vecs_y = 0; vecs_z -= 2; }
2363 else if (autocvar_g_shootfromcenter)
2368 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2378 else if (autocvar_g_shootfromclient)
2380 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2385 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2387 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2391 void attach_sameorigin(entity e, entity to, string tag)
2393 vector org, t_forward, t_left, t_up, e_forward, e_up;
2396 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2397 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2398 t_forward = v_forward * tagscale;
2399 t_left = v_right * -tagscale;
2400 t_up = v_up * tagscale;
2402 e.origin_x = org * t_forward;
2403 e.origin_y = org * t_left;
2404 e.origin_z = org * t_up;
2406 // current forward and up directions
2407 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2408 e.angles = AnglesTransform_FromVAngles(e.angles);
2410 e.angles = AnglesTransform_FromAngles(e.angles);
2411 fixedmakevectors(e.angles);
2413 // untransform forward, up!
2414 e_forward_x = v_forward * t_forward;
2415 e_forward_y = v_forward * t_left;
2416 e_forward_z = v_forward * t_up;
2417 e_up_x = v_up * t_forward;
2418 e_up_y = v_up * t_left;
2419 e_up_z = v_up * t_up;
2421 e.angles = fixedvectoangles2(e_forward, e_up);
2422 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2423 e.angles = AnglesTransform_ToVAngles(e.angles);
2425 e.angles = AnglesTransform_ToAngles(e.angles);
2427 setattachment(e, to, tag);
2428 setorigin(e, e.origin);
2431 void detach_sameorigin(entity e)
2434 org = gettaginfo(e, 0);
2435 e.angles = fixedvectoangles2(v_forward, v_up);
2436 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2437 e.angles = AnglesTransform_ToVAngles(e.angles);
2439 e.angles = AnglesTransform_ToAngles(e.angles);
2441 setattachment(e, world, "");
2442 setorigin(e, e.origin);
2445 void follow_sameorigin(entity e, entity to)
2447 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2448 e.aiment = to; // make the hole follow bmodel
2449 e.punchangle = to.angles; // the original angles of bmodel
2450 e.view_ofs = e.origin - to.origin; // relative origin
2451 e.v_angle = e.angles - to.angles; // relative angles
2454 void unfollow_sameorigin(entity e)
2456 e.movetype = MOVETYPE_NONE;
2459 entity gettaginfo_relative_ent;
2460 vector gettaginfo_relative(entity e, float tag)
2462 if (!gettaginfo_relative_ent)
2464 gettaginfo_relative_ent = spawn();
2465 gettaginfo_relative_ent.effects = EF_NODRAW;
2467 gettaginfo_relative_ent.model = e.model;
2468 gettaginfo_relative_ent.modelindex = e.modelindex;
2469 gettaginfo_relative_ent.frame = e.frame;
2470 return gettaginfo(gettaginfo_relative_ent, tag);
2475 float modeleffect_SendEntity(entity to, float sf)
2478 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2481 if(self.velocity != '0 0 0')
2483 if(self.angles != '0 0 0')
2485 if(self.avelocity != '0 0 0')
2488 WriteByte(MSG_ENTITY, f);
2489 WriteShort(MSG_ENTITY, self.modelindex);
2490 WriteByte(MSG_ENTITY, self.skin);
2491 WriteByte(MSG_ENTITY, self.frame);
2492 WriteCoord(MSG_ENTITY, self.origin_x);
2493 WriteCoord(MSG_ENTITY, self.origin_y);
2494 WriteCoord(MSG_ENTITY, self.origin_z);
2497 WriteCoord(MSG_ENTITY, self.velocity_x);
2498 WriteCoord(MSG_ENTITY, self.velocity_y);
2499 WriteCoord(MSG_ENTITY, self.velocity_z);
2503 WriteCoord(MSG_ENTITY, self.angles_x);
2504 WriteCoord(MSG_ENTITY, self.angles_y);
2505 WriteCoord(MSG_ENTITY, self.angles_z);
2509 WriteCoord(MSG_ENTITY, self.avelocity_x);
2510 WriteCoord(MSG_ENTITY, self.avelocity_y);
2511 WriteCoord(MSG_ENTITY, self.avelocity_z);
2513 WriteShort(MSG_ENTITY, self.scale * 256.0);
2514 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2515 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2516 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2517 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2522 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)
2527 e.classname = "modeleffect";
2535 e.teleport_time = t1;
2539 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2543 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2546 sz = max(e.scale, e.scale2);
2547 setsize(e, e.mins * sz, e.maxs * sz);
2548 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2551 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2553 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2556 float randombit(float bits)
2558 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2567 for(f = 1; f <= bits; f *= 2)
2576 r = (r - 1) / (n - 1);
2583 float randombits(float bits, float k, float error_return)
2587 while(k > 0 && bits != r)
2589 r += randombit(bits - r);
2598 void randombit_test(float bits, float iter)
2602 print(ftos(randombit(bits)), "\n");
2607 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2609 if(halflifedist > 0)
2610 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2611 else if(halflifedist < 0)
2612 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2621 #define cvar_string_normal builtin_cvar_string
2622 #define cvar_normal builtin_cvar
2624 string cvar_string_normal(string n)
2626 if not(cvar_type(n) & 1)
2627 backtrace(strcat("Attempt to access undefined cvar: ", n));
2628 return builtin_cvar_string(n);
2631 float cvar_normal(string n)
2633 return stof(cvar_string_normal(n));
2636 #define cvar_set_normal builtin_cvar_set
2644 oself.think = SUB_Remove;
2645 oself.nextthink = time;
2651 Execute func() after time + fdelay.
2652 self when func is executed = self when defer is called
2654 void defer(float fdelay, void() func)
2661 e.think = defer_think;
2662 e.nextthink = time + fdelay;
2665 .string aiment_classname;
2666 .float aiment_deadflag;
2667 void SetMovetypeFollow(entity ent, entity e)
2669 // FIXME this may not be warpzone aware
2670 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2671 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.
2672 ent.aiment = e; // make the hole follow bmodel
2673 ent.punchangle = e.angles; // the original angles of bmodel
2674 ent.view_ofs = ent.origin - e.origin; // relative origin
2675 ent.v_angle = ent.angles - e.angles; // relative angles
2676 ent.aiment_classname = strzone(e.classname);
2677 ent.aiment_deadflag = e.deadflag;
2679 void UnsetMovetypeFollow(entity ent)
2681 ent.movetype = MOVETYPE_FLY;
2682 PROJECTILE_MAKETRIGGER(ent);
2685 float LostMovetypeFollow(entity ent)
2688 if(ent.movetype != MOVETYPE_FOLLOW)
2694 if(ent.aiment.classname != ent.aiment_classname)
2696 if(ent.aiment.deadflag != ent.aiment_deadflag)
2702 float isPushable(entity e)
2711 case "droppedweapon":
2712 case "keepawayball":
2713 case "nexball_basketball":
2714 case "nexball_football":
2716 case "bullet": // antilagged bullets can't hit this either
2719 if (e.projectiledeathtype)