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");
1522 precache_sound ("misc/jetpack_fly.wav");
1524 precache_model ("models/sprites/0.spr32");
1525 precache_model ("models/sprites/1.spr32");
1526 precache_model ("models/sprites/2.spr32");
1527 precache_model ("models/sprites/3.spr32");
1528 precache_model ("models/sprites/4.spr32");
1529 precache_model ("models/sprites/5.spr32");
1530 precache_model ("models/sprites/6.spr32");
1531 precache_model ("models/sprites/7.spr32");
1532 precache_model ("models/sprites/8.spr32");
1533 precache_model ("models/sprites/9.spr32");
1534 precache_model ("models/sprites/10.spr32");
1536 // common weapon precaches
1537 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1538 precache_sound ("weapons/weapon_switch.wav");
1539 precache_sound ("weapons/weaponpickup.wav");
1540 precache_sound ("weapons/unavailable.wav");
1541 precache_sound ("weapons/dryfire.wav");
1542 if (g_grappling_hook)
1544 precache_sound ("weapons/hook_fire.wav"); // hook
1545 precache_sound ("weapons/hook_impact.wav"); // hook
1548 if(autocvar_sv_precacheweapons)
1550 //precache weapon models/sounds
1553 while (wep <= WEP_LAST)
1555 weapon_action(wep, WR_PRECACHE);
1560 precache_model("models/elaser.mdl");
1561 precache_model("models/laser.mdl");
1562 precache_model("models/ebomb.mdl");
1565 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1567 if (!self.noise && self.music) // quake 3 uses the music field
1568 self.noise = self.music;
1570 // plays music for the level if there is any
1573 precache_sound (self.noise);
1574 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1579 // WARNING: this kills the trace globals
1580 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1581 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1583 #define INITPRIO_FIRST 0
1584 #define INITPRIO_GAMETYPE 0
1585 #define INITPRIO_GAMETYPE_FALLBACK 1
1586 #define INITPRIO_FINDTARGET 10
1587 #define INITPRIO_DROPTOFLOOR 20
1588 #define INITPRIO_SETLOCATION 90
1589 #define INITPRIO_LINKDOORS 91
1590 #define INITPRIO_LAST 99
1592 .void(void) initialize_entity;
1593 .float initialize_entity_order;
1594 .entity initialize_entity_next;
1595 entity initialize_entity_first;
1597 void make_safe_for_remove(entity e)
1599 if (e.initialize_entity)
1601 entity ent, prev = world;
1602 for (ent = initialize_entity_first; ent; )
1604 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1606 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1607 // skip it in linked list
1610 prev.initialize_entity_next = ent.initialize_entity_next;
1611 ent = prev.initialize_entity_next;
1615 initialize_entity_first = ent.initialize_entity_next;
1616 ent = initialize_entity_first;
1622 ent = ent.initialize_entity_next;
1628 void objerror(string s)
1630 make_safe_for_remove(self);
1631 builtin_objerror(s);
1634 .float remove_except_protected_forbidden;
1635 void remove_except_protected(entity e)
1637 if(e.remove_except_protected_forbidden)
1638 error("not allowed to remove this at this point");
1642 void remove_unsafely(entity e)
1644 if(e.classname == "spike")
1645 error("Removing spikes is forbidden (crylink bug), please report");
1649 void remove_safely(entity e)
1651 make_safe_for_remove(e);
1655 void InitializeEntity(entity e, void(void) func, float order)
1659 if (!e || e.initialize_entity)
1661 // make a proxy initializer entity
1665 e.classname = "initialize_entity";
1669 e.initialize_entity = func;
1670 e.initialize_entity_order = order;
1672 cur = initialize_entity_first;
1676 if (!cur || cur.initialize_entity_order > order)
1678 // insert between prev and cur
1680 prev.initialize_entity_next = e;
1682 initialize_entity_first = e;
1683 e.initialize_entity_next = cur;
1687 cur = cur.initialize_entity_next;
1690 void InitializeEntitiesRun()
1693 startoflist = initialize_entity_first;
1694 initialize_entity_first = world;
1695 remove = remove_except_protected;
1696 for (self = startoflist; self; self = self.initialize_entity_next)
1698 self.remove_except_protected_forbidden = 1;
1700 for (self = startoflist; self; )
1703 var void(void) func;
1704 e = self.initialize_entity_next;
1705 func = self.initialize_entity;
1706 self.initialize_entity_order = 0;
1707 self.initialize_entity = func_null;
1708 self.initialize_entity_next = world;
1709 self.remove_except_protected_forbidden = 0;
1710 if (self.classname == "initialize_entity")
1714 builtin_remove(self);
1717 //dprint("Delayed initialization: ", self.classname, "\n");
1718 if(func != func_null)
1723 backtrace(strcat("Null function in: ", self.classname, "\n"));
1727 remove = remove_unsafely;
1730 .float uncustomizeentityforclient_set;
1731 .void(void) uncustomizeentityforclient;
1732 void UncustomizeEntitiesRun()
1736 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1737 self.uncustomizeentityforclient();
1740 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1742 e.customizeentityforclient = customizer;
1743 e.uncustomizeentityforclient = uncustomizer;
1744 e.uncustomizeentityforclient_set = (uncustomizer != func_null);
1748 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1751 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1755 if (e.classname == "")
1756 e.classname = "net_linked";
1758 if (e.model == "" || self.modelindex == 0)
1762 setmodel(e, "null");
1766 e.SendEntity = sendfunc;
1767 e.SendFlags = 0xFFFFFF;
1770 e.effects |= EF_NODEPTHTEST;
1774 e.nextthink = time + dt;
1775 e.think = SUB_Remove;
1779 void adaptor_think2touch()
1788 void adaptor_think2use()
1800 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1802 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
1803 self.projectiledeathtype |= HITTYPE_SPLASH;
1804 adaptor_think2use();
1807 // deferred dropping
1808 void DropToFloor_Handler()
1810 builtin_droptofloor();
1811 self.dropped_origin = self.origin;
1816 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1821 float trace_hits_box_a0, trace_hits_box_a1;
1823 float trace_hits_box_1d(float end, float thmi, float thma)
1827 // just check if x is in range
1835 // do the trace with respect to x
1836 // 0 -> end has to stay in thmi -> thma
1837 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1838 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1839 if (trace_hits_box_a0 > trace_hits_box_a1)
1845 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1850 // now it is a trace from 0 to end
1852 trace_hits_box_a0 = 0;
1853 trace_hits_box_a1 = 1;
1855 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1857 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1859 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1865 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1867 return trace_hits_box(start, end, thmi - ma, thma - mi);
1870 float SUB_NoImpactCheck()
1872 // zero hitcontents = this is not the real impact, but either the
1873 // mirror-impact of something hitting the projectile instead of the
1874 // projectile hitting the something, or a touchareagrid one. Neither of
1875 // these stop the projectile from moving, so...
1876 if(trace_dphitcontents == 0)
1878 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1879 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)));
1882 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1884 if (other == world && self.size != '0 0 0')
1887 tic = self.velocity * sys_frametime;
1888 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1889 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1890 if (trace_fraction >= 1)
1892 dprint("Odd... did not hit...?\n");
1894 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1896 dprint("Detected and prevented the sky-grapple bug.\n");
1904 #define SUB_OwnerCheck() (other && (other == self.owner))
1906 void RemoveGrapplingHook(entity pl);
1907 void W_Crylink_Dequeue(entity e);
1908 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1910 if(SUB_OwnerCheck())
1912 if(SUB_NoImpactCheck())
1914 if(self.classname == "grapplinghook")
1915 RemoveGrapplingHook(self.realowner);
1916 else if(self.classname == "spike")
1918 W_Crylink_Dequeue(self);
1925 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1926 UpdateCSQCProjectile(self);
1929 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1931 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1932 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1934 void URI_Get_Callback(float id, float status, string data)
1936 if(url_URI_Get_Callback(id, status, data))
1940 else if (id == URI_GET_DISCARD)
1944 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1947 Curl_URI_Get_Callback(id, status, data);
1949 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1952 OnlineBanList_URI_Get_Callback(id, status, data);
1956 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1960 string uid2name(string myuid) {
1962 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1964 // FIXME remove this later after 0.6 release
1965 // convert old style broken records to correct style
1968 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1971 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1972 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1977 s = "^1Unregistered Player";
1981 float race_readTime(string map, float pos)
1989 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1992 string race_readUID(string map, float pos)
2000 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2003 float race_readPos(string map, float t) {
2005 for (i = 1; i <= RANKINGS_CNT; ++i)
2006 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2009 return 0; // pos is zero if unranked
2012 void race_writeTime(string map, float t, string myuid)
2021 newpos = race_readPos(map, t);
2023 float i, prevpos = 0;
2024 for(i = 1; i <= RANKINGS_CNT; ++i)
2026 if(race_readUID(map, i) == myuid)
2029 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2030 for (i = prevpos; i > newpos; --i) {
2031 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2032 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2034 } else { // player has no ranked record yet
2035 for (i = RANKINGS_CNT; i > newpos; --i) {
2036 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2037 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2041 // store new time itself
2042 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2043 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2046 string race_readName(string map, float pos)
2054 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2057 string race_placeName(float pos) {
2058 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2060 if(mod(pos, 10) == 1)
2061 return strcat(ftos(pos), "st");
2062 else if(mod(pos, 10) == 2)
2063 return strcat(ftos(pos), "nd");
2064 else if(mod(pos, 10) == 3)
2065 return strcat(ftos(pos), "rd");
2067 return strcat(ftos(pos), "th");
2070 return strcat(ftos(pos), "th");
2073 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2076 vector start, org, delta, end, enddown, mstart;
2079 m = e.dphitcontentsmask;
2080 e.dphitcontentsmask = goodcontents | badcontents;
2083 delta = world.maxs - world.mins;
2087 for (i = 0; i < attempts; ++i)
2089 start_x = org_x + random() * delta_x;
2090 start_y = org_y + random() * delta_y;
2091 start_z = org_z + random() * delta_z;
2093 // rule 1: start inside world bounds, and outside
2094 // solid, and don't start from somewhere where you can
2095 // fall down to evil
2096 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2097 if (trace_fraction >= 1)
2099 if (trace_startsolid)
2101 if (trace_dphitcontents & badcontents)
2103 if (trace_dphitq3surfaceflags & badsurfaceflags)
2106 // rule 2: if we are too high, lower the point
2107 if (trace_fraction * delta_z > maxaboveground)
2108 start = trace_endpos + '0 0 1' * maxaboveground;
2109 enddown = trace_endpos;
2111 // rule 3: make sure we aren't outside the map. This only works
2112 // for somewhat well formed maps. A good rule of thumb is that
2113 // the map should have a convex outside hull.
2114 // these can be traceLINES as we already verified the starting box
2115 mstart = start + 0.5 * (e.mins + e.maxs);
2116 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2117 if (trace_fraction >= 1)
2119 traceline(mstart, mstart - '1 0 0' * delta_x, 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 1 0' * delta_y, MOVE_NORMAL, e);
2126 if (trace_fraction >= 1)
2128 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2129 if (trace_fraction >= 1)
2132 // rule 4: we must "see" some spawnpoint
2133 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2134 if(checkpvs(mstart, sp))
2138 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2139 if(checkpvs(mstart, sp))
2145 // find a random vector to "look at"
2146 end_x = org_x + random() * delta_x;
2147 end_y = org_y + random() * delta_y;
2148 end_z = org_z + random() * delta_z;
2149 end = start + normalize(end - start) * vlen(delta);
2151 // rule 4: start TO end must not be too short
2152 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2153 if (trace_startsolid)
2155 if (trace_fraction < minviewdistance / vlen(delta))
2158 // rule 5: don't want to look at sky
2159 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2162 // rule 6: we must not end up in trigger_hurt
2163 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2169 e.dphitcontentsmask = m;
2173 setorigin(e, start);
2174 e.angles = vectoangles(end - start);
2175 dprint("Needed ", ftos(i + 1), " attempts\n");
2182 float zcurveparticles_effectno;
2183 vector zcurveparticles_start;
2184 float zcurveparticles_spd;
2186 void endzcurveparticles()
2188 if(zcurveparticles_effectno)
2191 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2193 zcurveparticles_effectno = 0;
2196 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2198 spd = bound(0, floor(spd / 16), 32767);
2199 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2201 endzcurveparticles();
2202 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2203 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2204 WriteShort(MSG_BROADCAST, effectno);
2205 WriteCoord(MSG_BROADCAST, start_x);
2206 WriteCoord(MSG_BROADCAST, start_y);
2207 WriteCoord(MSG_BROADCAST, start_z);
2208 zcurveparticles_effectno = effectno;
2209 zcurveparticles_start = start;
2212 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2213 WriteCoord(MSG_BROADCAST, end_x);
2214 WriteCoord(MSG_BROADCAST, end_y);
2215 WriteCoord(MSG_BROADCAST, end_z);
2216 WriteCoord(MSG_BROADCAST, end_dz);
2217 zcurveparticles_spd = spd;
2220 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2223 vector vecxy, velxy;
2225 vecxy = end - start;
2230 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2232 endzcurveparticles();
2233 trailparticles(world, effectno, start, end);
2237 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2238 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2241 void write_recordmarker(entity pl, float tstart, float dt)
2243 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2245 // also write a marker into demo files for demotc-race-record-extractor to find
2248 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2249 " ", ftos(tstart), " ", ftos(dt), "\n"));
2252 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2265 if(allowcenter) // 2: allow center handedness
2278 if(allowcenter) // 2: allow center handedness
2294 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2299 if (autocvar_g_shootfromeye)
2303 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2304 else { vecs_y = 0; vecs_z -= 2; }
2312 else if (autocvar_g_shootfromcenter)
2317 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2327 else if (autocvar_g_shootfromclient)
2329 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2334 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2336 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2340 void attach_sameorigin(entity e, entity to, string tag)
2342 vector org, t_forward, t_left, t_up, e_forward, e_up;
2345 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2346 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2347 t_forward = v_forward * tagscale;
2348 t_left = v_right * -tagscale;
2349 t_up = v_up * tagscale;
2351 e.origin_x = org * t_forward;
2352 e.origin_y = org * t_left;
2353 e.origin_z = org * t_up;
2355 // current forward and up directions
2356 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2357 e.angles = AnglesTransform_FromVAngles(e.angles);
2359 e.angles = AnglesTransform_FromAngles(e.angles);
2360 fixedmakevectors(e.angles);
2362 // untransform forward, up!
2363 e_forward_x = v_forward * t_forward;
2364 e_forward_y = v_forward * t_left;
2365 e_forward_z = v_forward * t_up;
2366 e_up_x = v_up * t_forward;
2367 e_up_y = v_up * t_left;
2368 e_up_z = v_up * t_up;
2370 e.angles = fixedvectoangles2(e_forward, e_up);
2371 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2372 e.angles = AnglesTransform_ToVAngles(e.angles);
2374 e.angles = AnglesTransform_ToAngles(e.angles);
2376 setattachment(e, to, tag);
2377 setorigin(e, e.origin);
2380 void detach_sameorigin(entity e)
2383 org = gettaginfo(e, 0);
2384 e.angles = fixedvectoangles2(v_forward, v_up);
2385 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2386 e.angles = AnglesTransform_ToVAngles(e.angles);
2388 e.angles = AnglesTransform_ToAngles(e.angles);
2390 setattachment(e, world, "");
2391 setorigin(e, e.origin);
2394 void follow_sameorigin(entity e, entity to)
2396 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2397 e.aiment = to; // make the hole follow bmodel
2398 e.punchangle = to.angles; // the original angles of bmodel
2399 e.view_ofs = e.origin - to.origin; // relative origin
2400 e.v_angle = e.angles - to.angles; // relative angles
2403 void unfollow_sameorigin(entity e)
2405 e.movetype = MOVETYPE_NONE;
2408 entity gettaginfo_relative_ent;
2409 vector gettaginfo_relative(entity e, float tag)
2411 if (!gettaginfo_relative_ent)
2413 gettaginfo_relative_ent = spawn();
2414 gettaginfo_relative_ent.effects = EF_NODRAW;
2416 gettaginfo_relative_ent.model = e.model;
2417 gettaginfo_relative_ent.modelindex = e.modelindex;
2418 gettaginfo_relative_ent.frame = e.frame;
2419 return gettaginfo(gettaginfo_relative_ent, tag);
2422 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2426 if (pl.soundentity.cnt & p)
2428 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2429 pl.soundentity.cnt |= p;
2432 void SoundEntity_StopSound(entity pl, float chan)
2436 if (pl.soundentity.cnt & p)
2438 stopsoundto(MSG_ALL, pl.soundentity, chan);
2439 pl.soundentity.cnt &~= p;
2443 void SoundEntity_Attach(entity pl)
2445 pl.soundentity = spawn();
2446 pl.soundentity.classname = "soundentity";
2447 pl.soundentity.owner = pl;
2448 setattachment(pl.soundentity, pl, "");
2449 setmodel(pl.soundentity, "null");
2452 void SoundEntity_Detach(entity pl)
2455 for (i = 0; i <= 7; ++i)
2456 SoundEntity_StopSound(pl, i);
2461 float modeleffect_SendEntity(entity to, float sf)
2464 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2467 if(self.velocity != '0 0 0')
2469 if(self.angles != '0 0 0')
2471 if(self.avelocity != '0 0 0')
2474 WriteByte(MSG_ENTITY, f);
2475 WriteShort(MSG_ENTITY, self.modelindex);
2476 WriteByte(MSG_ENTITY, self.skin);
2477 WriteByte(MSG_ENTITY, self.frame);
2478 WriteCoord(MSG_ENTITY, self.origin_x);
2479 WriteCoord(MSG_ENTITY, self.origin_y);
2480 WriteCoord(MSG_ENTITY, self.origin_z);
2483 WriteCoord(MSG_ENTITY, self.velocity_x);
2484 WriteCoord(MSG_ENTITY, self.velocity_y);
2485 WriteCoord(MSG_ENTITY, self.velocity_z);
2489 WriteCoord(MSG_ENTITY, self.angles_x);
2490 WriteCoord(MSG_ENTITY, self.angles_y);
2491 WriteCoord(MSG_ENTITY, self.angles_z);
2495 WriteCoord(MSG_ENTITY, self.avelocity_x);
2496 WriteCoord(MSG_ENTITY, self.avelocity_y);
2497 WriteCoord(MSG_ENTITY, self.avelocity_z);
2499 WriteShort(MSG_ENTITY, self.scale * 256.0);
2500 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2501 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2502 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2503 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2508 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)
2513 e.classname = "modeleffect";
2521 e.teleport_time = t1;
2525 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2529 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2532 sz = max(e.scale, e.scale2);
2533 setsize(e, e.mins * sz, e.maxs * sz);
2534 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2537 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2539 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2542 float randombit(float bits)
2544 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2553 for(f = 1; f <= bits; f *= 2)
2562 r = (r - 1) / (n - 1);
2569 float randombits(float bits, float k, float error_return)
2573 while(k > 0 && bits != r)
2575 r += randombit(bits - r);
2584 void randombit_test(float bits, float iter)
2588 print(ftos(randombit(bits)), "\n");
2593 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2595 if(halflifedist > 0)
2596 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2597 else if(halflifedist < 0)
2598 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2607 #define cvar_string_normal builtin_cvar_string
2608 #define cvar_normal builtin_cvar
2610 string cvar_string_normal(string n)
2612 if not(cvar_type(n) & 1)
2613 backtrace(strcat("Attempt to access undefined cvar: ", n));
2614 return builtin_cvar_string(n);
2617 float cvar_normal(string n)
2619 return stof(cvar_string_normal(n));
2622 #define cvar_set_normal builtin_cvar_set
2630 oself.think = SUB_Remove;
2631 oself.nextthink = time;
2637 Execute func() after time + fdelay.
2638 self when func is executed = self when defer is called
2640 void defer(float fdelay, void() func)
2647 e.think = defer_think;
2648 e.nextthink = time + fdelay;
2651 .string aiment_classname;
2652 .float aiment_deadflag;
2653 void SetMovetypeFollow(entity ent, entity e)
2655 // FIXME this may not be warpzone aware
2656 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2657 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.
2658 ent.aiment = e; // make the hole follow bmodel
2659 ent.punchangle = e.angles; // the original angles of bmodel
2660 ent.view_ofs = ent.origin - e.origin; // relative origin
2661 ent.v_angle = ent.angles - e.angles; // relative angles
2662 ent.aiment_classname = strzone(e.classname);
2663 ent.aiment_deadflag = e.deadflag;
2665 void UnsetMovetypeFollow(entity ent)
2667 ent.movetype = MOVETYPE_FLY;
2668 PROJECTILE_MAKETRIGGER(ent);
2671 float LostMovetypeFollow(entity ent)
2674 if(ent.movetype != MOVETYPE_FOLLOW)
2680 if(ent.aiment.classname != ent.aiment_classname)
2682 if(ent.aiment.deadflag != ent.aiment_deadflag)
2688 float isPushable(entity e)
2697 case "droppedweapon":
2698 case "keepawayball":
2699 case "nexball_basketball":
2700 case "nexball_football":
2702 case "bullet": // antilagged bullets can't hit this either
2705 if (e.projectiledeathtype)
2710 void dedicated_print(string input) // print(), but only print if the server is not local
2712 if not(server_is_local) { print(input); }