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_pinata = 0; // incompatible
828 g_weapon_stay = 0; // incompatible
829 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
831 start_items |= IT_UNLIMITED_AMMO;
835 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
837 e = get_weaponinfo(i);
838 float w = want_weapon("g_start_weapon_", e, FALSE);
840 WEPSET_OR_AW(start_weapons, i);
842 WEPSET_OR_AW(start_weapons_default, i);
844 WEPSET_OR_AW(start_weapons_defaultmask, i);
848 if(!cvar("g_use_ammunition"))
849 start_items |= IT_UNLIMITED_AMMO;
851 if(cvar("g_nexball"))
852 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
854 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
856 start_ammo_rockets = 999;
857 start_ammo_shells = 999;
858 start_ammo_cells = 999;
859 start_ammo_nails = 999;
860 start_ammo_fuel = 999;
866 start_ammo_shells = cvar("g_lms_start_ammo_shells");
867 start_ammo_nails = cvar("g_lms_start_ammo_nails");
868 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
869 start_ammo_cells = cvar("g_lms_start_ammo_cells");
870 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
874 start_ammo_shells = cvar("g_start_ammo_shells");
875 start_ammo_nails = cvar("g_start_ammo_nails");
876 start_ammo_rockets = cvar("g_start_ammo_rockets");
877 start_ammo_cells = cvar("g_start_ammo_cells");
878 start_ammo_fuel = cvar("g_start_ammo_fuel");
884 start_health = cvar("g_lms_start_health");
885 start_armorvalue = cvar("g_lms_start_armor");
890 warmup_start_ammo_shells = start_ammo_shells;
891 warmup_start_ammo_nails = start_ammo_nails;
892 warmup_start_ammo_rockets = start_ammo_rockets;
893 warmup_start_ammo_cells = start_ammo_cells;
894 warmup_start_ammo_fuel = start_ammo_fuel;
895 warmup_start_health = start_health;
896 warmup_start_armorvalue = start_armorvalue;
897 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
898 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
899 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
901 if (!g_weaponarena && !g_minstagib && !g_ca)
903 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
904 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
905 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
906 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
907 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
908 warmup_start_health = cvar("g_warmup_start_health");
909 warmup_start_armorvalue = cvar("g_warmup_start_armor");
910 WEPSET_CLEAR_A(warmup_start_weapons);
911 WEPSET_CLEAR_A(warmup_start_weapons_default);
912 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
913 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
915 e = get_weaponinfo(i);
916 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
918 WEPSET_OR_AW(warmup_start_weapons, i);
920 WEPSET_OR_AW(warmup_start_weapons_default, i);
922 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
928 start_items |= IT_JETPACK;
930 MUTATOR_CALLHOOK(SetStartItems);
932 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
934 g_grappling_hook = 0; // these two can't coexist, as they use the same button
935 start_items |= IT_FUEL_REGEN;
936 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
937 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
940 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
942 e = get_weaponinfo(i);
943 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
944 weapon_action(i, WR_PRECACHE);
947 start_ammo_shells = max(0, start_ammo_shells);
948 start_ammo_nails = max(0, start_ammo_nails);
949 start_ammo_cells = max(0, start_ammo_cells);
950 start_ammo_rockets = max(0, start_ammo_rockets);
951 start_ammo_fuel = max(0, start_ammo_fuel);
953 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
954 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
955 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
956 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
957 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
961 float g_bugrigs_planar_movement;
962 float g_bugrigs_planar_movement_car_jumping;
963 float g_bugrigs_reverse_spinning;
964 float g_bugrigs_reverse_speeding;
965 float g_bugrigs_reverse_stopping;
966 float g_bugrigs_air_steering;
967 float g_bugrigs_angle_smoothing;
968 float g_bugrigs_friction_floor;
969 float g_bugrigs_friction_brake;
970 float g_bugrigs_friction_air;
971 float g_bugrigs_accel;
972 float g_bugrigs_speed_ref;
973 float g_bugrigs_speed_pow;
974 float g_bugrigs_steer;
976 float g_touchexplode;
977 float g_touchexplode_radius;
978 float g_touchexplode_damage;
979 float g_touchexplode_edgedamage;
980 float g_touchexplode_force;
985 string GetGametype(); // g_world.qc
986 void readlevelcvars(void)
988 // load ALL the mutators
989 if(cvar("g_dodging"))
990 MUTATOR_ADD(mutator_dodging);
991 if(cvar("g_spawn_near_teammate"))
992 MUTATOR_ADD(mutator_spawn_near_teammate);
993 if(cvar("g_physical_items"))
994 MUTATOR_ADD(mutator_physical_items);
995 if(cvar("g_minstagib"))
996 MUTATOR_ADD(mutator_minstagib);
997 if(cvar("g_invincible_projectiles"))
998 MUTATOR_ADD(mutator_invincibleprojectiles);
999 if(cvar("g_new_toys"))
1000 MUTATOR_ADD(mutator_new_toys);
1001 if(!cvar("g_minstagib")) // TODO: nix support?
1003 MUTATOR_ADD(mutator_nix);
1004 if(cvar("g_rocket_flying"))
1005 MUTATOR_ADD(mutator_rocketflying);
1006 if(cvar("g_vampire"))
1007 MUTATOR_ADD(mutator_vampire);
1008 if(cvar("g_superspectate"))
1009 MUTATOR_ADD(mutator_superspec);
1011 // is this a mutator? is this a mode?
1012 if(cvar("g_sandbox"))
1013 MUTATOR_ADD(sandbox);
1015 if(cvar("sv_allow_fullbright"))
1016 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1018 g_bugrigs = cvar("g_bugrigs");
1019 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1020 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1021 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1022 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1023 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1024 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1025 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1026 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1027 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1028 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1029 g_bugrigs_accel = cvar("g_bugrigs_accel");
1030 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1031 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1032 g_bugrigs_steer = cvar("g_bugrigs_steer");
1034 g_minstagib = cvar("g_minstagib");
1036 g_touchexplode = cvar("g_touchexplode");
1037 g_touchexplode_radius = cvar("g_touchexplode_radius");
1038 g_touchexplode_damage = cvar("g_touchexplode_damage");
1039 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1040 g_touchexplode_force = cvar("g_touchexplode_force");
1042 sv_clones = cvar("sv_clones");
1043 sv_foginterval = cvar("sv_foginterval");
1044 g_cloaked = cvar("g_cloaked");
1046 g_cloaked = 1; // always enable cloak in CTS
1047 g_jump_grunt = cvar("g_jump_grunt");
1048 g_footsteps = cvar("g_footsteps");
1049 g_grappling_hook = cvar("g_grappling_hook");
1050 g_jetpack = cvar("g_jetpack");
1051 g_midair = cvar("g_midair");
1052 g_norecoil = cvar("g_norecoil");
1053 g_bloodloss = cvar("g_bloodloss");
1054 sv_maxidle = cvar("sv_maxidle");
1055 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1056 sv_autotaunt = cvar("sv_autotaunt");
1057 sv_taunt = cvar("sv_taunt");
1059 inWarmupStage = cvar("g_warmup");
1060 g_warmup_limit = cvar("g_warmup_limit");
1061 g_warmup_allguns = cvar("g_warmup_allguns");
1062 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1064 if ((g_race && g_race_qualifying == 2) || g_arena || g_minstagib || g_assault || cvar("g_campaign"))
1065 inWarmupStage = 0; // these modes cannot work together, sorry
1067 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1068 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1069 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1070 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1071 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1072 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1073 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1074 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1075 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1076 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1077 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1078 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1079 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1080 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1082 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1083 g_weaponratefactor = cvar("g_weaponratefactor");
1084 g_weapondamagefactor = cvar("g_weapondamagefactor");
1085 g_weaponforcefactor = cvar("g_weaponforcefactor");
1086 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1088 g_pickup_shells = cvar("g_pickup_shells");
1089 g_pickup_shells_max = cvar("g_pickup_shells_max");
1090 g_pickup_nails = cvar("g_pickup_nails");
1091 g_pickup_nails_max = cvar("g_pickup_nails_max");
1092 g_pickup_rockets = cvar("g_pickup_rockets");
1093 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1094 g_pickup_cells = cvar("g_pickup_cells");
1095 g_pickup_cells_max = cvar("g_pickup_cells_max");
1096 g_pickup_fuel = cvar("g_pickup_fuel");
1097 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1098 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1099 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1100 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1101 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1102 g_pickup_armormedium = cvar("g_pickup_armormedium");
1103 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1104 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1105 g_pickup_armorbig = cvar("g_pickup_armorbig");
1106 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1107 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1108 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1109 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1110 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1111 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1112 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1113 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1114 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1115 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1116 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1117 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1118 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1119 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1120 g_pickup_healthmega = cvar("g_pickup_healthmega");
1121 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1122 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1124 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1125 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1127 g_pinata = cvar("g_pinata");
1129 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1131 g_weapon_stay = cvar("g_weapon_stay");
1133 if not(inWarmupStage && !g_ca)
1134 game_starttime = cvar("g_start_delay");
1136 readplayerstartcvars();
1142 string precache_sound (string s) = #19;
1143 float precache_sound_index (string s) = #19;
1145 #define SND_VOLUME 1
1146 #define SND_ATTENUATION 2
1147 #define SND_LARGEENTITY 8
1148 #define SND_LARGESOUND 16
1150 float sound_allowed(float dest, entity e)
1152 // sounds from world may always pass
1155 if (e.classname == "body")
1157 else if (e.realowner && e.realowner != e)
1159 else if (e.owner && e.owner != e)
1164 // sounds to self may always pass
1165 if (dest == MSG_ONE)
1166 if (e == msg_entity)
1168 // sounds by players can be removed
1169 if (autocvar_bot_sound_monopoly)
1170 if (clienttype(e) == CLIENTTYPE_REAL)
1172 // anything else may pass
1176 #ifdef COMPAT_XON010_CHANNELS
1177 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1178 void sound(entity e, float chan, string samp, float vol, float atten)
1180 if (!sound_allowed(MSG_BROADCAST, e))
1182 builtin_sound(e, chan, samp, vol, atten);
1186 void sound(entity e, float chan, string samp, float vol, float atten)
1188 if (!sound_allowed(MSG_BROADCAST, e))
1190 sound7(e, chan, samp, vol, atten, 0, 0);
1194 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1198 if (!sound_allowed(dest, e))
1201 entno = num_for_edict(e);
1202 idx = precache_sound_index(samp);
1207 atten = floor(atten * 64);
1208 vol = floor(vol * 255);
1211 sflags |= SND_VOLUME;
1213 sflags |= SND_ATTENUATION;
1214 if (entno >= 8192 || chan < 0 || chan > 7)
1215 sflags |= SND_LARGEENTITY;
1217 sflags |= SND_LARGESOUND;
1219 WriteByte(dest, SVC_SOUND);
1220 WriteByte(dest, sflags);
1221 if (sflags & SND_VOLUME)
1222 WriteByte(dest, vol);
1223 if (sflags & SND_ATTENUATION)
1224 WriteByte(dest, atten);
1225 if (sflags & SND_LARGEENTITY)
1227 WriteShort(dest, entno);
1228 WriteByte(dest, chan);
1232 WriteShort(dest, entno * 8 + chan);
1234 if (sflags & SND_LARGESOUND)
1235 WriteShort(dest, idx);
1237 WriteByte(dest, idx);
1239 WriteCoord(dest, o_x);
1240 WriteCoord(dest, o_y);
1241 WriteCoord(dest, o_z);
1243 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1247 if (!sound_allowed(dest, e))
1250 o = e.origin + 0.5 * (e.mins + e.maxs);
1251 soundtoat(dest, e, o, chan, samp, vol, atten);
1253 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1255 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1257 void stopsoundto(float dest, entity e, float chan)
1261 if (!sound_allowed(dest, e))
1264 entno = num_for_edict(e);
1266 if (entno >= 8192 || chan < 0 || chan > 7)
1269 idx = precache_sound_index("misc/null.wav");
1270 sflags = SND_LARGEENTITY;
1272 sflags |= SND_LARGESOUND;
1273 WriteByte(dest, SVC_SOUND);
1274 WriteByte(dest, sflags);
1275 WriteShort(dest, entno);
1276 WriteByte(dest, chan);
1277 if (sflags & SND_LARGESOUND)
1278 WriteShort(dest, idx);
1280 WriteByte(dest, idx);
1281 WriteCoord(dest, e.origin_x);
1282 WriteCoord(dest, e.origin_y);
1283 WriteCoord(dest, e.origin_z);
1287 WriteByte(dest, SVC_STOPSOUND);
1288 WriteShort(dest, entno * 8 + chan);
1291 void stopsound(entity e, float chan)
1293 if (!sound_allowed(MSG_BROADCAST, e))
1296 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1297 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1300 void play2(entity e, string filename)
1302 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1304 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1307 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1309 float spamsound(entity e, float chan, string samp, float vol, float atten)
1311 if (!sound_allowed(MSG_BROADCAST, e))
1314 if (time > e.spamtime)
1317 sound(e, chan, samp, vol, atten);
1323 void play2team(float t, string filename)
1327 if (autocvar_bot_sound_monopoly)
1330 FOR_EACH_REALPLAYER(head)
1333 play2(head, filename);
1337 void play2all(string samp)
1339 if (autocvar_bot_sound_monopoly)
1342 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1345 void PrecachePlayerSounds(string f);
1346 void precache_playermodel(string m)
1348 float globhandle, i, n;
1351 if(substring(m, -9,5) == "_lod1")
1353 if(substring(m, -9,5) == "_lod2")
1356 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1359 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1363 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1366 n = search_getsize(globhandle);
1367 for (i = 0; i < n; ++i)
1369 //print(search_getfilename(globhandle, i), "\n");
1370 f = search_getfilename(globhandle, i);
1371 PrecachePlayerSounds(f);
1373 search_end(globhandle);
1375 void precache_all_playermodels(string pattern)
1377 float globhandle, i, n;
1380 globhandle = search_begin(pattern, TRUE, FALSE);
1383 n = search_getsize(globhandle);
1384 for (i = 0; i < n; ++i)
1386 //print(search_getfilename(globhandle, i), "\n");
1387 f = search_getfilename(globhandle, i);
1388 precache_playermodel(f);
1390 search_end(globhandle);
1395 // gamemode related things
1396 precache_model ("models/misc/chatbubble.spr");
1398 #ifdef TTURRETS_ENABLED
1399 if (autocvar_g_turrets)
1403 // Precache all player models if desired
1404 if (autocvar_sv_precacheplayermodels)
1406 PrecachePlayerSounds("sound/player/default.sounds");
1407 precache_all_playermodels("models/player/*.zym");
1408 precache_all_playermodels("models/player/*.dpm");
1409 precache_all_playermodels("models/player/*.md3");
1410 precache_all_playermodels("models/player/*.psk");
1411 precache_all_playermodels("models/player/*.iqm");
1414 if (autocvar_sv_defaultcharacter)
1417 s = autocvar_sv_defaultplayermodel_red;
1419 precache_playermodel(s);
1420 s = autocvar_sv_defaultplayermodel_blue;
1422 precache_playermodel(s);
1423 s = autocvar_sv_defaultplayermodel_yellow;
1425 precache_playermodel(s);
1426 s = autocvar_sv_defaultplayermodel_pink;
1428 precache_playermodel(s);
1429 s = autocvar_sv_defaultplayermodel;
1431 precache_playermodel(s);
1436 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1437 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1440 // gore and miscellaneous sounds
1441 //precache_sound ("misc/h2ohit.wav");
1442 precache_model ("models/hook.md3");
1443 precache_sound ("misc/armorimpact.wav");
1444 precache_sound ("misc/bodyimpact1.wav");
1445 precache_sound ("misc/bodyimpact2.wav");
1446 precache_sound ("misc/gib.wav");
1447 precache_sound ("misc/gib_splat01.wav");
1448 precache_sound ("misc/gib_splat02.wav");
1449 precache_sound ("misc/gib_splat03.wav");
1450 precache_sound ("misc/gib_splat04.wav");
1451 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1452 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1453 precache_sound ("misc/null.wav");
1454 precache_sound ("misc/spawn.wav");
1455 precache_sound ("misc/talk.wav");
1456 precache_sound ("misc/teleport.wav");
1457 precache_sound ("misc/poweroff.wav");
1458 precache_sound ("player/lava.wav");
1459 precache_sound ("player/slime.wav");
1461 precache_model ("models/sprites/0.spr32");
1462 precache_model ("models/sprites/1.spr32");
1463 precache_model ("models/sprites/2.spr32");
1464 precache_model ("models/sprites/3.spr32");
1465 precache_model ("models/sprites/4.spr32");
1466 precache_model ("models/sprites/5.spr32");
1467 precache_model ("models/sprites/6.spr32");
1468 precache_model ("models/sprites/7.spr32");
1469 precache_model ("models/sprites/8.spr32");
1470 precache_model ("models/sprites/9.spr32");
1471 precache_model ("models/sprites/10.spr32");
1473 // common weapon precaches
1474 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1475 precache_sound ("weapons/weapon_switch.wav");
1476 precache_sound ("weapons/weaponpickup.wav");
1477 precache_sound ("weapons/unavailable.wav");
1478 precache_sound ("weapons/dryfire.wav");
1479 if (g_grappling_hook)
1481 precache_sound ("weapons/hook_fire.wav"); // hook
1482 precache_sound ("weapons/hook_impact.wav"); // hook
1485 if(autocvar_sv_precacheweapons)
1487 //precache weapon models/sounds
1490 while (wep <= WEP_LAST)
1492 weapon_action(wep, WR_PRECACHE);
1497 precache_model("models/elaser.mdl");
1498 precache_model("models/laser.mdl");
1499 precache_model("models/ebomb.mdl");
1502 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1504 if (!self.noise && self.music) // quake 3 uses the music field
1505 self.noise = self.music;
1507 // plays music for the level if there is any
1510 precache_sound (self.noise);
1511 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1516 // WARNING: this kills the trace globals
1517 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1518 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1520 #define INITPRIO_FIRST 0
1521 #define INITPRIO_GAMETYPE 0
1522 #define INITPRIO_GAMETYPE_FALLBACK 1
1523 #define INITPRIO_FINDTARGET 10
1524 #define INITPRIO_DROPTOFLOOR 20
1525 #define INITPRIO_SETLOCATION 90
1526 #define INITPRIO_LINKDOORS 91
1527 #define INITPRIO_LAST 99
1529 .void(void) initialize_entity;
1530 .float initialize_entity_order;
1531 .entity initialize_entity_next;
1532 entity initialize_entity_first;
1534 void make_safe_for_remove(entity e)
1536 if (e.initialize_entity)
1538 entity ent, prev = world;
1539 for (ent = initialize_entity_first; ent; )
1541 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1543 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1544 // skip it in linked list
1547 prev.initialize_entity_next = ent.initialize_entity_next;
1548 ent = prev.initialize_entity_next;
1552 initialize_entity_first = ent.initialize_entity_next;
1553 ent = initialize_entity_first;
1559 ent = ent.initialize_entity_next;
1565 void objerror(string s)
1567 make_safe_for_remove(self);
1568 builtin_objerror(s);
1571 .float remove_except_protected_forbidden;
1572 void remove_except_protected(entity e)
1574 if(e.remove_except_protected_forbidden)
1575 error("not allowed to remove this at this point");
1579 void remove_unsafely(entity e)
1581 if(e.classname == "spike")
1582 error("Removing spikes is forbidden (crylink bug), please report");
1586 void remove_safely(entity e)
1588 make_safe_for_remove(e);
1592 void InitializeEntity(entity e, void(void) func, float order)
1596 if (!e || e.initialize_entity)
1598 // make a proxy initializer entity
1602 e.classname = "initialize_entity";
1606 e.initialize_entity = func;
1607 e.initialize_entity_order = order;
1609 cur = initialize_entity_first;
1613 if (!cur || cur.initialize_entity_order > order)
1615 // insert between prev and cur
1617 prev.initialize_entity_next = e;
1619 initialize_entity_first = e;
1620 e.initialize_entity_next = cur;
1624 cur = cur.initialize_entity_next;
1627 void InitializeEntitiesRun()
1630 startoflist = initialize_entity_first;
1631 initialize_entity_first = world;
1632 remove = remove_except_protected;
1633 for (self = startoflist; self; self = self.initialize_entity_next)
1635 self.remove_except_protected_forbidden = 1;
1637 for (self = startoflist; self; )
1640 var void(void) func;
1641 e = self.initialize_entity_next;
1642 func = self.initialize_entity;
1643 self.initialize_entity_order = 0;
1644 self.initialize_entity = func_null;
1645 self.initialize_entity_next = world;
1646 self.remove_except_protected_forbidden = 0;
1647 if (self.classname == "initialize_entity")
1651 builtin_remove(self);
1654 //dprint("Delayed initialization: ", self.classname, "\n");
1660 backtrace(strcat("Null function in: ", self.classname, "\n"));
1664 remove = remove_unsafely;
1667 .float uncustomizeentityforclient_set;
1668 .void(void) uncustomizeentityforclient;
1669 void UncustomizeEntitiesRun()
1673 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1674 self.uncustomizeentityforclient();
1677 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1679 e.customizeentityforclient = customizer;
1680 e.uncustomizeentityforclient = uncustomizer;
1681 e.uncustomizeentityforclient_set = !!uncustomizer;
1685 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1688 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1692 if (e.classname == "")
1693 e.classname = "net_linked";
1695 if (e.model == "" || self.modelindex == 0)
1699 setmodel(e, "null");
1703 e.SendEntity = sendfunc;
1704 e.SendFlags = 0xFFFFFF;
1707 e.effects |= EF_NODEPTHTEST;
1711 e.nextthink = time + dt;
1712 e.think = SUB_Remove;
1716 void adaptor_think2touch()
1725 void adaptor_think2use()
1737 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1739 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
1740 self.projectiledeathtype |= HITTYPE_SPLASH;
1741 adaptor_think2use();
1744 // deferred dropping
1745 void DropToFloor_Handler()
1747 builtin_droptofloor();
1748 self.dropped_origin = self.origin;
1753 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1758 float trace_hits_box_a0, trace_hits_box_a1;
1760 float trace_hits_box_1d(float end, float thmi, float thma)
1764 // just check if x is in range
1772 // do the trace with respect to x
1773 // 0 -> end has to stay in thmi -> thma
1774 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1775 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1776 if (trace_hits_box_a0 > trace_hits_box_a1)
1782 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1787 // now it is a trace from 0 to end
1789 trace_hits_box_a0 = 0;
1790 trace_hits_box_a1 = 1;
1792 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1794 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1796 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1802 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1804 return trace_hits_box(start, end, thmi - ma, thma - mi);
1807 float SUB_NoImpactCheck()
1809 // zero hitcontents = this is not the real impact, but either the
1810 // mirror-impact of something hitting the projectile instead of the
1811 // projectile hitting the something, or a touchareagrid one. Neither of
1812 // these stop the projectile from moving, so...
1813 if(trace_dphitcontents == 0)
1815 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1816 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)));
1819 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1821 if (other == world && self.size != '0 0 0')
1824 tic = self.velocity * sys_frametime;
1825 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1826 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1827 if (trace_fraction >= 1)
1829 dprint("Odd... did not hit...?\n");
1831 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1833 dprint("Detected and prevented the sky-grapple bug.\n");
1841 #define SUB_OwnerCheck() (other && (other == self.owner))
1843 void RemoveGrapplingHook(entity pl);
1844 void W_Crylink_Dequeue(entity e);
1845 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1847 if(SUB_OwnerCheck())
1849 if(SUB_NoImpactCheck())
1851 if(self.classname == "grapplinghook")
1852 RemoveGrapplingHook(self.realowner);
1853 else if(self.classname == "spike")
1855 W_Crylink_Dequeue(self);
1862 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1863 UpdateCSQCProjectile(self);
1866 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1868 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1869 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1871 void URI_Get_Callback(float id, float status, string data)
1873 if(url_URI_Get_Callback(id, status, data))
1877 else if (id == URI_GET_DISCARD)
1881 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1884 Curl_URI_Get_Callback(id, status, data);
1886 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1889 OnlineBanList_URI_Get_Callback(id, status, data);
1893 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1897 string uid2name(string myuid) {
1899 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1901 // FIXME remove this later after 0.6 release
1902 // convert old style broken records to correct style
1905 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1908 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1909 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1914 s = "^1Unregistered Player";
1918 float race_readTime(string map, float pos)
1926 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1929 string race_readUID(string map, float pos)
1937 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
1940 float race_readPos(string map, float t) {
1942 for (i = 1; i <= RANKINGS_CNT; ++i)
1943 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
1946 return 0; // pos is zero if unranked
1949 void race_writeTime(string map, float t, string myuid)
1958 newpos = race_readPos(map, t);
1960 float i, prevpos = 0;
1961 for(i = 1; i <= RANKINGS_CNT; ++i)
1963 if(race_readUID(map, i) == myuid)
1966 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
1967 for (i = prevpos; i > newpos; --i) {
1968 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
1969 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
1971 } else { // player has no ranked record yet
1972 for (i = RANKINGS_CNT; i > newpos; --i) {
1973 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
1974 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
1978 // store new time itself
1979 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
1980 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
1983 string race_readName(string map, float pos)
1991 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
1994 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1997 vector start, org, delta, end, enddown, mstart;
2000 m = e.dphitcontentsmask;
2001 e.dphitcontentsmask = goodcontents | badcontents;
2004 delta = world.maxs - world.mins;
2008 for (i = 0; i < attempts; ++i)
2010 start_x = org_x + random() * delta_x;
2011 start_y = org_y + random() * delta_y;
2012 start_z = org_z + random() * delta_z;
2014 // rule 1: start inside world bounds, and outside
2015 // solid, and don't start from somewhere where you can
2016 // fall down to evil
2017 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2018 if (trace_fraction >= 1)
2020 if (trace_startsolid)
2022 if (trace_dphitcontents & badcontents)
2024 if (trace_dphitq3surfaceflags & badsurfaceflags)
2027 // rule 2: if we are too high, lower the point
2028 if (trace_fraction * delta_z > maxaboveground)
2029 start = trace_endpos + '0 0 1' * maxaboveground;
2030 enddown = trace_endpos;
2032 // rule 3: make sure we aren't outside the map. This only works
2033 // for somewhat well formed maps. A good rule of thumb is that
2034 // the map should have a convex outside hull.
2035 // these can be traceLINES as we already verified the starting box
2036 mstart = start + 0.5 * (e.mins + e.maxs);
2037 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2038 if (trace_fraction >= 1)
2040 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2041 if (trace_fraction >= 1)
2043 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2044 if (trace_fraction >= 1)
2046 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2047 if (trace_fraction >= 1)
2049 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2050 if (trace_fraction >= 1)
2053 // rule 4: we must "see" some spawnpoint
2054 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2055 if(checkpvs(mstart, sp))
2059 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2060 if(checkpvs(mstart, sp))
2066 // find a random vector to "look at"
2067 end_x = org_x + random() * delta_x;
2068 end_y = org_y + random() * delta_y;
2069 end_z = org_z + random() * delta_z;
2070 end = start + normalize(end - start) * vlen(delta);
2072 // rule 4: start TO end must not be too short
2073 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2074 if (trace_startsolid)
2076 if (trace_fraction < minviewdistance / vlen(delta))
2079 // rule 5: don't want to look at sky
2080 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2083 // rule 6: we must not end up in trigger_hurt
2084 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2090 e.dphitcontentsmask = m;
2094 setorigin(e, start);
2095 e.angles = vectoangles(end - start);
2096 dprint("Needed ", ftos(i + 1), " attempts\n");
2103 float zcurveparticles_effectno;
2104 vector zcurveparticles_start;
2105 float zcurveparticles_spd;
2107 void endzcurveparticles()
2109 if(zcurveparticles_effectno)
2112 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2114 zcurveparticles_effectno = 0;
2117 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2119 spd = bound(0, floor(spd / 16), 32767);
2120 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2122 endzcurveparticles();
2123 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2124 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2125 WriteShort(MSG_BROADCAST, effectno);
2126 WriteCoord(MSG_BROADCAST, start_x);
2127 WriteCoord(MSG_BROADCAST, start_y);
2128 WriteCoord(MSG_BROADCAST, start_z);
2129 zcurveparticles_effectno = effectno;
2130 zcurveparticles_start = start;
2133 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2134 WriteCoord(MSG_BROADCAST, end_x);
2135 WriteCoord(MSG_BROADCAST, end_y);
2136 WriteCoord(MSG_BROADCAST, end_z);
2137 WriteCoord(MSG_BROADCAST, end_dz);
2138 zcurveparticles_spd = spd;
2141 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2144 vector vecxy, velxy;
2146 vecxy = end - start;
2151 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2153 endzcurveparticles();
2154 trailparticles(world, effectno, start, end);
2158 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2159 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2162 void write_recordmarker(entity pl, float tstart, float dt)
2164 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2166 // also write a marker into demo files for demotc-race-record-extractor to find
2169 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2170 " ", ftos(tstart), " ", ftos(dt), "\n"));
2173 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2186 if(allowcenter) // 2: allow center handedness
2199 if(allowcenter) // 2: allow center handedness
2215 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2220 if (autocvar_g_shootfromeye)
2224 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2225 else { vecs_y = 0; vecs_z -= 2; }
2233 else if (autocvar_g_shootfromcenter)
2238 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2248 else if (autocvar_g_shootfromclient)
2250 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2255 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2257 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2261 void attach_sameorigin(entity e, entity to, string tag)
2263 vector org, t_forward, t_left, t_up, e_forward, e_up;
2266 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2267 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2268 t_forward = v_forward * tagscale;
2269 t_left = v_right * -tagscale;
2270 t_up = v_up * tagscale;
2272 e.origin_x = org * t_forward;
2273 e.origin_y = org * t_left;
2274 e.origin_z = org * t_up;
2276 // current forward and up directions
2277 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2278 e.angles = AnglesTransform_FromVAngles(e.angles);
2280 e.angles = AnglesTransform_FromAngles(e.angles);
2281 fixedmakevectors(e.angles);
2283 // untransform forward, up!
2284 e_forward_x = v_forward * t_forward;
2285 e_forward_y = v_forward * t_left;
2286 e_forward_z = v_forward * t_up;
2287 e_up_x = v_up * t_forward;
2288 e_up_y = v_up * t_left;
2289 e_up_z = v_up * t_up;
2291 e.angles = fixedvectoangles2(e_forward, e_up);
2292 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2293 e.angles = AnglesTransform_ToVAngles(e.angles);
2295 e.angles = AnglesTransform_ToAngles(e.angles);
2297 setattachment(e, to, tag);
2298 setorigin(e, e.origin);
2301 void detach_sameorigin(entity e)
2304 org = gettaginfo(e, 0);
2305 e.angles = fixedvectoangles2(v_forward, v_up);
2306 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2307 e.angles = AnglesTransform_ToVAngles(e.angles);
2309 e.angles = AnglesTransform_ToAngles(e.angles);
2311 setattachment(e, world, "");
2312 setorigin(e, e.origin);
2315 void follow_sameorigin(entity e, entity to)
2317 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2318 e.aiment = to; // make the hole follow bmodel
2319 e.punchangle = to.angles; // the original angles of bmodel
2320 e.view_ofs = e.origin - to.origin; // relative origin
2321 e.v_angle = e.angles - to.angles; // relative angles
2324 void unfollow_sameorigin(entity e)
2326 e.movetype = MOVETYPE_NONE;
2329 entity gettaginfo_relative_ent;
2330 vector gettaginfo_relative(entity e, float tag)
2332 if (!gettaginfo_relative_ent)
2334 gettaginfo_relative_ent = spawn();
2335 gettaginfo_relative_ent.effects = EF_NODRAW;
2337 gettaginfo_relative_ent.model = e.model;
2338 gettaginfo_relative_ent.modelindex = e.modelindex;
2339 gettaginfo_relative_ent.frame = e.frame;
2340 return gettaginfo(gettaginfo_relative_ent, tag);
2345 float modeleffect_SendEntity(entity to, float sf)
2348 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2351 if(self.velocity != '0 0 0')
2353 if(self.angles != '0 0 0')
2355 if(self.avelocity != '0 0 0')
2358 WriteByte(MSG_ENTITY, f);
2359 WriteShort(MSG_ENTITY, self.modelindex);
2360 WriteByte(MSG_ENTITY, self.skin);
2361 WriteByte(MSG_ENTITY, self.frame);
2362 WriteCoord(MSG_ENTITY, self.origin_x);
2363 WriteCoord(MSG_ENTITY, self.origin_y);
2364 WriteCoord(MSG_ENTITY, self.origin_z);
2367 WriteCoord(MSG_ENTITY, self.velocity_x);
2368 WriteCoord(MSG_ENTITY, self.velocity_y);
2369 WriteCoord(MSG_ENTITY, self.velocity_z);
2373 WriteCoord(MSG_ENTITY, self.angles_x);
2374 WriteCoord(MSG_ENTITY, self.angles_y);
2375 WriteCoord(MSG_ENTITY, self.angles_z);
2379 WriteCoord(MSG_ENTITY, self.avelocity_x);
2380 WriteCoord(MSG_ENTITY, self.avelocity_y);
2381 WriteCoord(MSG_ENTITY, self.avelocity_z);
2383 WriteShort(MSG_ENTITY, self.scale * 256.0);
2384 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2385 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2386 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2387 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2392 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)
2397 e.classname = "modeleffect";
2405 e.teleport_time = t1;
2409 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2413 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2416 sz = max(e.scale, e.scale2);
2417 setsize(e, e.mins * sz, e.maxs * sz);
2418 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2421 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2423 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2426 float randombit(float bits)
2428 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2437 for(f = 1; f <= bits; f *= 2)
2446 r = (r - 1) / (n - 1);
2453 float randombits(float bits, float k, float error_return)
2457 while(k > 0 && bits != r)
2459 r += randombit(bits - r);
2468 void randombit_test(float bits, float iter)
2472 print(ftos(randombit(bits)), "\n");
2477 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2479 if(halflifedist > 0)
2480 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2481 else if(halflifedist < 0)
2482 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2491 #define cvar_string_normal builtin_cvar_string
2492 #define cvar_normal builtin_cvar
2494 string cvar_string_normal(string n)
2496 if not(cvar_type(n) & 1)
2497 backtrace(strcat("Attempt to access undefined cvar: ", n));
2498 return builtin_cvar_string(n);
2501 float cvar_normal(string n)
2503 return stof(cvar_string_normal(n));
2506 #define cvar_set_normal builtin_cvar_set
2514 oself.think = SUB_Remove;
2515 oself.nextthink = time;
2521 Execute func() after time + fdelay.
2522 self when func is executed = self when defer is called
2524 void defer(float fdelay, void() func)
2531 e.think = defer_think;
2532 e.nextthink = time + fdelay;
2535 .string aiment_classname;
2536 .float aiment_deadflag;
2537 void SetMovetypeFollow(entity ent, entity e)
2539 // FIXME this may not be warpzone aware
2540 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2541 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.
2542 ent.aiment = e; // make the hole follow bmodel
2543 ent.punchangle = e.angles; // the original angles of bmodel
2544 ent.view_ofs = ent.origin - e.origin; // relative origin
2545 ent.v_angle = ent.angles - e.angles; // relative angles
2546 ent.aiment_classname = strzone(e.classname);
2547 ent.aiment_deadflag = e.deadflag;
2549 void UnsetMovetypeFollow(entity ent)
2551 ent.movetype = MOVETYPE_FLY;
2552 PROJECTILE_MAKETRIGGER(ent);
2555 float LostMovetypeFollow(entity ent)
2558 if(ent.movetype != MOVETYPE_FOLLOW)
2564 if(ent.aiment.classname != ent.aiment_classname)
2566 if(ent.aiment.deadflag != ent.aiment_deadflag)
2572 float isPushable(entity e)
2581 case "droppedweapon":
2582 case "keepawayball":
2583 case "nexball_basketball":
2584 case "nexball_football":
2586 case "bullet": // antilagged bullets can't hit this either
2589 if (e.projectiledeathtype)