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 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
577 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
578 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
579 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
580 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
582 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
585 if (s == "cl_weaponpriority")
586 self.switchweapon = w_getbestweapon(self);
587 if (s == "cl_allow_uidtracking")
588 PlayerStats_AddPlayer(self);
592 // decolorizes and team colors the player name when needed
593 string playername(entity p)
596 if (teamplay && !intermission_running && p.classname == "player")
598 t = Team_ColorCode(p.team);
599 return strcat(t, strdecolorize(p.netname));
605 vector randompos(vector m1, vector m2)
609 v_x = m2_x * random() + m1_x;
610 v_y = m2_y * random() + m1_y;
611 v_z = m2_z * random() + m1_z;
615 //#NO AUTOCVARS START
617 float g_pickup_shells;
618 float g_pickup_shells_max;
619 float g_pickup_nails;
620 float g_pickup_nails_max;
621 float g_pickup_rockets;
622 float g_pickup_rockets_max;
623 float g_pickup_cells;
624 float g_pickup_cells_max;
626 float g_pickup_fuel_jetpack;
627 float g_pickup_fuel_max;
628 float g_pickup_armorsmall;
629 float g_pickup_armorsmall_max;
630 float g_pickup_armorsmall_anyway;
631 float g_pickup_armormedium;
632 float g_pickup_armormedium_max;
633 float g_pickup_armormedium_anyway;
634 float g_pickup_armorbig;
635 float g_pickup_armorbig_max;
636 float g_pickup_armorbig_anyway;
637 float g_pickup_armorlarge;
638 float g_pickup_armorlarge_max;
639 float g_pickup_armorlarge_anyway;
640 float g_pickup_healthsmall;
641 float g_pickup_healthsmall_max;
642 float g_pickup_healthsmall_anyway;
643 float g_pickup_healthmedium;
644 float g_pickup_healthmedium_max;
645 float g_pickup_healthmedium_anyway;
646 float g_pickup_healthlarge;
647 float g_pickup_healthlarge_max;
648 float g_pickup_healthlarge_anyway;
649 float g_pickup_healthmega;
650 float g_pickup_healthmega_max;
651 float g_pickup_healthmega_anyway;
652 float g_pickup_ammo_anyway;
653 float g_pickup_weapons_anyway;
655 WEPSET_DECLARE_A(g_weaponarena_weapons);
656 float g_weaponarena_random;
657 float g_weaponarena_random_with_laser;
658 string g_weaponarena_list;
659 float g_weaponspeedfactor;
660 float g_weaponratefactor;
661 float g_weapondamagefactor;
662 float g_weaponforcefactor;
663 float g_weaponspreadfactor;
665 WEPSET_DECLARE_A(start_weapons);
666 WEPSET_DECLARE_A(start_weapons_default);
667 WEPSET_DECLARE_A(start_weapons_defaultmask);
669 float start_ammo_shells;
670 float start_ammo_nails;
671 float start_ammo_rockets;
672 float start_ammo_cells;
673 float start_ammo_fuel;
675 float start_armorvalue;
676 WEPSET_DECLARE_A(warmup_start_weapons);
677 WEPSET_DECLARE_A(warmup_start_weapons_default);
678 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
679 float warmup_start_ammo_shells;
680 float warmup_start_ammo_nails;
681 float warmup_start_ammo_rockets;
682 float warmup_start_ammo_cells;
683 float warmup_start_ammo_fuel;
684 float warmup_start_health;
685 float warmup_start_armorvalue;
688 entity get_weaponinfo(float w);
690 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
692 var float i = weaponinfo.weapon;
698 if (g_lms || g_ca || allguns)
700 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
706 d = (i == WEP_SHOTGUN);
708 d = 0; // weapon is set a few lines later
710 d = (i == WEP_LASER || i == WEP_SHOTGUN);
712 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
713 d |= (i == WEP_HOOK);
714 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
717 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
719 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
724 // 4: is set by default?
733 void readplayerstartcvars()
739 // initialize starting values for players
740 WEPSET_CLEAR_A(start_weapons);
741 WEPSET_CLEAR_A(start_weapons_default);
742 WEPSET_CLEAR_A(start_weapons_defaultmask);
744 start_ammo_shells = 0;
745 start_ammo_nails = 0;
746 start_ammo_rockets = 0;
747 start_ammo_cells = 0;
748 start_health = cvar("g_balance_health_start");
749 start_armorvalue = cvar("g_balance_armor_start");
752 WEPSET_CLEAR_A(g_weaponarena_weapons);
754 s = cvar_string("g_weaponarena");
755 if (s == "0" || s == "")
761 if (s == "0" || s == "")
767 // forcibly turn off weaponarena
772 g_weaponarena_list = "All Weapons";
773 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
775 e = get_weaponinfo(j);
776 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
777 WEPSET_OR_AW(g_weaponarena_weapons, j);
780 else if (s == "most")
783 g_weaponarena_list = "Most Weapons";
784 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
786 e = get_weaponinfo(j);
787 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
788 if (e.spawnflags & WEP_FLAG_NORMAL)
789 WEPSET_OR_AW(g_weaponarena_weapons, j);
792 else if (s == "none")
795 g_weaponarena_list = "No Weapons";
800 t = tokenize_console(s);
801 g_weaponarena_list = "";
802 for (i = 0; i < t; ++i)
805 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
807 e = get_weaponinfo(j);
810 WEPSET_OR_AW(g_weaponarena_weapons, j);
811 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
817 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
820 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
824 g_weaponarena_random = cvar("g_weaponarena_random");
826 g_weaponarena_random = 0;
827 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
831 g_minstagib = 0; // incompatible
832 g_pinata = 0; // incompatible
833 g_weapon_stay = 0; // incompatible
834 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
836 start_items |= IT_UNLIMITED_AMMO;
838 else if (g_minstagib)
840 g_pinata = 0; // incompatible
841 g_weapon_stay = 0; // incompatible
842 g_bloodloss = 0; // incompatible
844 start_armorvalue = 0;
845 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
846 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
847 start_items |= IT_UNLIMITED_SUPERWEAPONS;
849 if (g_minstagib_invis_alpha <= 0)
850 g_minstagib_invis_alpha = -1;
854 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
856 e = get_weaponinfo(i);
857 float w = want_weapon("g_start_weapon_", e, FALSE);
859 WEPSET_OR_AW(start_weapons, i);
861 WEPSET_OR_AW(start_weapons_default, i);
863 WEPSET_OR_AW(start_weapons_defaultmask, i);
867 if(!cvar("g_use_ammunition"))
868 start_items |= IT_UNLIMITED_AMMO;
870 if(cvar("g_nexball"))
871 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
875 start_ammo_cells = cvar("g_minstagib_ammo_start");
876 start_ammo_fuel = cvar("g_start_ammo_fuel");
878 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
880 start_ammo_rockets = 999;
881 start_ammo_shells = 999;
882 start_ammo_cells = 999;
883 start_ammo_nails = 999;
884 start_ammo_fuel = 999;
890 start_ammo_shells = cvar("g_lms_start_ammo_shells");
891 start_ammo_nails = cvar("g_lms_start_ammo_nails");
892 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
893 start_ammo_cells = cvar("g_lms_start_ammo_cells");
894 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
898 start_ammo_shells = cvar("g_start_ammo_shells");
899 start_ammo_nails = cvar("g_start_ammo_nails");
900 start_ammo_rockets = cvar("g_start_ammo_rockets");
901 start_ammo_cells = cvar("g_start_ammo_cells");
902 start_ammo_fuel = cvar("g_start_ammo_fuel");
908 start_health = cvar("g_lms_start_health");
909 start_armorvalue = cvar("g_lms_start_armor");
914 warmup_start_ammo_shells = start_ammo_shells;
915 warmup_start_ammo_nails = start_ammo_nails;
916 warmup_start_ammo_rockets = start_ammo_rockets;
917 warmup_start_ammo_cells = start_ammo_cells;
918 warmup_start_ammo_fuel = start_ammo_fuel;
919 warmup_start_health = start_health;
920 warmup_start_armorvalue = start_armorvalue;
921 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
922 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
923 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
925 if (!g_weaponarena && !g_minstagib && !g_ca)
927 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
928 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
929 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
930 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
931 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
932 warmup_start_health = cvar("g_warmup_start_health");
933 warmup_start_armorvalue = cvar("g_warmup_start_armor");
934 WEPSET_CLEAR_A(warmup_start_weapons);
935 WEPSET_CLEAR_A(warmup_start_weapons_default);
936 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
937 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
939 e = get_weaponinfo(i);
940 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
942 WEPSET_OR_AW(warmup_start_weapons, i);
944 WEPSET_OR_AW(warmup_start_weapons_default, i);
946 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
952 start_items |= IT_JETPACK;
954 MUTATOR_CALLHOOK(SetStartItems);
956 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
958 g_grappling_hook = 0; // these two can't coexist, as they use the same button
959 start_items |= IT_FUEL_REGEN;
960 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
961 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
964 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
966 e = get_weaponinfo(i);
967 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
968 weapon_action(i, WR_PRECACHE);
971 start_ammo_shells = max(0, start_ammo_shells);
972 start_ammo_nails = max(0, start_ammo_nails);
973 start_ammo_cells = max(0, start_ammo_cells);
974 start_ammo_rockets = max(0, start_ammo_rockets);
975 start_ammo_fuel = max(0, start_ammo_fuel);
977 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
978 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
979 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
980 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
981 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
985 float g_bugrigs_planar_movement;
986 float g_bugrigs_planar_movement_car_jumping;
987 float g_bugrigs_reverse_spinning;
988 float g_bugrigs_reverse_speeding;
989 float g_bugrigs_reverse_stopping;
990 float g_bugrigs_air_steering;
991 float g_bugrigs_angle_smoothing;
992 float g_bugrigs_friction_floor;
993 float g_bugrigs_friction_brake;
994 float g_bugrigs_friction_air;
995 float g_bugrigs_accel;
996 float g_bugrigs_speed_ref;
997 float g_bugrigs_speed_pow;
998 float g_bugrigs_steer;
1000 float g_touchexplode;
1001 float g_touchexplode_radius;
1002 float g_touchexplode_damage;
1003 float g_touchexplode_edgedamage;
1004 float g_touchexplode_force;
1009 string GetGametype(); // g_world.qc
1010 void readlevelcvars(void)
1012 g_minstagib = cvar("g_minstagib");
1014 // load ALL the mutators
1015 if(cvar("g_dodging"))
1016 MUTATOR_ADD(mutator_dodging);
1017 if(cvar("g_spawn_near_teammate"))
1018 MUTATOR_ADD(mutator_spawn_near_teammate);
1019 if(cvar("g_physical_items"))
1020 MUTATOR_ADD(mutator_physical_items);
1023 if(cvar("g_invincible_projectiles"))
1024 MUTATOR_ADD(mutator_invincibleprojectiles);
1025 if(cvar("g_new_toys"))
1026 MUTATOR_ADD(mutator_new_toys);
1028 MUTATOR_ADD(mutator_nix);
1029 if(cvar("g_rocket_flying"))
1030 MUTATOR_ADD(mutator_rocketflying);
1031 if(cvar("g_vampire"))
1032 MUTATOR_ADD(mutator_vampire);
1033 if(cvar("g_superspectate"))
1034 MUTATOR_ADD(mutator_superspec);
1037 // is this a mutator? is this a mode?
1038 if(cvar("g_sandbox"))
1039 MUTATOR_ADD(sandbox);
1041 if(cvar("sv_allow_fullbright"))
1042 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1044 g_bugrigs = cvar("g_bugrigs");
1045 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1046 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1047 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1048 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1049 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1050 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1051 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1052 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1053 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1054 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1055 g_bugrigs_accel = cvar("g_bugrigs_accel");
1056 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1057 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1058 g_bugrigs_steer = cvar("g_bugrigs_steer");
1060 g_touchexplode = cvar("g_touchexplode");
1061 g_touchexplode_radius = cvar("g_touchexplode_radius");
1062 g_touchexplode_damage = cvar("g_touchexplode_damage");
1063 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1064 g_touchexplode_force = cvar("g_touchexplode_force");
1066 sv_clones = cvar("sv_clones");
1067 sv_foginterval = cvar("sv_foginterval");
1068 g_cloaked = cvar("g_cloaked");
1070 g_cloaked = 1; // always enable cloak in CTS
1071 g_jump_grunt = cvar("g_jump_grunt");
1072 g_footsteps = cvar("g_footsteps");
1073 g_grappling_hook = cvar("g_grappling_hook");
1074 g_jetpack = cvar("g_jetpack");
1075 g_midair = cvar("g_midair");
1076 g_norecoil = cvar("g_norecoil");
1077 g_bloodloss = cvar("g_bloodloss");
1078 sv_maxidle = cvar("sv_maxidle");
1079 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1080 sv_autotaunt = cvar("sv_autotaunt");
1081 sv_taunt = cvar("sv_taunt");
1083 inWarmupStage = cvar("g_warmup");
1084 g_warmup_limit = cvar("g_warmup_limit");
1085 g_warmup_allguns = cvar("g_warmup_allguns");
1086 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1088 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1089 inWarmupStage = 0; // these modes cannot work together, sorry
1091 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1092 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1093 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1094 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1095 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1096 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1097 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1098 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1099 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1100 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1101 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1102 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1103 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1104 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1106 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1107 g_weaponratefactor = cvar("g_weaponratefactor");
1108 g_weapondamagefactor = cvar("g_weapondamagefactor");
1109 g_weaponforcefactor = cvar("g_weaponforcefactor");
1110 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1112 g_pickup_shells = cvar("g_pickup_shells");
1113 g_pickup_shells_max = cvar("g_pickup_shells_max");
1114 g_pickup_nails = cvar("g_pickup_nails");
1115 g_pickup_nails_max = cvar("g_pickup_nails_max");
1116 g_pickup_rockets = cvar("g_pickup_rockets");
1117 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1118 g_pickup_cells = cvar("g_pickup_cells");
1119 g_pickup_cells_max = cvar("g_pickup_cells_max");
1120 g_pickup_fuel = cvar("g_pickup_fuel");
1121 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1122 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1123 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1124 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1125 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1126 g_pickup_armormedium = cvar("g_pickup_armormedium");
1127 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1128 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1129 g_pickup_armorbig = cvar("g_pickup_armorbig");
1130 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1131 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1132 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1133 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1134 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1135 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1136 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1137 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1138 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1139 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1140 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1141 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1142 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1143 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1144 g_pickup_healthmega = cvar("g_pickup_healthmega");
1145 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1146 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1148 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1149 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1151 g_pinata = cvar("g_pinata");
1153 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1155 g_weapon_stay = cvar("g_weapon_stay");
1157 if not(inWarmupStage && !g_ca)
1158 game_starttime = cvar("g_start_delay");
1160 readplayerstartcvars();
1166 string precache_sound (string s) = #19;
1167 float precache_sound_index (string s) = #19;
1169 #define SND_VOLUME 1
1170 #define SND_ATTENUATION 2
1171 #define SND_LARGEENTITY 8
1172 #define SND_LARGESOUND 16
1174 float sound_allowed(float dest, entity e)
1176 // sounds from world may always pass
1179 if (e.classname == "body")
1181 else if (e.realowner && e.realowner != e)
1183 else if (e.owner && e.owner != e)
1188 // sounds to self may always pass
1189 if (dest == MSG_ONE)
1190 if (e == msg_entity)
1192 // sounds by players can be removed
1193 if (autocvar_bot_sound_monopoly)
1194 if (clienttype(e) == CLIENTTYPE_REAL)
1196 // anything else may pass
1200 #ifdef COMPAT_XON010_CHANNELS
1201 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1202 void sound(entity e, float chan, string samp, float vol, float atten)
1204 if (!sound_allowed(MSG_BROADCAST, e))
1206 builtin_sound(e, chan, samp, vol, atten);
1210 void sound(entity e, float chan, string samp, float vol, float atten)
1212 if (!sound_allowed(MSG_BROADCAST, e))
1214 sound7(e, chan, samp, vol, atten, 0, 0);
1218 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1222 if (!sound_allowed(dest, e))
1225 entno = num_for_edict(e);
1226 idx = precache_sound_index(samp);
1231 atten = floor(atten * 64);
1232 vol = floor(vol * 255);
1235 sflags |= SND_VOLUME;
1237 sflags |= SND_ATTENUATION;
1238 if (entno >= 8192 || chan < 0 || chan > 7)
1239 sflags |= SND_LARGEENTITY;
1241 sflags |= SND_LARGESOUND;
1243 WriteByte(dest, SVC_SOUND);
1244 WriteByte(dest, sflags);
1245 if (sflags & SND_VOLUME)
1246 WriteByte(dest, vol);
1247 if (sflags & SND_ATTENUATION)
1248 WriteByte(dest, atten);
1249 if (sflags & SND_LARGEENTITY)
1251 WriteShort(dest, entno);
1252 WriteByte(dest, chan);
1256 WriteShort(dest, entno * 8 + chan);
1258 if (sflags & SND_LARGESOUND)
1259 WriteShort(dest, idx);
1261 WriteByte(dest, idx);
1263 WriteCoord(dest, o_x);
1264 WriteCoord(dest, o_y);
1265 WriteCoord(dest, o_z);
1267 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1271 if (!sound_allowed(dest, e))
1274 o = e.origin + 0.5 * (e.mins + e.maxs);
1275 soundtoat(dest, e, o, chan, samp, vol, atten);
1277 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1279 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1281 void stopsoundto(float dest, entity e, float chan)
1285 if (!sound_allowed(dest, e))
1288 entno = num_for_edict(e);
1290 if (entno >= 8192 || chan < 0 || chan > 7)
1293 idx = precache_sound_index("misc/null.wav");
1294 sflags = SND_LARGEENTITY;
1296 sflags |= SND_LARGESOUND;
1297 WriteByte(dest, SVC_SOUND);
1298 WriteByte(dest, sflags);
1299 WriteShort(dest, entno);
1300 WriteByte(dest, chan);
1301 if (sflags & SND_LARGESOUND)
1302 WriteShort(dest, idx);
1304 WriteByte(dest, idx);
1305 WriteCoord(dest, e.origin_x);
1306 WriteCoord(dest, e.origin_y);
1307 WriteCoord(dest, e.origin_z);
1311 WriteByte(dest, SVC_STOPSOUND);
1312 WriteShort(dest, entno * 8 + chan);
1315 void stopsound(entity e, float chan)
1317 if (!sound_allowed(MSG_BROADCAST, e))
1320 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1321 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1324 void play2(entity e, string filename)
1326 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1328 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1331 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1333 float spamsound(entity e, float chan, string samp, float vol, float atten)
1335 if (!sound_allowed(MSG_BROADCAST, e))
1338 if (time > e.spamtime)
1341 sound(e, chan, samp, vol, atten);
1347 void play2team(float t, string filename)
1351 if (autocvar_bot_sound_monopoly)
1354 FOR_EACH_REALPLAYER(head)
1357 play2(head, filename);
1361 void play2all(string samp)
1363 if (autocvar_bot_sound_monopoly)
1366 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1369 void PrecachePlayerSounds(string f);
1370 void precache_playermodel(string m)
1372 float globhandle, i, n;
1375 if(substring(m, -9,5) == "_lod1")
1377 if(substring(m, -9,5) == "_lod2")
1380 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1383 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1387 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1390 n = search_getsize(globhandle);
1391 for (i = 0; i < n; ++i)
1393 //print(search_getfilename(globhandle, i), "\n");
1394 f = search_getfilename(globhandle, i);
1395 PrecachePlayerSounds(f);
1397 search_end(globhandle);
1399 void precache_all_playermodels(string pattern)
1401 float globhandle, i, n;
1404 globhandle = search_begin(pattern, TRUE, FALSE);
1407 n = search_getsize(globhandle);
1408 for (i = 0; i < n; ++i)
1410 //print(search_getfilename(globhandle, i), "\n");
1411 f = search_getfilename(globhandle, i);
1412 precache_playermodel(f);
1414 search_end(globhandle);
1419 // gamemode related things
1420 precache_model ("models/misc/chatbubble.spr");
1423 precache_model ("models/runematch/curse.mdl");
1424 precache_model ("models/runematch/rune.mdl");
1427 #ifdef TTURRETS_ENABLED
1428 if (autocvar_g_turrets)
1432 // Precache all player models if desired
1433 if (autocvar_sv_precacheplayermodels)
1435 PrecachePlayerSounds("sound/player/default.sounds");
1436 precache_all_playermodels("models/player/*.zym");
1437 precache_all_playermodels("models/player/*.dpm");
1438 precache_all_playermodels("models/player/*.md3");
1439 precache_all_playermodels("models/player/*.psk");
1440 precache_all_playermodels("models/player/*.iqm");
1443 if (autocvar_sv_defaultcharacter)
1446 s = autocvar_sv_defaultplayermodel_red;
1448 precache_playermodel(s);
1449 s = autocvar_sv_defaultplayermodel_blue;
1451 precache_playermodel(s);
1452 s = autocvar_sv_defaultplayermodel_yellow;
1454 precache_playermodel(s);
1455 s = autocvar_sv_defaultplayermodel_pink;
1457 precache_playermodel(s);
1458 s = autocvar_sv_defaultplayermodel;
1460 precache_playermodel(s);
1465 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1466 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1469 // gore and miscellaneous sounds
1470 //precache_sound ("misc/h2ohit.wav");
1471 precache_model ("models/hook.md3");
1472 precache_sound ("misc/armorimpact.wav");
1473 precache_sound ("misc/bodyimpact1.wav");
1474 precache_sound ("misc/bodyimpact2.wav");
1475 precache_sound ("misc/gib.wav");
1476 precache_sound ("misc/gib_splat01.wav");
1477 precache_sound ("misc/gib_splat02.wav");
1478 precache_sound ("misc/gib_splat03.wav");
1479 precache_sound ("misc/gib_splat04.wav");
1480 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1481 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1482 precache_sound ("misc/null.wav");
1483 precache_sound ("misc/spawn.wav");
1484 precache_sound ("misc/talk.wav");
1485 precache_sound ("misc/teleport.wav");
1486 precache_sound ("misc/poweroff.wav");
1487 precache_sound ("player/lava.wav");
1488 precache_sound ("player/slime.wav");
1490 precache_model ("models/sprites/0.spr32");
1491 precache_model ("models/sprites/1.spr32");
1492 precache_model ("models/sprites/2.spr32");
1493 precache_model ("models/sprites/3.spr32");
1494 precache_model ("models/sprites/4.spr32");
1495 precache_model ("models/sprites/5.spr32");
1496 precache_model ("models/sprites/6.spr32");
1497 precache_model ("models/sprites/7.spr32");
1498 precache_model ("models/sprites/8.spr32");
1499 precache_model ("models/sprites/9.spr32");
1500 precache_model ("models/sprites/10.spr32");
1502 // common weapon precaches
1503 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1504 precache_sound ("weapons/weapon_switch.wav");
1505 precache_sound ("weapons/weaponpickup.wav");
1506 precache_sound ("weapons/unavailable.wav");
1507 precache_sound ("weapons/dryfire.wav");
1508 if (g_grappling_hook)
1510 precache_sound ("weapons/hook_fire.wav"); // hook
1511 precache_sound ("weapons/hook_impact.wav"); // hook
1514 if(autocvar_sv_precacheweapons)
1516 //precache weapon models/sounds
1519 while (wep <= WEP_LAST)
1521 weapon_action(wep, WR_PRECACHE);
1526 precache_model("models/elaser.mdl");
1527 precache_model("models/laser.mdl");
1528 precache_model("models/ebomb.mdl");
1531 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1533 if (!self.noise && self.music) // quake 3 uses the music field
1534 self.noise = self.music;
1536 // plays music for the level if there is any
1539 precache_sound (self.noise);
1540 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1545 // WARNING: this kills the trace globals
1546 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1547 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1549 #define INITPRIO_FIRST 0
1550 #define INITPRIO_GAMETYPE 0
1551 #define INITPRIO_GAMETYPE_FALLBACK 1
1552 #define INITPRIO_FINDTARGET 10
1553 #define INITPRIO_DROPTOFLOOR 20
1554 #define INITPRIO_SETLOCATION 90
1555 #define INITPRIO_LINKDOORS 91
1556 #define INITPRIO_LAST 99
1558 .void(void) initialize_entity;
1559 .float initialize_entity_order;
1560 .entity initialize_entity_next;
1561 entity initialize_entity_first;
1563 void make_safe_for_remove(entity e)
1565 if (e.initialize_entity)
1567 entity ent, prev = world;
1568 for (ent = initialize_entity_first; ent; )
1570 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1572 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1573 // skip it in linked list
1576 prev.initialize_entity_next = ent.initialize_entity_next;
1577 ent = prev.initialize_entity_next;
1581 initialize_entity_first = ent.initialize_entity_next;
1582 ent = initialize_entity_first;
1588 ent = ent.initialize_entity_next;
1594 void objerror(string s)
1596 make_safe_for_remove(self);
1597 builtin_objerror(s);
1600 .float remove_except_protected_forbidden;
1601 void remove_except_protected(entity e)
1603 if(e.remove_except_protected_forbidden)
1604 error("not allowed to remove this at this point");
1608 void remove_unsafely(entity e)
1610 if(e.classname == "spike")
1611 error("Removing spikes is forbidden (crylink bug), please report");
1615 void remove_safely(entity e)
1617 make_safe_for_remove(e);
1621 void InitializeEntity(entity e, void(void) func, float order)
1625 if (!e || e.initialize_entity)
1627 // make a proxy initializer entity
1631 e.classname = "initialize_entity";
1635 e.initialize_entity = func;
1636 e.initialize_entity_order = order;
1638 cur = initialize_entity_first;
1642 if (!cur || cur.initialize_entity_order > order)
1644 // insert between prev and cur
1646 prev.initialize_entity_next = e;
1648 initialize_entity_first = e;
1649 e.initialize_entity_next = cur;
1653 cur = cur.initialize_entity_next;
1656 void InitializeEntitiesRun()
1659 startoflist = initialize_entity_first;
1660 initialize_entity_first = world;
1661 remove = remove_except_protected;
1662 for (self = startoflist; self; self = self.initialize_entity_next)
1664 self.remove_except_protected_forbidden = 1;
1666 for (self = startoflist; self; )
1669 var void(void) func;
1670 e = self.initialize_entity_next;
1671 func = self.initialize_entity;
1672 self.initialize_entity_order = 0;
1673 self.initialize_entity = func_null;
1674 self.initialize_entity_next = world;
1675 self.remove_except_protected_forbidden = 0;
1676 if (self.classname == "initialize_entity")
1680 builtin_remove(self);
1683 //dprint("Delayed initialization: ", self.classname, "\n");
1689 backtrace(strcat("Null function in: ", self.classname, "\n"));
1693 remove = remove_unsafely;
1696 .float uncustomizeentityforclient_set;
1697 .void(void) uncustomizeentityforclient;
1698 void UncustomizeEntitiesRun()
1702 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1703 self.uncustomizeentityforclient();
1706 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1708 e.customizeentityforclient = customizer;
1709 e.uncustomizeentityforclient = uncustomizer;
1710 e.uncustomizeentityforclient_set = !!uncustomizer;
1714 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1717 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1721 if (e.classname == "")
1722 e.classname = "net_linked";
1724 if (e.model == "" || self.modelindex == 0)
1728 setmodel(e, "null");
1732 e.SendEntity = sendfunc;
1733 e.SendFlags = 0xFFFFFF;
1736 e.effects |= EF_NODEPTHTEST;
1740 e.nextthink = time + dt;
1741 e.think = SUB_Remove;
1745 void adaptor_think2touch()
1754 void adaptor_think2use()
1766 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1768 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
1769 self.projectiledeathtype |= HITTYPE_SPLASH;
1770 adaptor_think2use();
1773 // deferred dropping
1774 void DropToFloor_Handler()
1776 builtin_droptofloor();
1777 self.dropped_origin = self.origin;
1782 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1787 float trace_hits_box_a0, trace_hits_box_a1;
1789 float trace_hits_box_1d(float end, float thmi, float thma)
1793 // just check if x is in range
1801 // do the trace with respect to x
1802 // 0 -> end has to stay in thmi -> thma
1803 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1804 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1805 if (trace_hits_box_a0 > trace_hits_box_a1)
1811 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1816 // now it is a trace from 0 to end
1818 trace_hits_box_a0 = 0;
1819 trace_hits_box_a1 = 1;
1821 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1823 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1825 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1831 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1833 return trace_hits_box(start, end, thmi - ma, thma - mi);
1836 float SUB_NoImpactCheck()
1838 // zero hitcontents = this is not the real impact, but either the
1839 // mirror-impact of something hitting the projectile instead of the
1840 // projectile hitting the something, or a touchareagrid one. Neither of
1841 // these stop the projectile from moving, so...
1842 if(trace_dphitcontents == 0)
1844 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1845 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)));
1848 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1850 if (other == world && self.size != '0 0 0')
1853 tic = self.velocity * sys_frametime;
1854 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1855 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1856 if (trace_fraction >= 1)
1858 dprint("Odd... did not hit...?\n");
1860 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1862 dprint("Detected and prevented the sky-grapple bug.\n");
1870 #define SUB_OwnerCheck() (other && (other == self.owner))
1872 void RemoveGrapplingHook(entity pl);
1873 void W_Crylink_Dequeue(entity e);
1874 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1876 if(SUB_OwnerCheck())
1878 if(SUB_NoImpactCheck())
1880 if(self.classname == "grapplinghook")
1881 RemoveGrapplingHook(self.realowner);
1882 else if(self.classname == "spike")
1884 W_Crylink_Dequeue(self);
1891 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1892 UpdateCSQCProjectile(self);
1895 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1897 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1898 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1900 void URI_Get_Callback(float id, float status, string data)
1902 if(url_URI_Get_Callback(id, status, data))
1906 else if (id == URI_GET_DISCARD)
1910 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1913 Curl_URI_Get_Callback(id, status, data);
1915 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1918 OnlineBanList_URI_Get_Callback(id, status, data);
1922 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1926 string uid2name(string myuid) {
1928 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1930 // FIXME remove this later after 0.6 release
1931 // convert old style broken records to correct style
1934 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1937 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1938 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1943 s = "^1Unregistered Player";
1947 float race_readTime(string map, float pos)
1955 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1958 string race_readUID(string map, float pos)
1966 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
1969 float race_readPos(string map, float t) {
1971 for (i = 1; i <= RANKINGS_CNT; ++i)
1972 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
1975 return 0; // pos is zero if unranked
1978 void race_writeTime(string map, float t, string myuid)
1987 newpos = race_readPos(map, t);
1989 float i, prevpos = 0;
1990 for(i = 1; i <= RANKINGS_CNT; ++i)
1992 if(race_readUID(map, i) == myuid)
1995 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
1996 for (i = prevpos; i > newpos; --i) {
1997 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
1998 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2000 } else { // player has no ranked record yet
2001 for (i = RANKINGS_CNT; i > newpos; --i) {
2002 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2003 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2007 // store new time itself
2008 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2009 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2012 string race_readName(string map, float pos)
2020 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2023 string race_placeName(float pos) {
2024 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2026 if(mod(pos, 10) == 1)
2027 return strcat(ftos(pos), "st");
2028 else if(mod(pos, 10) == 2)
2029 return strcat(ftos(pos), "nd");
2030 else if(mod(pos, 10) == 3)
2031 return strcat(ftos(pos), "rd");
2033 return strcat(ftos(pos), "th");
2036 return strcat(ftos(pos), "th");
2039 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2042 vector start, org, delta, end, enddown, mstart;
2045 m = e.dphitcontentsmask;
2046 e.dphitcontentsmask = goodcontents | badcontents;
2049 delta = world.maxs - world.mins;
2053 for (i = 0; i < attempts; ++i)
2055 start_x = org_x + random() * delta_x;
2056 start_y = org_y + random() * delta_y;
2057 start_z = org_z + random() * delta_z;
2059 // rule 1: start inside world bounds, and outside
2060 // solid, and don't start from somewhere where you can
2061 // fall down to evil
2062 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2063 if (trace_fraction >= 1)
2065 if (trace_startsolid)
2067 if (trace_dphitcontents & badcontents)
2069 if (trace_dphitq3surfaceflags & badsurfaceflags)
2072 // rule 2: if we are too high, lower the point
2073 if (trace_fraction * delta_z > maxaboveground)
2074 start = trace_endpos + '0 0 1' * maxaboveground;
2075 enddown = trace_endpos;
2077 // rule 3: make sure we aren't outside the map. This only works
2078 // for somewhat well formed maps. A good rule of thumb is that
2079 // the map should have a convex outside hull.
2080 // these can be traceLINES as we already verified the starting box
2081 mstart = start + 0.5 * (e.mins + e.maxs);
2082 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2083 if (trace_fraction >= 1)
2085 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2086 if (trace_fraction >= 1)
2088 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2089 if (trace_fraction >= 1)
2091 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2092 if (trace_fraction >= 1)
2094 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2095 if (trace_fraction >= 1)
2098 // rule 4: we must "see" some spawnpoint
2099 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2100 if(checkpvs(mstart, sp))
2104 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2105 if(checkpvs(mstart, sp))
2111 // find a random vector to "look at"
2112 end_x = org_x + random() * delta_x;
2113 end_y = org_y + random() * delta_y;
2114 end_z = org_z + random() * delta_z;
2115 end = start + normalize(end - start) * vlen(delta);
2117 // rule 4: start TO end must not be too short
2118 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2119 if (trace_startsolid)
2121 if (trace_fraction < minviewdistance / vlen(delta))
2124 // rule 5: don't want to look at sky
2125 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2128 // rule 6: we must not end up in trigger_hurt
2129 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2135 e.dphitcontentsmask = m;
2139 setorigin(e, start);
2140 e.angles = vectoangles(end - start);
2141 dprint("Needed ", ftos(i + 1), " attempts\n");
2148 float zcurveparticles_effectno;
2149 vector zcurveparticles_start;
2150 float zcurveparticles_spd;
2152 void endzcurveparticles()
2154 if(zcurveparticles_effectno)
2157 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2159 zcurveparticles_effectno = 0;
2162 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2164 spd = bound(0, floor(spd / 16), 32767);
2165 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2167 endzcurveparticles();
2168 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2169 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2170 WriteShort(MSG_BROADCAST, effectno);
2171 WriteCoord(MSG_BROADCAST, start_x);
2172 WriteCoord(MSG_BROADCAST, start_y);
2173 WriteCoord(MSG_BROADCAST, start_z);
2174 zcurveparticles_effectno = effectno;
2175 zcurveparticles_start = start;
2178 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2179 WriteCoord(MSG_BROADCAST, end_x);
2180 WriteCoord(MSG_BROADCAST, end_y);
2181 WriteCoord(MSG_BROADCAST, end_z);
2182 WriteCoord(MSG_BROADCAST, end_dz);
2183 zcurveparticles_spd = spd;
2186 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2189 vector vecxy, velxy;
2191 vecxy = end - start;
2196 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2198 endzcurveparticles();
2199 trailparticles(world, effectno, start, end);
2203 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2204 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2207 void write_recordmarker(entity pl, float tstart, float dt)
2209 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2211 // also write a marker into demo files for demotc-race-record-extractor to find
2214 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2215 " ", ftos(tstart), " ", ftos(dt), "\n"));
2218 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2231 if(allowcenter) // 2: allow center handedness
2244 if(allowcenter) // 2: allow center handedness
2260 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2265 if (autocvar_g_shootfromeye)
2269 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2270 else { vecs_y = 0; vecs_z -= 2; }
2278 else if (autocvar_g_shootfromcenter)
2283 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2293 else if (autocvar_g_shootfromclient)
2295 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2300 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2302 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2306 void attach_sameorigin(entity e, entity to, string tag)
2308 vector org, t_forward, t_left, t_up, e_forward, e_up;
2311 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2312 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2313 t_forward = v_forward * tagscale;
2314 t_left = v_right * -tagscale;
2315 t_up = v_up * tagscale;
2317 e.origin_x = org * t_forward;
2318 e.origin_y = org * t_left;
2319 e.origin_z = org * t_up;
2321 // current forward and up directions
2322 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2323 e.angles = AnglesTransform_FromVAngles(e.angles);
2325 e.angles = AnglesTransform_FromAngles(e.angles);
2326 fixedmakevectors(e.angles);
2328 // untransform forward, up!
2329 e_forward_x = v_forward * t_forward;
2330 e_forward_y = v_forward * t_left;
2331 e_forward_z = v_forward * t_up;
2332 e_up_x = v_up * t_forward;
2333 e_up_y = v_up * t_left;
2334 e_up_z = v_up * t_up;
2336 e.angles = fixedvectoangles2(e_forward, e_up);
2337 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2338 e.angles = AnglesTransform_ToVAngles(e.angles);
2340 e.angles = AnglesTransform_ToAngles(e.angles);
2342 setattachment(e, to, tag);
2343 setorigin(e, e.origin);
2346 void detach_sameorigin(entity e)
2349 org = gettaginfo(e, 0);
2350 e.angles = fixedvectoangles2(v_forward, v_up);
2351 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2352 e.angles = AnglesTransform_ToVAngles(e.angles);
2354 e.angles = AnglesTransform_ToAngles(e.angles);
2356 setattachment(e, world, "");
2357 setorigin(e, e.origin);
2360 void follow_sameorigin(entity e, entity to)
2362 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2363 e.aiment = to; // make the hole follow bmodel
2364 e.punchangle = to.angles; // the original angles of bmodel
2365 e.view_ofs = e.origin - to.origin; // relative origin
2366 e.v_angle = e.angles - to.angles; // relative angles
2369 void unfollow_sameorigin(entity e)
2371 e.movetype = MOVETYPE_NONE;
2374 entity gettaginfo_relative_ent;
2375 vector gettaginfo_relative(entity e, float tag)
2377 if (!gettaginfo_relative_ent)
2379 gettaginfo_relative_ent = spawn();
2380 gettaginfo_relative_ent.effects = EF_NODRAW;
2382 gettaginfo_relative_ent.model = e.model;
2383 gettaginfo_relative_ent.modelindex = e.modelindex;
2384 gettaginfo_relative_ent.frame = e.frame;
2385 return gettaginfo(gettaginfo_relative_ent, tag);
2390 float modeleffect_SendEntity(entity to, float sf)
2393 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2396 if(self.velocity != '0 0 0')
2398 if(self.angles != '0 0 0')
2400 if(self.avelocity != '0 0 0')
2403 WriteByte(MSG_ENTITY, f);
2404 WriteShort(MSG_ENTITY, self.modelindex);
2405 WriteByte(MSG_ENTITY, self.skin);
2406 WriteByte(MSG_ENTITY, self.frame);
2407 WriteCoord(MSG_ENTITY, self.origin_x);
2408 WriteCoord(MSG_ENTITY, self.origin_y);
2409 WriteCoord(MSG_ENTITY, self.origin_z);
2412 WriteCoord(MSG_ENTITY, self.velocity_x);
2413 WriteCoord(MSG_ENTITY, self.velocity_y);
2414 WriteCoord(MSG_ENTITY, self.velocity_z);
2418 WriteCoord(MSG_ENTITY, self.angles_x);
2419 WriteCoord(MSG_ENTITY, self.angles_y);
2420 WriteCoord(MSG_ENTITY, self.angles_z);
2424 WriteCoord(MSG_ENTITY, self.avelocity_x);
2425 WriteCoord(MSG_ENTITY, self.avelocity_y);
2426 WriteCoord(MSG_ENTITY, self.avelocity_z);
2428 WriteShort(MSG_ENTITY, self.scale * 256.0);
2429 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2430 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2431 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2432 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2437 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)
2442 e.classname = "modeleffect";
2450 e.teleport_time = t1;
2454 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2458 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2461 sz = max(e.scale, e.scale2);
2462 setsize(e, e.mins * sz, e.maxs * sz);
2463 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2466 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2468 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2471 float randombit(float bits)
2473 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2482 for(f = 1; f <= bits; f *= 2)
2491 r = (r - 1) / (n - 1);
2498 float randombits(float bits, float k, float error_return)
2502 while(k > 0 && bits != r)
2504 r += randombit(bits - r);
2513 void randombit_test(float bits, float iter)
2517 print(ftos(randombit(bits)), "\n");
2522 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2524 if(halflifedist > 0)
2525 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2526 else if(halflifedist < 0)
2527 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2536 #define cvar_string_normal builtin_cvar_string
2537 #define cvar_normal builtin_cvar
2539 string cvar_string_normal(string n)
2541 if not(cvar_type(n) & 1)
2542 backtrace(strcat("Attempt to access undefined cvar: ", n));
2543 return builtin_cvar_string(n);
2546 float cvar_normal(string n)
2548 return stof(cvar_string_normal(n));
2551 #define cvar_set_normal builtin_cvar_set
2559 oself.think = SUB_Remove;
2560 oself.nextthink = time;
2566 Execute func() after time + fdelay.
2567 self when func is executed = self when defer is called
2569 void defer(float fdelay, void() func)
2576 e.think = defer_think;
2577 e.nextthink = time + fdelay;
2580 .string aiment_classname;
2581 .float aiment_deadflag;
2582 void SetMovetypeFollow(entity ent, entity e)
2584 // FIXME this may not be warpzone aware
2585 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2586 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.
2587 ent.aiment = e; // make the hole follow bmodel
2588 ent.punchangle = e.angles; // the original angles of bmodel
2589 ent.view_ofs = ent.origin - e.origin; // relative origin
2590 ent.v_angle = ent.angles - e.angles; // relative angles
2591 ent.aiment_classname = strzone(e.classname);
2592 ent.aiment_deadflag = e.deadflag;
2594 void UnsetMovetypeFollow(entity ent)
2596 ent.movetype = MOVETYPE_FLY;
2597 PROJECTILE_MAKETRIGGER(ent);
2600 float LostMovetypeFollow(entity ent)
2603 if(ent.movetype != MOVETYPE_FOLLOW)
2609 if(ent.aiment.classname != ent.aiment_classname)
2611 if(ent.aiment.deadflag != ent.aiment_deadflag)
2617 float isPushable(entity e)
2626 case "droppedweapon":
2627 case "keepawayball":
2628 case "nexball_basketball":
2629 case "nexball_football":
2631 case "bullet": // antilagged bullets can't hit this either
2634 if (e.projectiledeathtype)
2639 void dedicated_print(string input) // print(), but only print if the server is not local
2641 if not(server_is_local) { print(input); }