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 // decolorizes and team colors the player name when needed
597 string playername(entity p)
600 if (teamplay && !intermission_running && p.classname == "player")
602 t = Team_ColorCode(p.team);
603 return strcat(t, strdecolorize(p.netname));
609 vector randompos(vector m1, vector m2)
613 v_x = m2_x * random() + m1_x;
614 v_y = m2_y * random() + m1_y;
615 v_z = m2_z * random() + m1_z;
619 //#NO AUTOCVARS START
621 float g_pickup_shells;
622 float g_pickup_shells_max;
623 float g_pickup_nails;
624 float g_pickup_nails_max;
625 float g_pickup_rockets;
626 float g_pickup_rockets_max;
627 float g_pickup_cells;
628 float g_pickup_cells_max;
630 float g_pickup_fuel_jetpack;
631 float g_pickup_fuel_max;
632 float g_pickup_armorsmall;
633 float g_pickup_armorsmall_max;
634 float g_pickup_armorsmall_anyway;
635 float g_pickup_armormedium;
636 float g_pickup_armormedium_max;
637 float g_pickup_armormedium_anyway;
638 float g_pickup_armorbig;
639 float g_pickup_armorbig_max;
640 float g_pickup_armorbig_anyway;
641 float g_pickup_armorlarge;
642 float g_pickup_armorlarge_max;
643 float g_pickup_armorlarge_anyway;
644 float g_pickup_healthsmall;
645 float g_pickup_healthsmall_max;
646 float g_pickup_healthsmall_anyway;
647 float g_pickup_healthmedium;
648 float g_pickup_healthmedium_max;
649 float g_pickup_healthmedium_anyway;
650 float g_pickup_healthlarge;
651 float g_pickup_healthlarge_max;
652 float g_pickup_healthlarge_anyway;
653 float g_pickup_healthmega;
654 float g_pickup_healthmega_max;
655 float g_pickup_healthmega_anyway;
656 float g_pickup_ammo_anyway;
657 float g_pickup_weapons_anyway;
659 WEPSET_DECLARE_A(g_weaponarena_weapons);
660 float g_weaponarena_random;
661 float g_weaponarena_random_with_laser;
662 string g_weaponarena_list;
663 float g_weaponspeedfactor;
664 float g_weaponratefactor;
665 float g_weapondamagefactor;
666 float g_weaponforcefactor;
667 float g_weaponspreadfactor;
669 WEPSET_DECLARE_A(start_weapons);
670 WEPSET_DECLARE_A(start_weapons_default);
671 WEPSET_DECLARE_A(start_weapons_defaultmask);
673 float start_ammo_shells;
674 float start_ammo_nails;
675 float start_ammo_rockets;
676 float start_ammo_cells;
677 float start_ammo_fuel;
679 float start_armorvalue;
680 WEPSET_DECLARE_A(warmup_start_weapons);
681 WEPSET_DECLARE_A(warmup_start_weapons_default);
682 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
683 float warmup_start_ammo_shells;
684 float warmup_start_ammo_nails;
685 float warmup_start_ammo_rockets;
686 float warmup_start_ammo_cells;
687 float warmup_start_ammo_fuel;
688 float warmup_start_health;
689 float warmup_start_armorvalue;
692 entity get_weaponinfo(float w);
694 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
696 var float i = weaponinfo.weapon;
702 if (g_lms || g_ca || allguns)
704 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
710 d = (i == WEP_SHOTGUN);
712 d = 0; // weapon is set a few lines later
714 d = (i == WEP_LASER || i == WEP_SHOTGUN);
716 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
717 d |= (i == WEP_HOOK);
718 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
721 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
723 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
728 // 4: is set by default?
737 void readplayerstartcvars()
743 // initialize starting values for players
744 WEPSET_CLEAR_A(start_weapons);
745 WEPSET_CLEAR_A(start_weapons_default);
746 WEPSET_CLEAR_A(start_weapons_defaultmask);
748 start_ammo_shells = 0;
749 start_ammo_nails = 0;
750 start_ammo_rockets = 0;
751 start_ammo_cells = 0;
752 start_health = cvar("g_balance_health_start");
753 start_armorvalue = cvar("g_balance_armor_start");
756 WEPSET_CLEAR_A(g_weaponarena_weapons);
758 s = cvar_string("g_weaponarena");
759 if (s == "0" || s == "")
765 if (s == "0" || s == "")
771 // forcibly turn off weaponarena
776 g_weaponarena_list = "All Weapons";
777 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
779 e = get_weaponinfo(j);
780 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
781 WEPSET_OR_AW(g_weaponarena_weapons, j);
784 else if (s == "most")
787 g_weaponarena_list = "Most Weapons";
788 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
790 e = get_weaponinfo(j);
791 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
792 if (e.spawnflags & WEP_FLAG_NORMAL)
793 WEPSET_OR_AW(g_weaponarena_weapons, j);
796 else if (s == "none")
799 g_weaponarena_list = "No Weapons";
804 t = tokenize_console(s);
805 g_weaponarena_list = "";
806 for (i = 0; i < t; ++i)
809 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
811 e = get_weaponinfo(j);
814 WEPSET_OR_AW(g_weaponarena_weapons, j);
815 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
821 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
824 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
828 g_weaponarena_random = cvar("g_weaponarena_random");
830 g_weaponarena_random = 0;
831 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
835 g_minstagib = 0; // incompatible
836 g_pinata = 0; // incompatible
837 g_weapon_stay = 0; // incompatible
838 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
840 start_items |= IT_UNLIMITED_AMMO;
842 else if (g_minstagib)
844 g_pinata = 0; // incompatible
845 g_weapon_stay = 0; // incompatible
846 g_bloodloss = 0; // incompatible
848 start_armorvalue = 0;
849 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
850 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
851 start_items |= IT_UNLIMITED_SUPERWEAPONS;
853 if (g_minstagib_invis_alpha <= 0)
854 g_minstagib_invis_alpha = -1;
858 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
860 e = get_weaponinfo(i);
861 float w = want_weapon("g_start_weapon_", e, FALSE);
863 WEPSET_OR_AW(start_weapons, i);
865 WEPSET_OR_AW(start_weapons_default, i);
867 WEPSET_OR_AW(start_weapons_defaultmask, i);
871 if(!cvar("g_use_ammunition"))
872 start_items |= IT_UNLIMITED_AMMO;
874 if(cvar("g_nexball"))
875 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
879 start_ammo_cells = cvar("g_minstagib_ammo_start");
880 start_ammo_fuel = cvar("g_start_ammo_fuel");
882 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
884 start_ammo_rockets = 999;
885 start_ammo_shells = 999;
886 start_ammo_cells = 999;
887 start_ammo_nails = 999;
888 start_ammo_fuel = 999;
894 start_ammo_shells = cvar("g_lms_start_ammo_shells");
895 start_ammo_nails = cvar("g_lms_start_ammo_nails");
896 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
897 start_ammo_cells = cvar("g_lms_start_ammo_cells");
898 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
902 start_ammo_shells = cvar("g_start_ammo_shells");
903 start_ammo_nails = cvar("g_start_ammo_nails");
904 start_ammo_rockets = cvar("g_start_ammo_rockets");
905 start_ammo_cells = cvar("g_start_ammo_cells");
906 start_ammo_fuel = cvar("g_start_ammo_fuel");
912 start_health = cvar("g_lms_start_health");
913 start_armorvalue = cvar("g_lms_start_armor");
918 warmup_start_ammo_shells = start_ammo_shells;
919 warmup_start_ammo_nails = start_ammo_nails;
920 warmup_start_ammo_rockets = start_ammo_rockets;
921 warmup_start_ammo_cells = start_ammo_cells;
922 warmup_start_ammo_fuel = start_ammo_fuel;
923 warmup_start_health = start_health;
924 warmup_start_armorvalue = start_armorvalue;
925 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
926 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
927 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
929 if (!g_weaponarena && !g_minstagib && !g_ca)
931 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
932 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
933 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
934 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
935 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
936 warmup_start_health = cvar("g_warmup_start_health");
937 warmup_start_armorvalue = cvar("g_warmup_start_armor");
938 WEPSET_CLEAR_A(warmup_start_weapons);
939 WEPSET_CLEAR_A(warmup_start_weapons_default);
940 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
941 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
943 e = get_weaponinfo(i);
944 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
946 WEPSET_OR_AW(warmup_start_weapons, i);
948 WEPSET_OR_AW(warmup_start_weapons_default, i);
950 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
956 start_items |= IT_JETPACK;
958 MUTATOR_CALLHOOK(SetStartItems);
960 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
962 g_grappling_hook = 0; // these two can't coexist, as they use the same button
963 start_items |= IT_FUEL_REGEN;
964 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
965 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
968 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
970 e = get_weaponinfo(i);
971 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
972 weapon_action(i, WR_PRECACHE);
975 start_ammo_shells = max(0, start_ammo_shells);
976 start_ammo_nails = max(0, start_ammo_nails);
977 start_ammo_cells = max(0, start_ammo_cells);
978 start_ammo_rockets = max(0, start_ammo_rockets);
979 start_ammo_fuel = max(0, start_ammo_fuel);
981 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
982 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
983 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
984 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
985 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
989 float g_bugrigs_planar_movement;
990 float g_bugrigs_planar_movement_car_jumping;
991 float g_bugrigs_reverse_spinning;
992 float g_bugrigs_reverse_speeding;
993 float g_bugrigs_reverse_stopping;
994 float g_bugrigs_air_steering;
995 float g_bugrigs_angle_smoothing;
996 float g_bugrigs_friction_floor;
997 float g_bugrigs_friction_brake;
998 float g_bugrigs_friction_air;
999 float g_bugrigs_accel;
1000 float g_bugrigs_speed_ref;
1001 float g_bugrigs_speed_pow;
1002 float g_bugrigs_steer;
1004 float g_touchexplode;
1005 float g_touchexplode_radius;
1006 float g_touchexplode_damage;
1007 float g_touchexplode_edgedamage;
1008 float g_touchexplode_force;
1015 float sv_pitch_fixyaw;
1017 string GetGametype(); // g_world.qc
1018 void readlevelcvars(void)
1020 g_minstagib = cvar("g_minstagib");
1022 // load ALL the mutators
1023 if(cvar("g_dodging"))
1024 MUTATOR_ADD(mutator_dodging);
1025 if(cvar("g_spawn_near_teammate"))
1026 MUTATOR_ADD(mutator_spawn_near_teammate);
1027 if(cvar("g_physical_items"))
1028 MUTATOR_ADD(mutator_physical_items);
1031 if(cvar("g_invincible_projectiles"))
1032 MUTATOR_ADD(mutator_invincibleprojectiles);
1033 if(cvar("g_new_toys"))
1034 MUTATOR_ADD(mutator_new_toys);
1036 MUTATOR_ADD(mutator_nix);
1037 if(cvar("g_rocket_flying"))
1038 MUTATOR_ADD(mutator_rocketflying);
1039 if(cvar("g_vampire"))
1040 MUTATOR_ADD(mutator_vampire);
1041 if(cvar("g_superspectate"))
1042 MUTATOR_ADD(mutator_superspec);
1045 // is this a mutator? is this a mode?
1046 if(cvar("g_sandbox"))
1047 MUTATOR_ADD(sandbox);
1049 if(cvar("sv_allow_fullbright"))
1050 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1052 g_bugrigs = cvar("g_bugrigs");
1053 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1054 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1055 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1056 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1057 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1058 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1059 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1060 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1061 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1062 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1063 g_bugrigs_accel = cvar("g_bugrigs_accel");
1064 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1065 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1066 g_bugrigs_steer = cvar("g_bugrigs_steer");
1068 g_touchexplode = cvar("g_touchexplode");
1069 g_touchexplode_radius = cvar("g_touchexplode_radius");
1070 g_touchexplode_damage = cvar("g_touchexplode_damage");
1071 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1072 g_touchexplode_force = cvar("g_touchexplode_force");
1074 #ifdef ALLOW_FORCEMODELS
1075 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1078 sv_clones = cvar("sv_clones");
1079 sv_foginterval = cvar("sv_foginterval");
1080 g_cloaked = cvar("g_cloaked");
1082 g_cloaked = 1; // always enable cloak in CTS
1083 g_jump_grunt = cvar("g_jump_grunt");
1084 g_footsteps = cvar("g_footsteps");
1085 g_grappling_hook = cvar("g_grappling_hook");
1086 g_jetpack = cvar("g_jetpack");
1087 g_midair = cvar("g_midair");
1088 g_norecoil = cvar("g_norecoil");
1089 g_bloodloss = cvar("g_bloodloss");
1090 sv_maxidle = cvar("sv_maxidle");
1091 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1092 sv_autotaunt = cvar("sv_autotaunt");
1093 sv_taunt = cvar("sv_taunt");
1095 inWarmupStage = cvar("g_warmup");
1096 g_warmup_limit = cvar("g_warmup_limit");
1097 g_warmup_allguns = cvar("g_warmup_allguns");
1098 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1100 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1101 inWarmupStage = 0; // these modes cannot work together, sorry
1103 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1104 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1105 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1106 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1107 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1108 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1109 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1110 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1111 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1112 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1113 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1114 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1115 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1116 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1118 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1119 g_weaponratefactor = cvar("g_weaponratefactor");
1120 g_weapondamagefactor = cvar("g_weapondamagefactor");
1121 g_weaponforcefactor = cvar("g_weaponforcefactor");
1122 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1124 g_pickup_shells = cvar("g_pickup_shells");
1125 g_pickup_shells_max = cvar("g_pickup_shells_max");
1126 g_pickup_nails = cvar("g_pickup_nails");
1127 g_pickup_nails_max = cvar("g_pickup_nails_max");
1128 g_pickup_rockets = cvar("g_pickup_rockets");
1129 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1130 g_pickup_cells = cvar("g_pickup_cells");
1131 g_pickup_cells_max = cvar("g_pickup_cells_max");
1132 g_pickup_fuel = cvar("g_pickup_fuel");
1133 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1134 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1135 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1136 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1137 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1138 g_pickup_armormedium = cvar("g_pickup_armormedium");
1139 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1140 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1141 g_pickup_armorbig = cvar("g_pickup_armorbig");
1142 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1143 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1144 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1145 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1146 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1147 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1148 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1149 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1150 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1151 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1152 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1153 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1154 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1155 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1156 g_pickup_healthmega = cvar("g_pickup_healthmega");
1157 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1158 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1160 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1161 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1163 g_pinata = cvar("g_pinata");
1165 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1167 g_weapon_stay = cvar("g_weapon_stay");
1169 if not(inWarmupStage && !g_ca)
1170 game_starttime = cvar("g_start_delay");
1172 sv_pitch_min = cvar("sv_pitch_min");
1173 sv_pitch_max = cvar("sv_pitch_max");
1174 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1176 readplayerstartcvars();
1182 string precache_sound (string s) = #19;
1183 float precache_sound_index (string s) = #19;
1185 #define SND_VOLUME 1
1186 #define SND_ATTENUATION 2
1187 #define SND_LARGEENTITY 8
1188 #define SND_LARGESOUND 16
1190 float sound_allowed(float dest, entity e)
1192 // sounds from world may always pass
1195 if (e.classname == "body")
1197 else if (e.realowner && e.realowner != e)
1199 else if (e.owner && e.owner != e)
1204 // sounds to self may always pass
1205 if (dest == MSG_ONE)
1206 if (e == msg_entity)
1208 // sounds by players can be removed
1209 if (autocvar_bot_sound_monopoly)
1210 if (clienttype(e) == CLIENTTYPE_REAL)
1212 // anything else may pass
1216 #ifdef COMPAT_XON010_CHANNELS
1217 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1218 void sound(entity e, float chan, string samp, float vol, float atten)
1220 if (!sound_allowed(MSG_BROADCAST, e))
1222 builtin_sound(e, chan, samp, vol, atten);
1226 void sound(entity e, float chan, string samp, float vol, float atten)
1228 if (!sound_allowed(MSG_BROADCAST, e))
1230 sound7(e, chan, samp, vol, atten, 0, 0);
1234 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1238 if (!sound_allowed(dest, e))
1241 entno = num_for_edict(e);
1242 idx = precache_sound_index(samp);
1247 atten = floor(atten * 64);
1248 vol = floor(vol * 255);
1251 sflags |= SND_VOLUME;
1253 sflags |= SND_ATTENUATION;
1254 if (entno >= 8192 || chan < 0 || chan > 7)
1255 sflags |= SND_LARGEENTITY;
1257 sflags |= SND_LARGESOUND;
1259 WriteByte(dest, SVC_SOUND);
1260 WriteByte(dest, sflags);
1261 if (sflags & SND_VOLUME)
1262 WriteByte(dest, vol);
1263 if (sflags & SND_ATTENUATION)
1264 WriteByte(dest, atten);
1265 if (sflags & SND_LARGEENTITY)
1267 WriteShort(dest, entno);
1268 WriteByte(dest, chan);
1272 WriteShort(dest, entno * 8 + chan);
1274 if (sflags & SND_LARGESOUND)
1275 WriteShort(dest, idx);
1277 WriteByte(dest, idx);
1279 WriteCoord(dest, o_x);
1280 WriteCoord(dest, o_y);
1281 WriteCoord(dest, o_z);
1283 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1287 if (!sound_allowed(dest, e))
1290 o = e.origin + 0.5 * (e.mins + e.maxs);
1291 soundtoat(dest, e, o, chan, samp, vol, atten);
1293 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1295 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1297 void stopsoundto(float dest, entity e, float chan)
1301 if (!sound_allowed(dest, e))
1304 entno = num_for_edict(e);
1306 if (entno >= 8192 || chan < 0 || chan > 7)
1309 idx = precache_sound_index("misc/null.wav");
1310 sflags = SND_LARGEENTITY;
1312 sflags |= SND_LARGESOUND;
1313 WriteByte(dest, SVC_SOUND);
1314 WriteByte(dest, sflags);
1315 WriteShort(dest, entno);
1316 WriteByte(dest, chan);
1317 if (sflags & SND_LARGESOUND)
1318 WriteShort(dest, idx);
1320 WriteByte(dest, idx);
1321 WriteCoord(dest, e.origin_x);
1322 WriteCoord(dest, e.origin_y);
1323 WriteCoord(dest, e.origin_z);
1327 WriteByte(dest, SVC_STOPSOUND);
1328 WriteShort(dest, entno * 8 + chan);
1331 void stopsound(entity e, float chan)
1333 if (!sound_allowed(MSG_BROADCAST, e))
1336 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1337 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1340 void play2(entity e, string filename)
1342 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1344 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1347 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1349 float spamsound(entity e, float chan, string samp, float vol, float atten)
1351 if (!sound_allowed(MSG_BROADCAST, e))
1354 if (time > e.spamtime)
1357 sound(e, chan, samp, vol, atten);
1363 void play2team(float t, string filename)
1367 if (autocvar_bot_sound_monopoly)
1370 FOR_EACH_REALPLAYER(head)
1373 play2(head, filename);
1377 void play2all(string samp)
1379 if (autocvar_bot_sound_monopoly)
1382 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1385 void PrecachePlayerSounds(string f);
1386 void precache_playermodel(string m)
1388 float globhandle, i, n;
1391 if(substring(m, -9,5) == "_lod1")
1393 if(substring(m, -9,5) == "_lod2")
1396 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1399 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1403 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1406 n = search_getsize(globhandle);
1407 for (i = 0; i < n; ++i)
1409 //print(search_getfilename(globhandle, i), "\n");
1410 f = search_getfilename(globhandle, i);
1411 PrecachePlayerSounds(f);
1413 search_end(globhandle);
1415 void precache_all_playermodels(string pattern)
1417 float globhandle, i, n;
1420 globhandle = search_begin(pattern, TRUE, FALSE);
1423 n = search_getsize(globhandle);
1424 for (i = 0; i < n; ++i)
1426 //print(search_getfilename(globhandle, i), "\n");
1427 f = search_getfilename(globhandle, i);
1428 precache_playermodel(f);
1430 search_end(globhandle);
1435 // gamemode related things
1436 precache_model ("models/misc/chatbubble.spr");
1439 precache_model ("models/runematch/curse.mdl");
1440 precache_model ("models/runematch/rune.mdl");
1443 #ifdef TTURRETS_ENABLED
1444 if (autocvar_g_turrets)
1448 // Precache all player models if desired
1449 if (autocvar_sv_precacheplayermodels)
1451 PrecachePlayerSounds("sound/player/default.sounds");
1452 precache_all_playermodels("models/player/*.zym");
1453 precache_all_playermodels("models/player/*.dpm");
1454 precache_all_playermodels("models/player/*.md3");
1455 precache_all_playermodels("models/player/*.psk");
1456 precache_all_playermodels("models/player/*.iqm");
1459 if (autocvar_sv_defaultcharacter)
1462 s = autocvar_sv_defaultplayermodel_red;
1464 precache_playermodel(s);
1465 s = autocvar_sv_defaultplayermodel_blue;
1467 precache_playermodel(s);
1468 s = autocvar_sv_defaultplayermodel_yellow;
1470 precache_playermodel(s);
1471 s = autocvar_sv_defaultplayermodel_pink;
1473 precache_playermodel(s);
1474 s = autocvar_sv_defaultplayermodel;
1476 precache_playermodel(s);
1481 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1482 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1485 // gore and miscellaneous sounds
1486 //precache_sound ("misc/h2ohit.wav");
1487 precache_model ("models/hook.md3");
1488 precache_sound ("misc/armorimpact.wav");
1489 precache_sound ("misc/bodyimpact1.wav");
1490 precache_sound ("misc/bodyimpact2.wav");
1491 precache_sound ("misc/gib.wav");
1492 precache_sound ("misc/gib_splat01.wav");
1493 precache_sound ("misc/gib_splat02.wav");
1494 precache_sound ("misc/gib_splat03.wav");
1495 precache_sound ("misc/gib_splat04.wav");
1496 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1497 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1498 precache_sound ("misc/null.wav");
1499 precache_sound ("misc/spawn.wav");
1500 precache_sound ("misc/talk.wav");
1501 precache_sound ("misc/teleport.wav");
1502 precache_sound ("misc/poweroff.wav");
1503 precache_sound ("player/lava.wav");
1504 precache_sound ("player/slime.wav");
1506 precache_model ("models/sprites/0.spr32");
1507 precache_model ("models/sprites/1.spr32");
1508 precache_model ("models/sprites/2.spr32");
1509 precache_model ("models/sprites/3.spr32");
1510 precache_model ("models/sprites/4.spr32");
1511 precache_model ("models/sprites/5.spr32");
1512 precache_model ("models/sprites/6.spr32");
1513 precache_model ("models/sprites/7.spr32");
1514 precache_model ("models/sprites/8.spr32");
1515 precache_model ("models/sprites/9.spr32");
1516 precache_model ("models/sprites/10.spr32");
1518 // common weapon precaches
1519 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1520 precache_sound ("weapons/weapon_switch.wav");
1521 precache_sound ("weapons/weaponpickup.wav");
1522 precache_sound ("weapons/unavailable.wav");
1523 precache_sound ("weapons/dryfire.wav");
1524 if (g_grappling_hook)
1526 precache_sound ("weapons/hook_fire.wav"); // hook
1527 precache_sound ("weapons/hook_impact.wav"); // hook
1530 if(autocvar_sv_precacheweapons)
1532 //precache weapon models/sounds
1535 while (wep <= WEP_LAST)
1537 weapon_action(wep, WR_PRECACHE);
1542 precache_model("models/elaser.mdl");
1543 precache_model("models/laser.mdl");
1544 precache_model("models/ebomb.mdl");
1547 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1549 if (!self.noise && self.music) // quake 3 uses the music field
1550 self.noise = self.music;
1552 // plays music for the level if there is any
1555 precache_sound (self.noise);
1556 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1561 // WARNING: this kills the trace globals
1562 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1563 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1565 #define INITPRIO_FIRST 0
1566 #define INITPRIO_GAMETYPE 0
1567 #define INITPRIO_GAMETYPE_FALLBACK 1
1568 #define INITPRIO_FINDTARGET 10
1569 #define INITPRIO_DROPTOFLOOR 20
1570 #define INITPRIO_SETLOCATION 90
1571 #define INITPRIO_LINKDOORS 91
1572 #define INITPRIO_LAST 99
1574 .void(void) initialize_entity;
1575 .float initialize_entity_order;
1576 .entity initialize_entity_next;
1577 entity initialize_entity_first;
1579 void make_safe_for_remove(entity e)
1581 if (e.initialize_entity)
1583 entity ent, prev = world;
1584 for (ent = initialize_entity_first; ent; )
1586 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1588 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1589 // skip it in linked list
1592 prev.initialize_entity_next = ent.initialize_entity_next;
1593 ent = prev.initialize_entity_next;
1597 initialize_entity_first = ent.initialize_entity_next;
1598 ent = initialize_entity_first;
1604 ent = ent.initialize_entity_next;
1610 void objerror(string s)
1612 make_safe_for_remove(self);
1613 builtin_objerror(s);
1616 .float remove_except_protected_forbidden;
1617 void remove_except_protected(entity e)
1619 if(e.remove_except_protected_forbidden)
1620 error("not allowed to remove this at this point");
1624 void remove_unsafely(entity e)
1626 if(e.classname == "spike")
1627 error("Removing spikes is forbidden (crylink bug), please report");
1631 void remove_safely(entity e)
1633 make_safe_for_remove(e);
1637 void InitializeEntity(entity e, void(void) func, float order)
1641 if (!e || e.initialize_entity)
1643 // make a proxy initializer entity
1647 e.classname = "initialize_entity";
1651 e.initialize_entity = func;
1652 e.initialize_entity_order = order;
1654 cur = initialize_entity_first;
1658 if (!cur || cur.initialize_entity_order > order)
1660 // insert between prev and cur
1662 prev.initialize_entity_next = e;
1664 initialize_entity_first = e;
1665 e.initialize_entity_next = cur;
1669 cur = cur.initialize_entity_next;
1672 void InitializeEntitiesRun()
1675 startoflist = initialize_entity_first;
1676 initialize_entity_first = world;
1677 remove = remove_except_protected;
1678 for (self = startoflist; self; self = self.initialize_entity_next)
1680 self.remove_except_protected_forbidden = 1;
1682 for (self = startoflist; self; )
1685 var void(void) func;
1686 e = self.initialize_entity_next;
1687 func = self.initialize_entity;
1688 self.initialize_entity_order = 0;
1689 self.initialize_entity = func_null;
1690 self.initialize_entity_next = world;
1691 self.remove_except_protected_forbidden = 0;
1692 if (self.classname == "initialize_entity")
1696 builtin_remove(self);
1699 //dprint("Delayed initialization: ", self.classname, "\n");
1705 backtrace(strcat("Null function in: ", self.classname, "\n"));
1709 remove = remove_unsafely;
1712 .float uncustomizeentityforclient_set;
1713 .void(void) uncustomizeentityforclient;
1714 void UncustomizeEntitiesRun()
1718 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1719 self.uncustomizeentityforclient();
1722 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1724 e.customizeentityforclient = customizer;
1725 e.uncustomizeentityforclient = uncustomizer;
1726 e.uncustomizeentityforclient_set = !!uncustomizer;
1730 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1733 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1737 if (e.classname == "")
1738 e.classname = "net_linked";
1740 if (e.model == "" || self.modelindex == 0)
1744 setmodel(e, "null");
1748 e.SendEntity = sendfunc;
1749 e.SendFlags = 0xFFFFFF;
1752 e.effects |= EF_NODEPTHTEST;
1756 e.nextthink = time + dt;
1757 e.think = SUB_Remove;
1761 void adaptor_think2touch()
1770 void adaptor_think2use()
1782 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1784 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
1785 self.projectiledeathtype |= HITTYPE_SPLASH;
1786 adaptor_think2use();
1789 // deferred dropping
1790 void DropToFloor_Handler()
1792 builtin_droptofloor();
1793 self.dropped_origin = self.origin;
1798 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1803 float trace_hits_box_a0, trace_hits_box_a1;
1805 float trace_hits_box_1d(float end, float thmi, float thma)
1809 // just check if x is in range
1817 // do the trace with respect to x
1818 // 0 -> end has to stay in thmi -> thma
1819 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1820 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1821 if (trace_hits_box_a0 > trace_hits_box_a1)
1827 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1832 // now it is a trace from 0 to end
1834 trace_hits_box_a0 = 0;
1835 trace_hits_box_a1 = 1;
1837 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1839 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1841 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1847 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1849 return trace_hits_box(start, end, thmi - ma, thma - mi);
1852 float SUB_NoImpactCheck()
1854 // zero hitcontents = this is not the real impact, but either the
1855 // mirror-impact of something hitting the projectile instead of the
1856 // projectile hitting the something, or a touchareagrid one. Neither of
1857 // these stop the projectile from moving, so...
1858 if(trace_dphitcontents == 0)
1860 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1861 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)));
1864 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1866 if (other == world && self.size != '0 0 0')
1869 tic = self.velocity * sys_frametime;
1870 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1871 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1872 if (trace_fraction >= 1)
1874 dprint("Odd... did not hit...?\n");
1876 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1878 dprint("Detected and prevented the sky-grapple bug.\n");
1886 #define SUB_OwnerCheck() (other && (other == self.owner))
1888 void RemoveGrapplingHook(entity pl);
1889 void W_Crylink_Dequeue(entity e);
1890 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1892 if(SUB_OwnerCheck())
1894 if(SUB_NoImpactCheck())
1896 if(self.classname == "grapplinghook")
1897 RemoveGrapplingHook(self.realowner);
1898 else if(self.classname == "spike")
1900 W_Crylink_Dequeue(self);
1907 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1908 UpdateCSQCProjectile(self);
1911 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1913 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1914 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1916 void URI_Get_Callback(float id, float status, string data)
1918 if(url_URI_Get_Callback(id, status, data))
1922 else if (id == URI_GET_DISCARD)
1926 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1929 Curl_URI_Get_Callback(id, status, data);
1931 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1934 OnlineBanList_URI_Get_Callback(id, status, data);
1938 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1942 string uid2name(string myuid) {
1944 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1946 // FIXME remove this later after 0.6 release
1947 // convert old style broken records to correct style
1950 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1953 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1954 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1959 s = "^1Unregistered Player";
1963 float race_readTime(string map, float pos)
1971 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1974 string race_readUID(string map, float pos)
1982 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
1985 float race_readPos(string map, float t) {
1987 for (i = 1; i <= RANKINGS_CNT; ++i)
1988 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
1991 return 0; // pos is zero if unranked
1994 void race_writeTime(string map, float t, string myuid)
2003 newpos = race_readPos(map, t);
2005 float i, prevpos = 0;
2006 for(i = 1; i <= RANKINGS_CNT; ++i)
2008 if(race_readUID(map, i) == myuid)
2011 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2012 for (i = prevpos; i > newpos; --i) {
2013 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2014 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2016 } else { // player has no ranked record yet
2017 for (i = RANKINGS_CNT; i > newpos; --i) {
2018 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2019 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2023 // store new time itself
2024 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2025 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2028 string race_readName(string map, float pos)
2036 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2039 string race_placeName(float pos) {
2040 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2042 if(mod(pos, 10) == 1)
2043 return strcat(ftos(pos), "st");
2044 else if(mod(pos, 10) == 2)
2045 return strcat(ftos(pos), "nd");
2046 else if(mod(pos, 10) == 3)
2047 return strcat(ftos(pos), "rd");
2049 return strcat(ftos(pos), "th");
2052 return strcat(ftos(pos), "th");
2055 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2058 vector start, org, delta, end, enddown, mstart;
2061 m = e.dphitcontentsmask;
2062 e.dphitcontentsmask = goodcontents | badcontents;
2065 delta = world.maxs - world.mins;
2069 for (i = 0; i < attempts; ++i)
2071 start_x = org_x + random() * delta_x;
2072 start_y = org_y + random() * delta_y;
2073 start_z = org_z + random() * delta_z;
2075 // rule 1: start inside world bounds, and outside
2076 // solid, and don't start from somewhere where you can
2077 // fall down to evil
2078 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2079 if (trace_fraction >= 1)
2081 if (trace_startsolid)
2083 if (trace_dphitcontents & badcontents)
2085 if (trace_dphitq3surfaceflags & badsurfaceflags)
2088 // rule 2: if we are too high, lower the point
2089 if (trace_fraction * delta_z > maxaboveground)
2090 start = trace_endpos + '0 0 1' * maxaboveground;
2091 enddown = trace_endpos;
2093 // rule 3: make sure we aren't outside the map. This only works
2094 // for somewhat well formed maps. A good rule of thumb is that
2095 // the map should have a convex outside hull.
2096 // these can be traceLINES as we already verified the starting box
2097 mstart = start + 0.5 * (e.mins + e.maxs);
2098 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2099 if (trace_fraction >= 1)
2101 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2102 if (trace_fraction >= 1)
2104 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2105 if (trace_fraction >= 1)
2107 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2108 if (trace_fraction >= 1)
2110 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2111 if (trace_fraction >= 1)
2114 // rule 4: we must "see" some spawnpoint
2115 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2116 if(checkpvs(mstart, sp))
2120 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2121 if(checkpvs(mstart, sp))
2127 // find a random vector to "look at"
2128 end_x = org_x + random() * delta_x;
2129 end_y = org_y + random() * delta_y;
2130 end_z = org_z + random() * delta_z;
2131 end = start + normalize(end - start) * vlen(delta);
2133 // rule 4: start TO end must not be too short
2134 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2135 if (trace_startsolid)
2137 if (trace_fraction < minviewdistance / vlen(delta))
2140 // rule 5: don't want to look at sky
2141 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2144 // rule 6: we must not end up in trigger_hurt
2145 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2151 e.dphitcontentsmask = m;
2155 setorigin(e, start);
2156 e.angles = vectoangles(end - start);
2157 dprint("Needed ", ftos(i + 1), " attempts\n");
2164 float zcurveparticles_effectno;
2165 vector zcurveparticles_start;
2166 float zcurveparticles_spd;
2168 void endzcurveparticles()
2170 if(zcurveparticles_effectno)
2173 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2175 zcurveparticles_effectno = 0;
2178 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2180 spd = bound(0, floor(spd / 16), 32767);
2181 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2183 endzcurveparticles();
2184 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2185 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2186 WriteShort(MSG_BROADCAST, effectno);
2187 WriteCoord(MSG_BROADCAST, start_x);
2188 WriteCoord(MSG_BROADCAST, start_y);
2189 WriteCoord(MSG_BROADCAST, start_z);
2190 zcurveparticles_effectno = effectno;
2191 zcurveparticles_start = start;
2194 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2195 WriteCoord(MSG_BROADCAST, end_x);
2196 WriteCoord(MSG_BROADCAST, end_y);
2197 WriteCoord(MSG_BROADCAST, end_z);
2198 WriteCoord(MSG_BROADCAST, end_dz);
2199 zcurveparticles_spd = spd;
2202 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2205 vector vecxy, velxy;
2207 vecxy = end - start;
2212 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2214 endzcurveparticles();
2215 trailparticles(world, effectno, start, end);
2219 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2220 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2223 void write_recordmarker(entity pl, float tstart, float dt)
2225 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2227 // also write a marker into demo files for demotc-race-record-extractor to find
2230 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2231 " ", ftos(tstart), " ", ftos(dt), "\n"));
2234 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2247 if(allowcenter) // 2: allow center handedness
2260 if(allowcenter) // 2: allow center handedness
2276 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2281 if (autocvar_g_shootfromeye)
2285 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2286 else { vecs_y = 0; vecs_z -= 2; }
2294 else if (autocvar_g_shootfromcenter)
2299 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2309 else if (autocvar_g_shootfromclient)
2311 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2316 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2318 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2322 void attach_sameorigin(entity e, entity to, string tag)
2324 vector org, t_forward, t_left, t_up, e_forward, e_up;
2327 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2328 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2329 t_forward = v_forward * tagscale;
2330 t_left = v_right * -tagscale;
2331 t_up = v_up * tagscale;
2333 e.origin_x = org * t_forward;
2334 e.origin_y = org * t_left;
2335 e.origin_z = org * t_up;
2337 // current forward and up directions
2338 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2339 e.angles = AnglesTransform_FromVAngles(e.angles);
2341 e.angles = AnglesTransform_FromAngles(e.angles);
2342 fixedmakevectors(e.angles);
2344 // untransform forward, up!
2345 e_forward_x = v_forward * t_forward;
2346 e_forward_y = v_forward * t_left;
2347 e_forward_z = v_forward * t_up;
2348 e_up_x = v_up * t_forward;
2349 e_up_y = v_up * t_left;
2350 e_up_z = v_up * t_up;
2352 e.angles = fixedvectoangles2(e_forward, e_up);
2353 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2354 e.angles = AnglesTransform_ToVAngles(e.angles);
2356 e.angles = AnglesTransform_ToAngles(e.angles);
2358 setattachment(e, to, tag);
2359 setorigin(e, e.origin);
2362 void detach_sameorigin(entity e)
2365 org = gettaginfo(e, 0);
2366 e.angles = fixedvectoangles2(v_forward, v_up);
2367 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2368 e.angles = AnglesTransform_ToVAngles(e.angles);
2370 e.angles = AnglesTransform_ToAngles(e.angles);
2372 setattachment(e, world, "");
2373 setorigin(e, e.origin);
2376 void follow_sameorigin(entity e, entity to)
2378 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2379 e.aiment = to; // make the hole follow bmodel
2380 e.punchangle = to.angles; // the original angles of bmodel
2381 e.view_ofs = e.origin - to.origin; // relative origin
2382 e.v_angle = e.angles - to.angles; // relative angles
2385 void unfollow_sameorigin(entity e)
2387 e.movetype = MOVETYPE_NONE;
2390 entity gettaginfo_relative_ent;
2391 vector gettaginfo_relative(entity e, float tag)
2393 if (!gettaginfo_relative_ent)
2395 gettaginfo_relative_ent = spawn();
2396 gettaginfo_relative_ent.effects = EF_NODRAW;
2398 gettaginfo_relative_ent.model = e.model;
2399 gettaginfo_relative_ent.modelindex = e.modelindex;
2400 gettaginfo_relative_ent.frame = e.frame;
2401 return gettaginfo(gettaginfo_relative_ent, tag);
2406 float modeleffect_SendEntity(entity to, float sf)
2409 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2412 if(self.velocity != '0 0 0')
2414 if(self.angles != '0 0 0')
2416 if(self.avelocity != '0 0 0')
2419 WriteByte(MSG_ENTITY, f);
2420 WriteShort(MSG_ENTITY, self.modelindex);
2421 WriteByte(MSG_ENTITY, self.skin);
2422 WriteByte(MSG_ENTITY, self.frame);
2423 WriteCoord(MSG_ENTITY, self.origin_x);
2424 WriteCoord(MSG_ENTITY, self.origin_y);
2425 WriteCoord(MSG_ENTITY, self.origin_z);
2428 WriteCoord(MSG_ENTITY, self.velocity_x);
2429 WriteCoord(MSG_ENTITY, self.velocity_y);
2430 WriteCoord(MSG_ENTITY, self.velocity_z);
2434 WriteCoord(MSG_ENTITY, self.angles_x);
2435 WriteCoord(MSG_ENTITY, self.angles_y);
2436 WriteCoord(MSG_ENTITY, self.angles_z);
2440 WriteCoord(MSG_ENTITY, self.avelocity_x);
2441 WriteCoord(MSG_ENTITY, self.avelocity_y);
2442 WriteCoord(MSG_ENTITY, self.avelocity_z);
2444 WriteShort(MSG_ENTITY, self.scale * 256.0);
2445 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2446 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2447 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2448 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2453 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)
2458 e.classname = "modeleffect";
2466 e.teleport_time = t1;
2470 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2474 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2477 sz = max(e.scale, e.scale2);
2478 setsize(e, e.mins * sz, e.maxs * sz);
2479 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2482 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2484 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2487 float randombit(float bits)
2489 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2498 for(f = 1; f <= bits; f *= 2)
2507 r = (r - 1) / (n - 1);
2514 float randombits(float bits, float k, float error_return)
2518 while(k > 0 && bits != r)
2520 r += randombit(bits - r);
2529 void randombit_test(float bits, float iter)
2533 print(ftos(randombit(bits)), "\n");
2538 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2540 if(halflifedist > 0)
2541 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2542 else if(halflifedist < 0)
2543 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2552 #define cvar_string_normal builtin_cvar_string
2553 #define cvar_normal builtin_cvar
2555 string cvar_string_normal(string n)
2557 if not(cvar_type(n) & 1)
2558 backtrace(strcat("Attempt to access undefined cvar: ", n));
2559 return builtin_cvar_string(n);
2562 float cvar_normal(string n)
2564 return stof(cvar_string_normal(n));
2567 #define cvar_set_normal builtin_cvar_set
2575 oself.think = SUB_Remove;
2576 oself.nextthink = time;
2582 Execute func() after time + fdelay.
2583 self when func is executed = self when defer is called
2585 void defer(float fdelay, void() func)
2592 e.think = defer_think;
2593 e.nextthink = time + fdelay;
2596 .string aiment_classname;
2597 .float aiment_deadflag;
2598 void SetMovetypeFollow(entity ent, entity e)
2600 // FIXME this may not be warpzone aware
2601 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2602 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.
2603 ent.aiment = e; // make the hole follow bmodel
2604 ent.punchangle = e.angles; // the original angles of bmodel
2605 ent.view_ofs = ent.origin - e.origin; // relative origin
2606 ent.v_angle = ent.angles - e.angles; // relative angles
2607 ent.aiment_classname = strzone(e.classname);
2608 ent.aiment_deadflag = e.deadflag;
2610 void UnsetMovetypeFollow(entity ent)
2612 ent.movetype = MOVETYPE_FLY;
2613 PROJECTILE_MAKETRIGGER(ent);
2616 float LostMovetypeFollow(entity ent)
2619 if(ent.movetype != MOVETYPE_FOLLOW)
2625 if(ent.aiment.classname != ent.aiment_classname)
2627 if(ent.aiment.deadflag != ent.aiment_deadflag)
2633 float isPushable(entity e)
2642 case "droppedweapon":
2643 case "keepawayball":
2644 case "nexball_basketball":
2645 case "nexball_football":
2647 case "bullet": // antilagged bullets can't hit this either
2650 if (e.projectiledeathtype)
2655 void dedicated_print(string input) // print(), but only print if the server is not local
2657 if not(server_is_local) { print(input); }