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)
82 string STR_PLAYER = "player";
83 string STR_SPECTATOR = "spectator";
84 string STR_OBSERVER = "observer";
86 #define IS_PLAYER(v) (v.classname == STR_PLAYER)
87 #define IS_SPEC(v) (v.classname == STR_SPECTATOR)
88 #define IS_OBSERVER(v) (v.classname == STR_OBSERVER)
89 #define IS_CLIENT(v) (v.flags & FL_CLIENT)
90 #define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT)
91 #define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL)
92 #define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT)
94 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
95 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v))
96 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v))
98 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v))
99 #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if not(IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too
100 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v))
102 #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
104 // copies a string to a tempstring (so one can strunzone it)
105 string strcat1(string s) = #115; // FRIK_FILE
110 void GameLogEcho(string s)
115 if (autocvar_sv_eventlog_files)
120 matches = autocvar_sv_eventlog_files_counter + 1;
121 cvar_set("sv_eventlog_files_counter", ftos(matches));
124 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
125 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
126 logfile = fopen(fn, FILE_APPEND);
127 fputs(logfile, ":logversion:3\n");
131 if (autocvar_sv_eventlog_files_timestamps)
132 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
134 fputs(logfile, strcat(s, "\n"));
137 if (autocvar_sv_eventlog_console)
146 // will be opened later
151 if (logfile_open && logfile >= 0)
158 float spawnpoint_nag;
159 void relocate_spawnpoint()
161 // nudge off the floor
162 setorigin(self, self.origin + '0 0 1');
164 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
165 if (trace_startsolid)
171 if (!move_out_of_solid(self))
172 objerror("could not get out of solid at all!");
173 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
174 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
175 print(" ", ftos(self.origin_y - o_y));
176 print(" ", ftos(self.origin_z - o_z), "'\n");
177 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
180 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
186 self.mins = self.maxs = '0 0 0';
187 objerror("player spawn point in solid, mapper sucks!\n");
192 self.use = spawnpoint_use;
193 self.team_saved = self.team;
197 if (have_team_spawns != 0)
199 have_team_spawns = 1;
200 have_team_spawns_forteam[self.team] = 1;
202 if (autocvar_r_showbboxes)
204 // show where spawnpoints point at too
205 makevectors(self.angles);
208 e.classname = "info_player_foo";
209 setorigin(e, self.origin + v_forward * 24);
210 setsize(e, '-8 -8 -8', '8 8 8');
211 e.solid = SOLID_TRIGGER;
215 #define strstr strstrofs
217 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
218 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
219 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
220 // BE CONSTANT OR strzoneD!
221 float strstr(string haystack, string needle, float offset)
225 len = strlen(needle);
226 endpos = strlen(haystack) - len;
227 while(offset <= endpos)
229 found = substring(haystack, offset, len);
238 float NUM_NEAREST_ENTITIES = 4;
239 entity nearest_entity[NUM_NEAREST_ENTITIES];
240 float nearest_length[NUM_NEAREST_ENTITIES];
241 entity findnearest(vector point, .string field, string value, vector axismod)
252 localhead = find(world, field, value);
255 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
256 dist = localhead.oldorigin;
258 dist = localhead.origin;
260 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
263 for (i = 0; i < num_nearest; ++i)
265 if (len < nearest_length[i])
269 // now i tells us where to insert at
270 // INSERTION SORT! YOU'VE SEEN IT! RUN!
271 if (i < NUM_NEAREST_ENTITIES)
273 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
275 nearest_length[j + 1] = nearest_length[j];
276 nearest_entity[j + 1] = nearest_entity[j];
278 nearest_length[i] = len;
279 nearest_entity[i] = localhead;
280 if (num_nearest < NUM_NEAREST_ENTITIES)
281 num_nearest = num_nearest + 1;
284 localhead = find(localhead, field, value);
287 // now use the first one from our list that we can see
288 for (i = 0; i < num_nearest; ++i)
290 traceline(point, nearest_entity[i].origin, TRUE, world);
291 if (trace_fraction == 1)
295 dprint("Nearest point (");
296 dprint(nearest_entity[0].netname);
297 dprint(") is not visible, using a visible one.\n");
299 return nearest_entity[i];
303 if (num_nearest == 0)
306 dprint("Not seeing any location point, using nearest as fallback.\n");
308 dprint("Candidates were: ");
309 for(j = 0; j < num_nearest; ++j)
313 dprint(nearest_entity[j].netname);
318 return nearest_entity[0];
321 void spawnfunc_target_location()
323 self.classname = "target_location";
324 // location name in netname
325 // eventually support: count, teamgame selectors, line of sight?
328 void spawnfunc_info_location()
330 self.classname = "target_location";
331 self.message = self.netname;
334 string NearestLocation(vector p)
339 loc = findnearest(p, classname, "target_location", '1 1 1');
346 loc = findnearest(p, target, "###item###", '1 1 4');
353 string formatmessage(string msg)
364 WarpZone_crosshair_trace(self);
365 cursor = trace_endpos;
366 cursor_ent = trace_ent;
370 break; // too many replacements
373 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
374 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
387 replacement = substring(msg, p, 2);
388 escape = substring(msg, p + 1, 1);
392 else if (escape == "\\")
394 else if (escape == "n")
396 else if (escape == "a")
397 replacement = ftos(floor(self.armorvalue));
398 else if (escape == "h")
399 replacement = ftos(floor(self.health));
400 else if (escape == "l")
401 replacement = NearestLocation(self.origin);
402 else if (escape == "y")
403 replacement = NearestLocation(cursor);
404 else if (escape == "d")
405 replacement = NearestLocation(self.death_origin);
406 else if (escape == "w") {
410 wep = self.switchweapon;
413 replacement = W_Name(wep);
414 } else if (escape == "W") {
415 if (self.items & IT_SHELLS) replacement = "shells";
416 else if (self.items & IT_NAILS) replacement = "bullets";
417 else if (self.items & IT_ROCKETS) replacement = "rockets";
418 else if (self.items & IT_CELLS) replacement = "cells";
419 else replacement = "batteries"; // ;)
420 } else if (escape == "x") {
421 replacement = cursor_ent.netname;
422 if (replacement == "" || !cursor_ent)
423 replacement = "nothing";
424 } else if (escape == "s")
425 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
426 else if (escape == "S")
427 replacement = ftos(vlen(self.velocity));
429 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
430 p = p + strlen(replacement);
435 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
436 return (value == 0) ? FALSE : TRUE;
445 >0: receives a cvar from name=argv(f) value=argv(f+1)
447 void GetCvars_handleString(string thisname, float f, .string field, string name)
452 strunzone(self.field);
453 self.field = string_null;
457 if (thisname == name)
460 strunzone(self.field);
461 self.field = strzone(argv(f + 1));
465 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
467 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
469 GetCvars_handleString(thisname, f, field, name);
470 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
471 if (thisname == name)
474 s = func(strcat1(self.field));
477 strunzone(self.field);
478 self.field = strzone(s);
482 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
489 if (thisname == name)
490 self.field = stof(argv(f + 1));
493 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
495 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
502 if (thisname == name)
506 self.field = stof(argv(f + 1));
515 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
518 float w_getbestweapon(entity e);
519 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
522 o = W_FixWeaponOrder_ForceComplete(wo);
523 if(self.weaponorder_byimpulse)
525 strunzone(self.weaponorder_byimpulse);
526 self.weaponorder_byimpulse = string_null;
528 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
531 void GetCvars(float f)
533 string s = string_null;
536 s = strcat1(argv(f));
541 MUTATOR_CALLHOOK(GetCvars);
543 Notification_GetCvars();
545 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
546 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
547 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
548 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
549 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
550 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
551 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
552 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
553 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
554 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
555 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
556 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
557 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
558 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
559 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
560 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
561 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
562 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
563 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
564 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
565 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
566 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
567 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
569 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
570 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
572 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
573 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
574 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
575 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
576 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
578 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
581 if (s == "cl_weaponpriority")
582 self.switchweapon = w_getbestweapon(self);
583 if (s == "cl_allow_uidtracking")
584 PlayerStats_AddPlayer(self);
588 // decolorizes and team colors the player name when needed
589 string playername(entity p)
592 if (teamplay && !intermission_running && p.classname == "player")
594 t = Team_ColorCode(p.team);
595 return strcat(t, strdecolorize(p.netname));
601 vector randompos(vector m1, vector m2)
605 v_x = m2_x * random() + m1_x;
606 v_y = m2_y * random() + m1_y;
607 v_z = m2_z * random() + m1_z;
611 //#NO AUTOCVARS START
613 float g_pickup_shells;
614 float g_pickup_shells_max;
615 float g_pickup_nails;
616 float g_pickup_nails_max;
617 float g_pickup_rockets;
618 float g_pickup_rockets_max;
619 float g_pickup_cells;
620 float g_pickup_cells_max;
622 float g_pickup_fuel_jetpack;
623 float g_pickup_fuel_max;
624 float g_pickup_armorsmall;
625 float g_pickup_armorsmall_max;
626 float g_pickup_armorsmall_anyway;
627 float g_pickup_armormedium;
628 float g_pickup_armormedium_max;
629 float g_pickup_armormedium_anyway;
630 float g_pickup_armorbig;
631 float g_pickup_armorbig_max;
632 float g_pickup_armorbig_anyway;
633 float g_pickup_armorlarge;
634 float g_pickup_armorlarge_max;
635 float g_pickup_armorlarge_anyway;
636 float g_pickup_healthsmall;
637 float g_pickup_healthsmall_max;
638 float g_pickup_healthsmall_anyway;
639 float g_pickup_healthmedium;
640 float g_pickup_healthmedium_max;
641 float g_pickup_healthmedium_anyway;
642 float g_pickup_healthlarge;
643 float g_pickup_healthlarge_max;
644 float g_pickup_healthlarge_anyway;
645 float g_pickup_healthmega;
646 float g_pickup_healthmega_max;
647 float g_pickup_healthmega_anyway;
648 float g_pickup_ammo_anyway;
649 float g_pickup_weapons_anyway;
651 WEPSET_DECLARE_A(g_weaponarena_weapons);
652 float g_weaponarena_random;
653 float g_weaponarena_random_with_laser;
654 string g_weaponarena_list;
655 float g_weaponspeedfactor;
656 float g_weaponratefactor;
657 float g_weapondamagefactor;
658 float g_weaponforcefactor;
659 float g_weaponspreadfactor;
661 WEPSET_DECLARE_A(start_weapons);
662 WEPSET_DECLARE_A(start_weapons_default);
663 WEPSET_DECLARE_A(start_weapons_defaultmask);
665 float start_ammo_shells;
666 float start_ammo_nails;
667 float start_ammo_rockets;
668 float start_ammo_cells;
669 float start_ammo_fuel;
671 float start_armorvalue;
672 WEPSET_DECLARE_A(warmup_start_weapons);
673 WEPSET_DECLARE_A(warmup_start_weapons_default);
674 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
675 float warmup_start_ammo_shells;
676 float warmup_start_ammo_nails;
677 float warmup_start_ammo_rockets;
678 float warmup_start_ammo_cells;
679 float warmup_start_ammo_fuel;
680 float warmup_start_health;
681 float warmup_start_armorvalue;
684 entity get_weaponinfo(float w);
686 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
688 var float i = weaponinfo.weapon;
694 if (g_lms || g_ca || allguns)
696 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
702 d = (i == WEP_SHOTGUN);
704 d = 0; // weapon is set a few lines later
706 d = (i == WEP_LASER || i == WEP_SHOTGUN);
708 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
709 d |= (i == WEP_HOOK);
710 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
713 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
715 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
720 // 4: is set by default?
729 void readplayerstartcvars()
735 // initialize starting values for players
736 WEPSET_CLEAR_A(start_weapons);
737 WEPSET_CLEAR_A(start_weapons_default);
738 WEPSET_CLEAR_A(start_weapons_defaultmask);
740 start_ammo_shells = 0;
741 start_ammo_nails = 0;
742 start_ammo_rockets = 0;
743 start_ammo_cells = 0;
744 start_health = cvar("g_balance_health_start");
745 start_armorvalue = cvar("g_balance_armor_start");
748 WEPSET_CLEAR_A(g_weaponarena_weapons);
750 s = cvar_string("g_weaponarena");
751 if (s == "0" || s == "")
757 if (s == "0" || s == "")
763 // forcibly turn off weaponarena
768 g_weaponarena_list = "All Weapons";
769 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
771 e = get_weaponinfo(j);
772 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
773 WEPSET_OR_AW(g_weaponarena_weapons, j);
776 else if (s == "most")
779 g_weaponarena_list = "Most Weapons";
780 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
782 e = get_weaponinfo(j);
783 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
784 if (e.spawnflags & WEP_FLAG_NORMAL)
785 WEPSET_OR_AW(g_weaponarena_weapons, j);
788 else if (s == "none")
791 g_weaponarena_list = "No Weapons";
796 t = tokenize_console(s);
797 g_weaponarena_list = "";
798 for (i = 0; i < t; ++i)
801 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
803 e = get_weaponinfo(j);
806 WEPSET_OR_AW(g_weaponarena_weapons, j);
807 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
813 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
816 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
820 g_weaponarena_random = cvar("g_weaponarena_random");
822 g_weaponarena_random = 0;
823 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
827 g_minstagib = 0; // incompatible
828 g_pinata = 0; // incompatible
829 g_weapon_stay = 0; // incompatible
830 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
832 start_items |= IT_UNLIMITED_AMMO;
834 else if (g_minstagib)
836 g_pinata = 0; // incompatible
837 g_weapon_stay = 0; // incompatible
838 g_bloodloss = 0; // incompatible
840 start_armorvalue = 0;
841 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
842 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
843 start_items |= IT_UNLIMITED_SUPERWEAPONS;
845 if (g_minstagib_invis_alpha <= 0)
846 g_minstagib_invis_alpha = -1;
850 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
852 e = get_weaponinfo(i);
853 float w = want_weapon("g_start_weapon_", e, FALSE);
855 WEPSET_OR_AW(start_weapons, i);
857 WEPSET_OR_AW(start_weapons_default, i);
859 WEPSET_OR_AW(start_weapons_defaultmask, i);
863 if(!cvar("g_use_ammunition"))
864 start_items |= IT_UNLIMITED_AMMO;
866 if(cvar("g_nexball"))
867 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
871 start_ammo_cells = cvar("g_minstagib_ammo_start");
872 start_ammo_fuel = cvar("g_start_ammo_fuel");
874 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
876 start_ammo_rockets = 999;
877 start_ammo_shells = 999;
878 start_ammo_cells = 999;
879 start_ammo_nails = 999;
880 start_ammo_fuel = 999;
886 start_ammo_shells = cvar("g_lms_start_ammo_shells");
887 start_ammo_nails = cvar("g_lms_start_ammo_nails");
888 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
889 start_ammo_cells = cvar("g_lms_start_ammo_cells");
890 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
894 start_ammo_shells = cvar("g_start_ammo_shells");
895 start_ammo_nails = cvar("g_start_ammo_nails");
896 start_ammo_rockets = cvar("g_start_ammo_rockets");
897 start_ammo_cells = cvar("g_start_ammo_cells");
898 start_ammo_fuel = cvar("g_start_ammo_fuel");
904 start_health = cvar("g_lms_start_health");
905 start_armorvalue = cvar("g_lms_start_armor");
910 warmup_start_ammo_shells = start_ammo_shells;
911 warmup_start_ammo_nails = start_ammo_nails;
912 warmup_start_ammo_rockets = start_ammo_rockets;
913 warmup_start_ammo_cells = start_ammo_cells;
914 warmup_start_ammo_fuel = start_ammo_fuel;
915 warmup_start_health = start_health;
916 warmup_start_armorvalue = start_armorvalue;
917 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
918 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
919 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
921 if (!g_weaponarena && !g_minstagib && !g_ca)
923 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
924 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
925 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
926 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
927 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
928 warmup_start_health = cvar("g_warmup_start_health");
929 warmup_start_armorvalue = cvar("g_warmup_start_armor");
930 WEPSET_CLEAR_A(warmup_start_weapons);
931 WEPSET_CLEAR_A(warmup_start_weapons_default);
932 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
933 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
935 e = get_weaponinfo(i);
936 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
938 WEPSET_OR_AW(warmup_start_weapons, i);
940 WEPSET_OR_AW(warmup_start_weapons_default, i);
942 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
948 start_items |= IT_JETPACK;
950 MUTATOR_CALLHOOK(SetStartItems);
952 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
954 g_grappling_hook = 0; // these two can't coexist, as they use the same button
955 start_items |= IT_FUEL_REGEN;
956 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
957 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
960 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
962 e = get_weaponinfo(i);
963 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
964 weapon_action(i, WR_PRECACHE);
967 start_ammo_shells = max(0, start_ammo_shells);
968 start_ammo_nails = max(0, start_ammo_nails);
969 start_ammo_cells = max(0, start_ammo_cells);
970 start_ammo_rockets = max(0, start_ammo_rockets);
971 start_ammo_fuel = max(0, start_ammo_fuel);
973 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
974 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
975 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
976 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
977 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
981 float g_bugrigs_planar_movement;
982 float g_bugrigs_planar_movement_car_jumping;
983 float g_bugrigs_reverse_spinning;
984 float g_bugrigs_reverse_speeding;
985 float g_bugrigs_reverse_stopping;
986 float g_bugrigs_air_steering;
987 float g_bugrigs_angle_smoothing;
988 float g_bugrigs_friction_floor;
989 float g_bugrigs_friction_brake;
990 float g_bugrigs_friction_air;
991 float g_bugrigs_accel;
992 float g_bugrigs_speed_ref;
993 float g_bugrigs_speed_pow;
994 float g_bugrigs_steer;
996 float g_touchexplode;
997 float g_touchexplode_radius;
998 float g_touchexplode_damage;
999 float g_touchexplode_edgedamage;
1000 float g_touchexplode_force;
1005 string GetGametype(); // g_world.qc
1006 void readlevelcvars(void)
1008 g_minstagib = cvar("g_minstagib");
1010 // load ALL the mutators
1011 if(cvar("g_dodging"))
1012 MUTATOR_ADD(mutator_dodging);
1013 if(cvar("g_spawn_near_teammate"))
1014 MUTATOR_ADD(mutator_spawn_near_teammate);
1015 if(cvar("g_physical_items"))
1016 MUTATOR_ADD(mutator_physical_items);
1019 if(cvar("g_invincible_projectiles"))
1020 MUTATOR_ADD(mutator_invincibleprojectiles);
1021 if(cvar("g_new_toys"))
1022 MUTATOR_ADD(mutator_new_toys);
1024 MUTATOR_ADD(mutator_nix);
1025 if(cvar("g_rocket_flying"))
1026 MUTATOR_ADD(mutator_rocketflying);
1027 if(cvar("g_vampire"))
1028 MUTATOR_ADD(mutator_vampire);
1029 if(cvar("g_superspectate"))
1030 MUTATOR_ADD(mutator_superspec);
1033 // is this a mutator? is this a mode?
1034 if(cvar("g_sandbox"))
1035 MUTATOR_ADD(sandbox);
1037 if(cvar("sv_allow_fullbright"))
1038 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1040 g_bugrigs = cvar("g_bugrigs");
1041 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1042 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1043 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1044 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1045 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1046 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1047 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1048 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1049 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1050 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1051 g_bugrigs_accel = cvar("g_bugrigs_accel");
1052 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1053 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1054 g_bugrigs_steer = cvar("g_bugrigs_steer");
1056 g_touchexplode = cvar("g_touchexplode");
1057 g_touchexplode_radius = cvar("g_touchexplode_radius");
1058 g_touchexplode_damage = cvar("g_touchexplode_damage");
1059 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1060 g_touchexplode_force = cvar("g_touchexplode_force");
1062 sv_clones = cvar("sv_clones");
1063 sv_foginterval = cvar("sv_foginterval");
1064 g_cloaked = cvar("g_cloaked");
1066 g_cloaked = 1; // always enable cloak in CTS
1067 g_jump_grunt = cvar("g_jump_grunt");
1068 g_footsteps = cvar("g_footsteps");
1069 g_grappling_hook = cvar("g_grappling_hook");
1070 g_jetpack = cvar("g_jetpack");
1071 g_midair = cvar("g_midair");
1072 g_norecoil = cvar("g_norecoil");
1073 g_bloodloss = cvar("g_bloodloss");
1074 sv_maxidle = cvar("sv_maxidle");
1075 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1076 sv_autotaunt = cvar("sv_autotaunt");
1077 sv_taunt = cvar("sv_taunt");
1079 inWarmupStage = cvar("g_warmup");
1080 g_warmup_limit = cvar("g_warmup_limit");
1081 g_warmup_allguns = cvar("g_warmup_allguns");
1082 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1084 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1085 inWarmupStage = 0; // these modes cannot work together, sorry
1087 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1088 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1089 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1090 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1091 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1092 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1093 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1094 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1095 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1096 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1097 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1098 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1099 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1100 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1102 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1103 g_weaponratefactor = cvar("g_weaponratefactor");
1104 g_weapondamagefactor = cvar("g_weapondamagefactor");
1105 g_weaponforcefactor = cvar("g_weaponforcefactor");
1106 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1108 g_pickup_shells = cvar("g_pickup_shells");
1109 g_pickup_shells_max = cvar("g_pickup_shells_max");
1110 g_pickup_nails = cvar("g_pickup_nails");
1111 g_pickup_nails_max = cvar("g_pickup_nails_max");
1112 g_pickup_rockets = cvar("g_pickup_rockets");
1113 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1114 g_pickup_cells = cvar("g_pickup_cells");
1115 g_pickup_cells_max = cvar("g_pickup_cells_max");
1116 g_pickup_fuel = cvar("g_pickup_fuel");
1117 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1118 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1119 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1120 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1121 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1122 g_pickup_armormedium = cvar("g_pickup_armormedium");
1123 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1124 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1125 g_pickup_armorbig = cvar("g_pickup_armorbig");
1126 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1127 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1128 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1129 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1130 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1131 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1132 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1133 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1134 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1135 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1136 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1137 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1138 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1139 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1140 g_pickup_healthmega = cvar("g_pickup_healthmega");
1141 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1142 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1144 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1145 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1147 g_pinata = cvar("g_pinata");
1149 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1151 g_weapon_stay = cvar("g_weapon_stay");
1153 if not(inWarmupStage && !g_ca)
1154 game_starttime = cvar("g_start_delay");
1156 readplayerstartcvars();
1162 string precache_sound (string s) = #19;
1163 float precache_sound_index (string s) = #19;
1165 #define SND_VOLUME 1
1166 #define SND_ATTENUATION 2
1167 #define SND_LARGEENTITY 8
1168 #define SND_LARGESOUND 16
1170 float sound_allowed(float dest, entity e)
1172 // sounds from world may always pass
1175 if (e.classname == "body")
1177 else if (e.realowner && e.realowner != e)
1179 else if (e.owner && e.owner != e)
1184 // sounds to self may always pass
1185 if (dest == MSG_ONE)
1186 if (e == msg_entity)
1188 // sounds by players can be removed
1189 if (autocvar_bot_sound_monopoly)
1190 if (clienttype(e) == CLIENTTYPE_REAL)
1192 // anything else may pass
1196 #ifdef COMPAT_XON010_CHANNELS
1197 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1198 void sound(entity e, float chan, string samp, float vol, float atten)
1200 if (!sound_allowed(MSG_BROADCAST, e))
1202 builtin_sound(e, chan, samp, vol, atten);
1206 void sound(entity e, float chan, string samp, float vol, float atten)
1208 if (!sound_allowed(MSG_BROADCAST, e))
1210 sound7(e, chan, samp, vol, atten, 0, 0);
1214 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1218 if (!sound_allowed(dest, e))
1221 entno = num_for_edict(e);
1222 idx = precache_sound_index(samp);
1227 atten = floor(atten * 64);
1228 vol = floor(vol * 255);
1231 sflags |= SND_VOLUME;
1233 sflags |= SND_ATTENUATION;
1234 if (entno >= 8192 || chan < 0 || chan > 7)
1235 sflags |= SND_LARGEENTITY;
1237 sflags |= SND_LARGESOUND;
1239 WriteByte(dest, SVC_SOUND);
1240 WriteByte(dest, sflags);
1241 if (sflags & SND_VOLUME)
1242 WriteByte(dest, vol);
1243 if (sflags & SND_ATTENUATION)
1244 WriteByte(dest, atten);
1245 if (sflags & SND_LARGEENTITY)
1247 WriteShort(dest, entno);
1248 WriteByte(dest, chan);
1252 WriteShort(dest, entno * 8 + chan);
1254 if (sflags & SND_LARGESOUND)
1255 WriteShort(dest, idx);
1257 WriteByte(dest, idx);
1259 WriteCoord(dest, o_x);
1260 WriteCoord(dest, o_y);
1261 WriteCoord(dest, o_z);
1263 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1267 if (!sound_allowed(dest, e))
1270 o = e.origin + 0.5 * (e.mins + e.maxs);
1271 soundtoat(dest, e, o, chan, samp, vol, atten);
1273 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1275 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1277 void stopsoundto(float dest, entity e, float chan)
1281 if (!sound_allowed(dest, e))
1284 entno = num_for_edict(e);
1286 if (entno >= 8192 || chan < 0 || chan > 7)
1289 idx = precache_sound_index("misc/null.wav");
1290 sflags = SND_LARGEENTITY;
1292 sflags |= SND_LARGESOUND;
1293 WriteByte(dest, SVC_SOUND);
1294 WriteByte(dest, sflags);
1295 WriteShort(dest, entno);
1296 WriteByte(dest, chan);
1297 if (sflags & SND_LARGESOUND)
1298 WriteShort(dest, idx);
1300 WriteByte(dest, idx);
1301 WriteCoord(dest, e.origin_x);
1302 WriteCoord(dest, e.origin_y);
1303 WriteCoord(dest, e.origin_z);
1307 WriteByte(dest, SVC_STOPSOUND);
1308 WriteShort(dest, entno * 8 + chan);
1311 void stopsound(entity e, float chan)
1313 if (!sound_allowed(MSG_BROADCAST, e))
1316 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1317 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1320 void play2(entity e, string filename)
1322 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1324 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1327 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1329 float spamsound(entity e, float chan, string samp, float vol, float atten)
1331 if (!sound_allowed(MSG_BROADCAST, e))
1334 if (time > e.spamtime)
1337 sound(e, chan, samp, vol, atten);
1343 void play2team(float t, string filename)
1347 if (autocvar_bot_sound_monopoly)
1350 FOR_EACH_REALPLAYER(head)
1353 play2(head, filename);
1357 void play2all(string samp)
1359 if (autocvar_bot_sound_monopoly)
1362 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1365 void PrecachePlayerSounds(string f);
1366 void precache_playermodel(string m)
1368 float globhandle, i, n;
1371 if(substring(m, -9,5) == "_lod1")
1373 if(substring(m, -9,5) == "_lod2")
1376 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1379 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1383 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1386 n = search_getsize(globhandle);
1387 for (i = 0; i < n; ++i)
1389 //print(search_getfilename(globhandle, i), "\n");
1390 f = search_getfilename(globhandle, i);
1391 PrecachePlayerSounds(f);
1393 search_end(globhandle);
1395 void precache_all_playermodels(string pattern)
1397 float globhandle, i, n;
1400 globhandle = search_begin(pattern, TRUE, FALSE);
1403 n = search_getsize(globhandle);
1404 for (i = 0; i < n; ++i)
1406 //print(search_getfilename(globhandle, i), "\n");
1407 f = search_getfilename(globhandle, i);
1408 precache_playermodel(f);
1410 search_end(globhandle);
1415 // gamemode related things
1416 precache_model ("models/misc/chatbubble.spr");
1419 precache_model ("models/runematch/curse.mdl");
1420 precache_model ("models/runematch/rune.mdl");
1423 #ifdef TTURRETS_ENABLED
1424 if (autocvar_g_turrets)
1428 // Precache all player models if desired
1429 if (autocvar_sv_precacheplayermodels)
1431 PrecachePlayerSounds("sound/player/default.sounds");
1432 precache_all_playermodels("models/player/*.zym");
1433 precache_all_playermodels("models/player/*.dpm");
1434 precache_all_playermodels("models/player/*.md3");
1435 precache_all_playermodels("models/player/*.psk");
1436 precache_all_playermodels("models/player/*.iqm");
1439 if (autocvar_sv_defaultcharacter)
1442 s = autocvar_sv_defaultplayermodel_red;
1444 precache_playermodel(s);
1445 s = autocvar_sv_defaultplayermodel_blue;
1447 precache_playermodel(s);
1448 s = autocvar_sv_defaultplayermodel_yellow;
1450 precache_playermodel(s);
1451 s = autocvar_sv_defaultplayermodel_pink;
1453 precache_playermodel(s);
1454 s = autocvar_sv_defaultplayermodel;
1456 precache_playermodel(s);
1461 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1462 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1465 // gore and miscellaneous sounds
1466 //precache_sound ("misc/h2ohit.wav");
1467 precache_model ("models/hook.md3");
1468 precache_sound ("misc/armorimpact.wav");
1469 precache_sound ("misc/bodyimpact1.wav");
1470 precache_sound ("misc/bodyimpact2.wav");
1471 precache_sound ("misc/gib.wav");
1472 precache_sound ("misc/gib_splat01.wav");
1473 precache_sound ("misc/gib_splat02.wav");
1474 precache_sound ("misc/gib_splat03.wav");
1475 precache_sound ("misc/gib_splat04.wav");
1476 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1477 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1478 precache_sound ("misc/null.wav");
1479 precache_sound ("misc/spawn.wav");
1480 precache_sound ("misc/talk.wav");
1481 precache_sound ("misc/teleport.wav");
1482 precache_sound ("misc/poweroff.wav");
1483 precache_sound ("player/lava.wav");
1484 precache_sound ("player/slime.wav");
1486 precache_model ("models/sprites/0.spr32");
1487 precache_model ("models/sprites/1.spr32");
1488 precache_model ("models/sprites/2.spr32");
1489 precache_model ("models/sprites/3.spr32");
1490 precache_model ("models/sprites/4.spr32");
1491 precache_model ("models/sprites/5.spr32");
1492 precache_model ("models/sprites/6.spr32");
1493 precache_model ("models/sprites/7.spr32");
1494 precache_model ("models/sprites/8.spr32");
1495 precache_model ("models/sprites/9.spr32");
1496 precache_model ("models/sprites/10.spr32");
1498 // common weapon precaches
1499 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1500 precache_sound ("weapons/weapon_switch.wav");
1501 precache_sound ("weapons/weaponpickup.wav");
1502 precache_sound ("weapons/unavailable.wav");
1503 precache_sound ("weapons/dryfire.wav");
1504 if (g_grappling_hook)
1506 precache_sound ("weapons/hook_fire.wav"); // hook
1507 precache_sound ("weapons/hook_impact.wav"); // hook
1510 if(autocvar_sv_precacheweapons)
1512 //precache weapon models/sounds
1515 while (wep <= WEP_LAST)
1517 weapon_action(wep, WR_PRECACHE);
1522 precache_model("models/elaser.mdl");
1523 precache_model("models/laser.mdl");
1524 precache_model("models/ebomb.mdl");
1527 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1529 if (!self.noise && self.music) // quake 3 uses the music field
1530 self.noise = self.music;
1532 // plays music for the level if there is any
1535 precache_sound (self.noise);
1536 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1541 // WARNING: this kills the trace globals
1542 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1543 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1545 #define INITPRIO_FIRST 0
1546 #define INITPRIO_GAMETYPE 0
1547 #define INITPRIO_GAMETYPE_FALLBACK 1
1548 #define INITPRIO_FINDTARGET 10
1549 #define INITPRIO_DROPTOFLOOR 20
1550 #define INITPRIO_SETLOCATION 90
1551 #define INITPRIO_LINKDOORS 91
1552 #define INITPRIO_LAST 99
1554 .void(void) initialize_entity;
1555 .float initialize_entity_order;
1556 .entity initialize_entity_next;
1557 entity initialize_entity_first;
1559 void make_safe_for_remove(entity e)
1561 if (e.initialize_entity)
1563 entity ent, prev = world;
1564 for (ent = initialize_entity_first; ent; )
1566 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1568 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1569 // skip it in linked list
1572 prev.initialize_entity_next = ent.initialize_entity_next;
1573 ent = prev.initialize_entity_next;
1577 initialize_entity_first = ent.initialize_entity_next;
1578 ent = initialize_entity_first;
1584 ent = ent.initialize_entity_next;
1590 void objerror(string s)
1592 make_safe_for_remove(self);
1593 builtin_objerror(s);
1596 .float remove_except_protected_forbidden;
1597 void remove_except_protected(entity e)
1599 if(e.remove_except_protected_forbidden)
1600 error("not allowed to remove this at this point");
1604 void remove_unsafely(entity e)
1606 if(e.classname == "spike")
1607 error("Removing spikes is forbidden (crylink bug), please report");
1611 void remove_safely(entity e)
1613 make_safe_for_remove(e);
1617 void InitializeEntity(entity e, void(void) func, float order)
1621 if (!e || e.initialize_entity)
1623 // make a proxy initializer entity
1627 e.classname = "initialize_entity";
1631 e.initialize_entity = func;
1632 e.initialize_entity_order = order;
1634 cur = initialize_entity_first;
1638 if (!cur || cur.initialize_entity_order > order)
1640 // insert between prev and cur
1642 prev.initialize_entity_next = e;
1644 initialize_entity_first = e;
1645 e.initialize_entity_next = cur;
1649 cur = cur.initialize_entity_next;
1652 void InitializeEntitiesRun()
1655 startoflist = initialize_entity_first;
1656 initialize_entity_first = world;
1657 remove = remove_except_protected;
1658 for (self = startoflist; self; self = self.initialize_entity_next)
1660 self.remove_except_protected_forbidden = 1;
1662 for (self = startoflist; self; )
1665 var void(void) func;
1666 e = self.initialize_entity_next;
1667 func = self.initialize_entity;
1668 self.initialize_entity_order = 0;
1669 self.initialize_entity = func_null;
1670 self.initialize_entity_next = world;
1671 self.remove_except_protected_forbidden = 0;
1672 if (self.classname == "initialize_entity")
1676 builtin_remove(self);
1679 //dprint("Delayed initialization: ", self.classname, "\n");
1685 backtrace(strcat("Null function in: ", self.classname, "\n"));
1689 remove = remove_unsafely;
1692 .float uncustomizeentityforclient_set;
1693 .void(void) uncustomizeentityforclient;
1694 void UncustomizeEntitiesRun()
1698 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1699 self.uncustomizeentityforclient();
1702 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1704 e.customizeentityforclient = customizer;
1705 e.uncustomizeentityforclient = uncustomizer;
1706 e.uncustomizeentityforclient_set = !!uncustomizer;
1710 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1713 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1717 if (e.classname == "")
1718 e.classname = "net_linked";
1720 if (e.model == "" || self.modelindex == 0)
1724 setmodel(e, "null");
1728 e.SendEntity = sendfunc;
1729 e.SendFlags = 0xFFFFFF;
1732 e.effects |= EF_NODEPTHTEST;
1736 e.nextthink = time + dt;
1737 e.think = SUB_Remove;
1741 void adaptor_think2touch()
1750 void adaptor_think2use()
1762 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1764 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
1765 self.projectiledeathtype |= HITTYPE_SPLASH;
1766 adaptor_think2use();
1769 // deferred dropping
1770 void DropToFloor_Handler()
1772 builtin_droptofloor();
1773 self.dropped_origin = self.origin;
1778 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1783 float trace_hits_box_a0, trace_hits_box_a1;
1785 float trace_hits_box_1d(float end, float thmi, float thma)
1789 // just check if x is in range
1797 // do the trace with respect to x
1798 // 0 -> end has to stay in thmi -> thma
1799 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1800 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1801 if (trace_hits_box_a0 > trace_hits_box_a1)
1807 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1812 // now it is a trace from 0 to end
1814 trace_hits_box_a0 = 0;
1815 trace_hits_box_a1 = 1;
1817 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1819 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1821 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1827 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1829 return trace_hits_box(start, end, thmi - ma, thma - mi);
1832 float SUB_NoImpactCheck()
1834 // zero hitcontents = this is not the real impact, but either the
1835 // mirror-impact of something hitting the projectile instead of the
1836 // projectile hitting the something, or a touchareagrid one. Neither of
1837 // these stop the projectile from moving, so...
1838 if(trace_dphitcontents == 0)
1840 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1841 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)));
1844 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1846 if (other == world && self.size != '0 0 0')
1849 tic = self.velocity * sys_frametime;
1850 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1851 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1852 if (trace_fraction >= 1)
1854 dprint("Odd... did not hit...?\n");
1856 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1858 dprint("Detected and prevented the sky-grapple bug.\n");
1866 #define SUB_OwnerCheck() (other && (other == self.owner))
1868 void RemoveGrapplingHook(entity pl);
1869 void W_Crylink_Dequeue(entity e);
1870 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1872 if(SUB_OwnerCheck())
1874 if(SUB_NoImpactCheck())
1876 if(self.classname == "grapplinghook")
1877 RemoveGrapplingHook(self.realowner);
1878 else if(self.classname == "spike")
1880 W_Crylink_Dequeue(self);
1887 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1888 UpdateCSQCProjectile(self);
1891 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1893 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1894 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1896 void URI_Get_Callback(float id, float status, string data)
1898 if(url_URI_Get_Callback(id, status, data))
1902 else if (id == URI_GET_DISCARD)
1906 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1909 Curl_URI_Get_Callback(id, status, data);
1911 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1914 OnlineBanList_URI_Get_Callback(id, status, data);
1918 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1922 string uid2name(string myuid) {
1924 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1926 // FIXME remove this later after 0.6 release
1927 // convert old style broken records to correct style
1930 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1933 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1934 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1939 s = "^1Unregistered Player";
1943 float race_readTime(string map, float pos)
1951 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1954 string race_readUID(string map, float pos)
1962 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
1965 float race_readPos(string map, float t) {
1967 for (i = 1; i <= RANKINGS_CNT; ++i)
1968 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
1971 return 0; // pos is zero if unranked
1974 void race_writeTime(string map, float t, string myuid)
1983 newpos = race_readPos(map, t);
1985 float i, prevpos = 0;
1986 for(i = 1; i <= RANKINGS_CNT; ++i)
1988 if(race_readUID(map, i) == myuid)
1991 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
1992 for (i = prevpos; i > newpos; --i) {
1993 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
1994 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
1996 } else { // player has no ranked record yet
1997 for (i = RANKINGS_CNT; i > newpos; --i) {
1998 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
1999 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2003 // store new time itself
2004 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2005 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2008 string race_readName(string map, float pos)
2016 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2019 string race_placeName(float pos) {
2020 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2022 if(mod(pos, 10) == 1)
2023 return strcat(ftos(pos), "st");
2024 else if(mod(pos, 10) == 2)
2025 return strcat(ftos(pos), "nd");
2026 else if(mod(pos, 10) == 3)
2027 return strcat(ftos(pos), "rd");
2029 return strcat(ftos(pos), "th");
2032 return strcat(ftos(pos), "th");
2035 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2038 vector start, org, delta, end, enddown, mstart;
2041 m = e.dphitcontentsmask;
2042 e.dphitcontentsmask = goodcontents | badcontents;
2045 delta = world.maxs - world.mins;
2049 for (i = 0; i < attempts; ++i)
2051 start_x = org_x + random() * delta_x;
2052 start_y = org_y + random() * delta_y;
2053 start_z = org_z + random() * delta_z;
2055 // rule 1: start inside world bounds, and outside
2056 // solid, and don't start from somewhere where you can
2057 // fall down to evil
2058 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2059 if (trace_fraction >= 1)
2061 if (trace_startsolid)
2063 if (trace_dphitcontents & badcontents)
2065 if (trace_dphitq3surfaceflags & badsurfaceflags)
2068 // rule 2: if we are too high, lower the point
2069 if (trace_fraction * delta_z > maxaboveground)
2070 start = trace_endpos + '0 0 1' * maxaboveground;
2071 enddown = trace_endpos;
2073 // rule 3: make sure we aren't outside the map. This only works
2074 // for somewhat well formed maps. A good rule of thumb is that
2075 // the map should have a convex outside hull.
2076 // these can be traceLINES as we already verified the starting box
2077 mstart = start + 0.5 * (e.mins + e.maxs);
2078 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2079 if (trace_fraction >= 1)
2081 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2082 if (trace_fraction >= 1)
2084 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2085 if (trace_fraction >= 1)
2087 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2088 if (trace_fraction >= 1)
2090 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2091 if (trace_fraction >= 1)
2094 // rule 4: we must "see" some spawnpoint
2095 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2096 if(checkpvs(mstart, sp))
2100 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2101 if(checkpvs(mstart, sp))
2107 // find a random vector to "look at"
2108 end_x = org_x + random() * delta_x;
2109 end_y = org_y + random() * delta_y;
2110 end_z = org_z + random() * delta_z;
2111 end = start + normalize(end - start) * vlen(delta);
2113 // rule 4: start TO end must not be too short
2114 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2115 if (trace_startsolid)
2117 if (trace_fraction < minviewdistance / vlen(delta))
2120 // rule 5: don't want to look at sky
2121 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2124 // rule 6: we must not end up in trigger_hurt
2125 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2131 e.dphitcontentsmask = m;
2135 setorigin(e, start);
2136 e.angles = vectoangles(end - start);
2137 dprint("Needed ", ftos(i + 1), " attempts\n");
2144 float zcurveparticles_effectno;
2145 vector zcurveparticles_start;
2146 float zcurveparticles_spd;
2148 void endzcurveparticles()
2150 if(zcurveparticles_effectno)
2153 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2155 zcurveparticles_effectno = 0;
2158 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2160 spd = bound(0, floor(spd / 16), 32767);
2161 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2163 endzcurveparticles();
2164 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2165 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2166 WriteShort(MSG_BROADCAST, effectno);
2167 WriteCoord(MSG_BROADCAST, start_x);
2168 WriteCoord(MSG_BROADCAST, start_y);
2169 WriteCoord(MSG_BROADCAST, start_z);
2170 zcurveparticles_effectno = effectno;
2171 zcurveparticles_start = start;
2174 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2175 WriteCoord(MSG_BROADCAST, end_x);
2176 WriteCoord(MSG_BROADCAST, end_y);
2177 WriteCoord(MSG_BROADCAST, end_z);
2178 WriteCoord(MSG_BROADCAST, end_dz);
2179 zcurveparticles_spd = spd;
2182 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2185 vector vecxy, velxy;
2187 vecxy = end - start;
2192 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2194 endzcurveparticles();
2195 trailparticles(world, effectno, start, end);
2199 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2200 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2203 void write_recordmarker(entity pl, float tstart, float dt)
2205 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2207 // also write a marker into demo files for demotc-race-record-extractor to find
2210 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2211 " ", ftos(tstart), " ", ftos(dt), "\n"));
2214 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2227 if(allowcenter) // 2: allow center handedness
2240 if(allowcenter) // 2: allow center handedness
2256 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2261 if (autocvar_g_shootfromeye)
2265 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2266 else { vecs_y = 0; vecs_z -= 2; }
2274 else if (autocvar_g_shootfromcenter)
2279 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2289 else if (autocvar_g_shootfromclient)
2291 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2296 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2298 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2302 void attach_sameorigin(entity e, entity to, string tag)
2304 vector org, t_forward, t_left, t_up, e_forward, e_up;
2307 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2308 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2309 t_forward = v_forward * tagscale;
2310 t_left = v_right * -tagscale;
2311 t_up = v_up * tagscale;
2313 e.origin_x = org * t_forward;
2314 e.origin_y = org * t_left;
2315 e.origin_z = org * t_up;
2317 // current forward and up directions
2318 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2319 e.angles = AnglesTransform_FromVAngles(e.angles);
2321 e.angles = AnglesTransform_FromAngles(e.angles);
2322 fixedmakevectors(e.angles);
2324 // untransform forward, up!
2325 e_forward_x = v_forward * t_forward;
2326 e_forward_y = v_forward * t_left;
2327 e_forward_z = v_forward * t_up;
2328 e_up_x = v_up * t_forward;
2329 e_up_y = v_up * t_left;
2330 e_up_z = v_up * t_up;
2332 e.angles = fixedvectoangles2(e_forward, e_up);
2333 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2334 e.angles = AnglesTransform_ToVAngles(e.angles);
2336 e.angles = AnglesTransform_ToAngles(e.angles);
2338 setattachment(e, to, tag);
2339 setorigin(e, e.origin);
2342 void detach_sameorigin(entity e)
2345 org = gettaginfo(e, 0);
2346 e.angles = fixedvectoangles2(v_forward, v_up);
2347 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2348 e.angles = AnglesTransform_ToVAngles(e.angles);
2350 e.angles = AnglesTransform_ToAngles(e.angles);
2352 setattachment(e, world, "");
2353 setorigin(e, e.origin);
2356 void follow_sameorigin(entity e, entity to)
2358 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2359 e.aiment = to; // make the hole follow bmodel
2360 e.punchangle = to.angles; // the original angles of bmodel
2361 e.view_ofs = e.origin - to.origin; // relative origin
2362 e.v_angle = e.angles - to.angles; // relative angles
2365 void unfollow_sameorigin(entity e)
2367 e.movetype = MOVETYPE_NONE;
2370 entity gettaginfo_relative_ent;
2371 vector gettaginfo_relative(entity e, float tag)
2373 if (!gettaginfo_relative_ent)
2375 gettaginfo_relative_ent = spawn();
2376 gettaginfo_relative_ent.effects = EF_NODRAW;
2378 gettaginfo_relative_ent.model = e.model;
2379 gettaginfo_relative_ent.modelindex = e.modelindex;
2380 gettaginfo_relative_ent.frame = e.frame;
2381 return gettaginfo(gettaginfo_relative_ent, tag);
2386 float modeleffect_SendEntity(entity to, float sf)
2389 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2392 if(self.velocity != '0 0 0')
2394 if(self.angles != '0 0 0')
2396 if(self.avelocity != '0 0 0')
2399 WriteByte(MSG_ENTITY, f);
2400 WriteShort(MSG_ENTITY, self.modelindex);
2401 WriteByte(MSG_ENTITY, self.skin);
2402 WriteByte(MSG_ENTITY, self.frame);
2403 WriteCoord(MSG_ENTITY, self.origin_x);
2404 WriteCoord(MSG_ENTITY, self.origin_y);
2405 WriteCoord(MSG_ENTITY, self.origin_z);
2408 WriteCoord(MSG_ENTITY, self.velocity_x);
2409 WriteCoord(MSG_ENTITY, self.velocity_y);
2410 WriteCoord(MSG_ENTITY, self.velocity_z);
2414 WriteCoord(MSG_ENTITY, self.angles_x);
2415 WriteCoord(MSG_ENTITY, self.angles_y);
2416 WriteCoord(MSG_ENTITY, self.angles_z);
2420 WriteCoord(MSG_ENTITY, self.avelocity_x);
2421 WriteCoord(MSG_ENTITY, self.avelocity_y);
2422 WriteCoord(MSG_ENTITY, self.avelocity_z);
2424 WriteShort(MSG_ENTITY, self.scale * 256.0);
2425 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2426 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2427 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2428 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2433 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)
2438 e.classname = "modeleffect";
2446 e.teleport_time = t1;
2450 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2454 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2457 sz = max(e.scale, e.scale2);
2458 setsize(e, e.mins * sz, e.maxs * sz);
2459 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2462 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2464 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2467 float randombit(float bits)
2469 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2478 for(f = 1; f <= bits; f *= 2)
2487 r = (r - 1) / (n - 1);
2494 float randombits(float bits, float k, float error_return)
2498 while(k > 0 && bits != r)
2500 r += randombit(bits - r);
2509 void randombit_test(float bits, float iter)
2513 print(ftos(randombit(bits)), "\n");
2518 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2520 if(halflifedist > 0)
2521 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2522 else if(halflifedist < 0)
2523 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2532 #define cvar_string_normal builtin_cvar_string
2533 #define cvar_normal builtin_cvar
2535 string cvar_string_normal(string n)
2537 if not(cvar_type(n) & 1)
2538 backtrace(strcat("Attempt to access undefined cvar: ", n));
2539 return builtin_cvar_string(n);
2542 float cvar_normal(string n)
2544 return stof(cvar_string_normal(n));
2547 #define cvar_set_normal builtin_cvar_set
2555 oself.think = SUB_Remove;
2556 oself.nextthink = time;
2562 Execute func() after time + fdelay.
2563 self when func is executed = self when defer is called
2565 void defer(float fdelay, void() func)
2572 e.think = defer_think;
2573 e.nextthink = time + fdelay;
2576 .string aiment_classname;
2577 .float aiment_deadflag;
2578 void SetMovetypeFollow(entity ent, entity e)
2580 // FIXME this may not be warpzone aware
2581 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2582 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.
2583 ent.aiment = e; // make the hole follow bmodel
2584 ent.punchangle = e.angles; // the original angles of bmodel
2585 ent.view_ofs = ent.origin - e.origin; // relative origin
2586 ent.v_angle = ent.angles - e.angles; // relative angles
2587 ent.aiment_classname = strzone(e.classname);
2588 ent.aiment_deadflag = e.deadflag;
2590 void UnsetMovetypeFollow(entity ent)
2592 ent.movetype = MOVETYPE_FLY;
2593 PROJECTILE_MAKETRIGGER(ent);
2596 float LostMovetypeFollow(entity ent)
2599 if(ent.movetype != MOVETYPE_FOLLOW)
2605 if(ent.aiment.classname != ent.aiment_classname)
2607 if(ent.aiment.deadflag != ent.aiment_deadflag)
2613 float isPushable(entity e)
2622 case "droppedweapon":
2623 case "keepawayball":
2624 case "nexball_basketball":
2625 case "nexball_football":
2627 case "bullet": // antilagged bullets can't hit this either
2630 if (e.projectiledeathtype)