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 bcenterprint(string s)
112 // TODO replace by MSG_ALL (would show it to spectators too, though)?
114 FOR_EACH_PLAYER(head)
115 if (clienttype(head) == CLIENTTYPE_REAL)
116 centerprint(head, s);
119 void GameLogEcho(string s)
124 if (autocvar_sv_eventlog_files)
129 matches = autocvar_sv_eventlog_files_counter + 1;
130 cvar_set("sv_eventlog_files_counter", ftos(matches));
133 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
134 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
135 logfile = fopen(fn, FILE_APPEND);
136 fputs(logfile, ":logversion:3\n");
140 if (autocvar_sv_eventlog_files_timestamps)
141 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
143 fputs(logfile, strcat(s, "\n"));
146 if (autocvar_sv_eventlog_console)
155 // will be opened later
160 if (logfile_open && logfile >= 0)
167 float spawnpoint_nag;
168 void relocate_spawnpoint()
170 // nudge off the floor
171 setorigin(self, self.origin + '0 0 1');
173 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
174 if (trace_startsolid)
180 if (!move_out_of_solid(self))
181 objerror("could not get out of solid at all!");
182 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
183 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
184 print(" ", ftos(self.origin_y - o_y));
185 print(" ", ftos(self.origin_z - o_z), "'\n");
186 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
189 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
195 self.mins = self.maxs = '0 0 0';
196 objerror("player spawn point in solid, mapper sucks!\n");
201 self.use = spawnpoint_use;
202 self.team_saved = self.team;
206 if (have_team_spawns != 0)
208 have_team_spawns = 1;
209 have_team_spawns_forteam[self.team] = 1;
211 if (autocvar_r_showbboxes)
213 // show where spawnpoints point at too
214 makevectors(self.angles);
217 e.classname = "info_player_foo";
218 setorigin(e, self.origin + v_forward * 24);
219 setsize(e, '-8 -8 -8', '8 8 8');
220 e.solid = SOLID_TRIGGER;
224 #define strstr strstrofs
226 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
227 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
228 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
229 // BE CONSTANT OR strzoneD!
230 float strstr(string haystack, string needle, float offset)
234 len = strlen(needle);
235 endpos = strlen(haystack) - len;
236 while(offset <= endpos)
238 found = substring(haystack, offset, len);
247 float NUM_NEAREST_ENTITIES = 4;
248 entity nearest_entity[NUM_NEAREST_ENTITIES];
249 float nearest_length[NUM_NEAREST_ENTITIES];
250 entity findnearest(vector point, .string field, string value, vector axismod)
261 localhead = find(world, field, value);
264 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
265 dist = localhead.oldorigin;
267 dist = localhead.origin;
269 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
272 for (i = 0; i < num_nearest; ++i)
274 if (len < nearest_length[i])
278 // now i tells us where to insert at
279 // INSERTION SORT! YOU'VE SEEN IT! RUN!
280 if (i < NUM_NEAREST_ENTITIES)
282 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
284 nearest_length[j + 1] = nearest_length[j];
285 nearest_entity[j + 1] = nearest_entity[j];
287 nearest_length[i] = len;
288 nearest_entity[i] = localhead;
289 if (num_nearest < NUM_NEAREST_ENTITIES)
290 num_nearest = num_nearest + 1;
293 localhead = find(localhead, field, value);
296 // now use the first one from our list that we can see
297 for (i = 0; i < num_nearest; ++i)
299 traceline(point, nearest_entity[i].origin, TRUE, world);
300 if (trace_fraction == 1)
304 dprint("Nearest point (");
305 dprint(nearest_entity[0].netname);
306 dprint(") is not visible, using a visible one.\n");
308 return nearest_entity[i];
312 if (num_nearest == 0)
315 dprint("Not seeing any location point, using nearest as fallback.\n");
317 dprint("Candidates were: ");
318 for(j = 0; j < num_nearest; ++j)
322 dprint(nearest_entity[j].netname);
327 return nearest_entity[0];
330 void spawnfunc_target_location()
332 self.classname = "target_location";
333 // location name in netname
334 // eventually support: count, teamgame selectors, line of sight?
337 void spawnfunc_info_location()
339 self.classname = "target_location";
340 self.message = self.netname;
343 string NearestLocation(vector p)
348 loc = findnearest(p, classname, "target_location", '1 1 1');
355 loc = findnearest(p, target, "###item###", '1 1 4');
362 string formatmessage(string msg)
373 WarpZone_crosshair_trace(self);
374 cursor = trace_endpos;
375 cursor_ent = trace_ent;
379 break; // too many replacements
382 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
383 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
396 replacement = substring(msg, p, 2);
397 escape = substring(msg, p + 1, 1);
401 else if (escape == "\\")
403 else if (escape == "n")
405 else if (escape == "a")
406 replacement = ftos(floor(self.armorvalue));
407 else if (escape == "h")
408 replacement = ftos(floor(self.health));
409 else if (escape == "l")
410 replacement = NearestLocation(self.origin);
411 else if (escape == "y")
412 replacement = NearestLocation(cursor);
413 else if (escape == "d")
414 replacement = NearestLocation(self.death_origin);
415 else if (escape == "w") {
419 wep = self.switchweapon;
422 replacement = W_Name(wep);
423 } else if (escape == "W") {
424 if (self.items & IT_SHELLS) replacement = "shells";
425 else if (self.items & IT_NAILS) replacement = "bullets";
426 else if (self.items & IT_ROCKETS) replacement = "rockets";
427 else if (self.items & IT_CELLS) replacement = "cells";
428 else replacement = "batteries"; // ;)
429 } else if (escape == "x") {
430 replacement = cursor_ent.netname;
431 if (replacement == "" || !cursor_ent)
432 replacement = "nothing";
433 } else if (escape == "s")
434 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
435 else if (escape == "S")
436 replacement = ftos(vlen(self.velocity));
438 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
439 p = p + strlen(replacement);
444 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
445 return (value == 0) ? FALSE : TRUE;
454 >0: receives a cvar from name=argv(f) value=argv(f+1)
456 void GetCvars_handleString(string thisname, float f, .string field, string name)
461 strunzone(self.field);
462 self.field = string_null;
466 if (thisname == name)
469 strunzone(self.field);
470 self.field = strzone(argv(f + 1));
474 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
476 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
478 GetCvars_handleString(thisname, f, field, name);
479 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
480 if (thisname == name)
483 s = func(strcat1(self.field));
486 strunzone(self.field);
487 self.field = strzone(s);
491 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
498 if (thisname == name)
499 self.field = stof(argv(f + 1));
502 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
504 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
511 if (thisname == name)
515 self.field = stof(argv(f + 1));
524 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
527 float w_getbestweapon(entity e);
528 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
531 o = W_FixWeaponOrder_ForceComplete(wo);
532 if(self.weaponorder_byimpulse)
534 strunzone(self.weaponorder_byimpulse);
535 self.weaponorder_byimpulse = string_null;
537 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
540 void GetCvars(float f)
542 string s = string_null;
545 s = strcat1(argv(f));
549 MUTATOR_CALLHOOK(GetCvars);
550 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
551 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
552 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
553 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
554 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
555 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
556 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
557 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
558 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
559 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
560 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
561 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
562 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
563 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
564 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
565 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
566 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
567 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
568 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
569 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
570 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
571 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
572 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
574 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
575 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
577 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
578 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
579 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
580 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
581 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
583 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
586 if (s == "cl_weaponpriority")
587 self.switchweapon = w_getbestweapon(self);
588 if (s == "cl_allow_uidtracking")
589 PlayerStats_AddPlayer(self);
593 // decolorizes and team colors the player name when needed
594 string playername(entity p)
597 if (teamplay && !intermission_running && p.classname == "player")
599 t = Team_ColorCode(p.team);
600 return strcat(t, strdecolorize(p.netname));
606 vector randompos(vector m1, vector m2)
610 v_x = m2_x * random() + m1_x;
611 v_y = m2_y * random() + m1_y;
612 v_z = m2_z * random() + m1_z;
616 //#NO AUTOCVARS START
618 float g_pickup_shells;
619 float g_pickup_shells_max;
620 float g_pickup_nails;
621 float g_pickup_nails_max;
622 float g_pickup_rockets;
623 float g_pickup_rockets_max;
624 float g_pickup_cells;
625 float g_pickup_cells_max;
627 float g_pickup_fuel_jetpack;
628 float g_pickup_fuel_max;
629 float g_pickup_armorsmall;
630 float g_pickup_armorsmall_max;
631 float g_pickup_armorsmall_anyway;
632 float g_pickup_armormedium;
633 float g_pickup_armormedium_max;
634 float g_pickup_armormedium_anyway;
635 float g_pickup_armorbig;
636 float g_pickup_armorbig_max;
637 float g_pickup_armorbig_anyway;
638 float g_pickup_armorlarge;
639 float g_pickup_armorlarge_max;
640 float g_pickup_armorlarge_anyway;
641 float g_pickup_healthsmall;
642 float g_pickup_healthsmall_max;
643 float g_pickup_healthsmall_anyway;
644 float g_pickup_healthmedium;
645 float g_pickup_healthmedium_max;
646 float g_pickup_healthmedium_anyway;
647 float g_pickup_healthlarge;
648 float g_pickup_healthlarge_max;
649 float g_pickup_healthlarge_anyway;
650 float g_pickup_healthmega;
651 float g_pickup_healthmega_max;
652 float g_pickup_healthmega_anyway;
653 float g_pickup_ammo_anyway;
654 float g_pickup_weapons_anyway;
656 WEPSET_DECLARE_A(g_weaponarena_weapons);
657 float g_weaponarena_random;
658 float g_weaponarena_random_with_laser;
659 string g_weaponarena_list;
660 float g_weaponspeedfactor;
661 float g_weaponratefactor;
662 float g_weapondamagefactor;
663 float g_weaponforcefactor;
664 float g_weaponspreadfactor;
666 WEPSET_DECLARE_A(start_weapons);
667 WEPSET_DECLARE_A(start_weapons_default);
668 WEPSET_DECLARE_A(start_weapons_defaultmask);
670 float start_ammo_shells;
671 float start_ammo_nails;
672 float start_ammo_rockets;
673 float start_ammo_cells;
674 float start_ammo_fuel;
676 float start_armorvalue;
677 WEPSET_DECLARE_A(warmup_start_weapons);
678 WEPSET_DECLARE_A(warmup_start_weapons_default);
679 WEPSET_DECLARE_A(warmup_start_weapons_defaultmask);
680 float warmup_start_ammo_shells;
681 float warmup_start_ammo_nails;
682 float warmup_start_ammo_rockets;
683 float warmup_start_ammo_cells;
684 float warmup_start_ammo_fuel;
685 float warmup_start_health;
686 float warmup_start_armorvalue;
689 entity get_weaponinfo(float w);
691 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
693 var float i = weaponinfo.weapon;
699 if (g_lms || g_ca || allguns)
701 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
707 d = (i == WEP_SHOTGUN);
709 d = 0; // weapon is set a few lines later
711 d = (i == WEP_LASER || i == WEP_SHOTGUN);
713 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
714 d |= (i == WEP_HOOK);
715 if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
718 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
720 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
725 // 4: is set by default?
734 void readplayerstartcvars()
740 // initialize starting values for players
741 WEPSET_CLEAR_A(start_weapons);
742 WEPSET_CLEAR_A(start_weapons_default);
743 WEPSET_CLEAR_A(start_weapons_defaultmask);
745 start_ammo_shells = 0;
746 start_ammo_nails = 0;
747 start_ammo_rockets = 0;
748 start_ammo_cells = 0;
749 start_health = cvar("g_balance_health_start");
750 start_armorvalue = cvar("g_balance_armor_start");
753 WEPSET_CLEAR_A(g_weaponarena_weapons);
755 s = cvar_string("g_weaponarena");
756 if (s == "0" || s == "")
762 if (s == "0" || s == "")
768 // forcibly turn off weaponarena
773 g_weaponarena_list = "All Weapons";
774 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
776 e = get_weaponinfo(j);
777 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
778 WEPSET_OR_AW(g_weaponarena_weapons, j);
781 else if (s == "most")
784 g_weaponarena_list = "Most Weapons";
785 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
787 e = get_weaponinfo(j);
788 if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
789 if (e.spawnflags & WEP_FLAG_NORMAL)
790 WEPSET_OR_AW(g_weaponarena_weapons, j);
793 else if (s == "none")
796 g_weaponarena_list = "No Weapons";
801 t = tokenize_console(s);
802 g_weaponarena_list = "";
803 for (i = 0; i < t; ++i)
806 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
808 e = get_weaponinfo(j);
811 WEPSET_OR_AW(g_weaponarena_weapons, j);
812 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
818 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
821 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
825 g_weaponarena_random = cvar("g_weaponarena_random");
827 g_weaponarena_random = 0;
828 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
832 g_minstagib = 0; // incompatible
833 g_pinata = 0; // incompatible
834 g_weapon_stay = 0; // incompatible
835 WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
837 start_items |= IT_UNLIMITED_AMMO;
839 else if (g_minstagib)
841 g_pinata = 0; // incompatible
842 g_weapon_stay = 0; // incompatible
843 g_bloodloss = 0; // incompatible
845 start_armorvalue = 0;
846 WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
847 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
848 start_items |= IT_UNLIMITED_SUPERWEAPONS;
850 if (g_minstagib_invis_alpha <= 0)
851 g_minstagib_invis_alpha = -1;
855 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
857 e = get_weaponinfo(i);
858 float w = want_weapon("g_start_weapon_", e, FALSE);
860 WEPSET_OR_AW(start_weapons, i);
862 WEPSET_OR_AW(start_weapons_default, i);
864 WEPSET_OR_AW(start_weapons_defaultmask, i);
868 if(!cvar("g_use_ammunition"))
869 start_items |= IT_UNLIMITED_AMMO;
871 if(cvar("g_nexball"))
872 start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
876 start_ammo_cells = cvar("g_minstagib_ammo_start");
877 start_ammo_fuel = cvar("g_start_ammo_fuel");
879 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
881 start_ammo_rockets = 999;
882 start_ammo_shells = 999;
883 start_ammo_cells = 999;
884 start_ammo_nails = 999;
885 start_ammo_fuel = 999;
891 start_ammo_shells = cvar("g_lms_start_ammo_shells");
892 start_ammo_nails = cvar("g_lms_start_ammo_nails");
893 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
894 start_ammo_cells = cvar("g_lms_start_ammo_cells");
895 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
899 start_ammo_shells = cvar("g_start_ammo_shells");
900 start_ammo_nails = cvar("g_start_ammo_nails");
901 start_ammo_rockets = cvar("g_start_ammo_rockets");
902 start_ammo_cells = cvar("g_start_ammo_cells");
903 start_ammo_fuel = cvar("g_start_ammo_fuel");
909 start_health = cvar("g_lms_start_health");
910 start_armorvalue = cvar("g_lms_start_armor");
915 warmup_start_ammo_shells = start_ammo_shells;
916 warmup_start_ammo_nails = start_ammo_nails;
917 warmup_start_ammo_rockets = start_ammo_rockets;
918 warmup_start_ammo_cells = start_ammo_cells;
919 warmup_start_ammo_fuel = start_ammo_fuel;
920 warmup_start_health = start_health;
921 warmup_start_armorvalue = start_armorvalue;
922 WEPSET_COPY_AA(warmup_start_weapons, start_weapons);
923 WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default);
924 WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask);
926 if (!g_weaponarena && !g_minstagib && !g_ca)
928 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
929 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
930 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
931 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
932 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
933 warmup_start_health = cvar("g_warmup_start_health");
934 warmup_start_armorvalue = cvar("g_warmup_start_armor");
935 WEPSET_CLEAR_A(warmup_start_weapons);
936 WEPSET_CLEAR_A(warmup_start_weapons_default);
937 WEPSET_CLEAR_A(warmup_start_weapons_defaultmask);
938 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
940 e = get_weaponinfo(i);
941 float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"));
943 WEPSET_OR_AW(warmup_start_weapons, i);
945 WEPSET_OR_AW(warmup_start_weapons_default, i);
947 WEPSET_OR_AW(warmup_start_weapons_defaultmask, i);
953 start_items |= IT_JETPACK;
955 MUTATOR_CALLHOOK(SetStartItems);
957 if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK)))
959 g_grappling_hook = 0; // these two can't coexist, as they use the same button
960 start_items |= IT_FUEL_REGEN;
961 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
962 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
965 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
967 e = get_weaponinfo(i);
968 if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
969 weapon_action(i, WR_PRECACHE);
972 start_ammo_shells = max(0, start_ammo_shells);
973 start_ammo_nails = max(0, start_ammo_nails);
974 start_ammo_cells = max(0, start_ammo_cells);
975 start_ammo_rockets = max(0, start_ammo_rockets);
976 start_ammo_fuel = max(0, start_ammo_fuel);
978 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
979 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
980 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
981 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
982 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
986 float g_bugrigs_planar_movement;
987 float g_bugrigs_planar_movement_car_jumping;
988 float g_bugrigs_reverse_spinning;
989 float g_bugrigs_reverse_speeding;
990 float g_bugrigs_reverse_stopping;
991 float g_bugrigs_air_steering;
992 float g_bugrigs_angle_smoothing;
993 float g_bugrigs_friction_floor;
994 float g_bugrigs_friction_brake;
995 float g_bugrigs_friction_air;
996 float g_bugrigs_accel;
997 float g_bugrigs_speed_ref;
998 float g_bugrigs_speed_pow;
999 float g_bugrigs_steer;
1001 float g_touchexplode;
1002 float g_touchexplode_radius;
1003 float g_touchexplode_damage;
1004 float g_touchexplode_edgedamage;
1005 float g_touchexplode_force;
1010 string GetGametype(); // g_world.qc
1011 void readlevelcvars(void)
1013 g_minstagib = cvar("g_minstagib");
1015 // load ALL the mutators
1016 if(cvar("g_dodging"))
1017 MUTATOR_ADD(mutator_dodging);
1018 if(cvar("g_spawn_near_teammate"))
1019 MUTATOR_ADD(mutator_spawn_near_teammate);
1020 if(cvar("g_physical_items"))
1021 MUTATOR_ADD(mutator_physical_items);
1024 if(cvar("g_invincible_projectiles"))
1025 MUTATOR_ADD(mutator_invincibleprojectiles);
1026 if(cvar("g_new_toys"))
1027 MUTATOR_ADD(mutator_new_toys);
1029 MUTATOR_ADD(mutator_nix);
1030 if(cvar("g_rocket_flying"))
1031 MUTATOR_ADD(mutator_rocketflying);
1032 if(cvar("g_vampire"))
1033 MUTATOR_ADD(mutator_vampire);
1034 if(cvar("g_superspectate"))
1035 MUTATOR_ADD(mutator_superspec);
1038 // is this a mutator? is this a mode?
1039 if(cvar("g_sandbox"))
1040 MUTATOR_ADD(sandbox);
1042 if(cvar("sv_allow_fullbright"))
1043 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1045 g_bugrigs = cvar("g_bugrigs");
1046 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1047 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1048 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1049 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1050 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1051 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1052 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1053 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1054 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1055 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1056 g_bugrigs_accel = cvar("g_bugrigs_accel");
1057 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1058 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1059 g_bugrigs_steer = cvar("g_bugrigs_steer");
1061 g_touchexplode = cvar("g_touchexplode");
1062 g_touchexplode_radius = cvar("g_touchexplode_radius");
1063 g_touchexplode_damage = cvar("g_touchexplode_damage");
1064 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1065 g_touchexplode_force = cvar("g_touchexplode_force");
1067 sv_clones = cvar("sv_clones");
1068 sv_foginterval = cvar("sv_foginterval");
1069 g_cloaked = cvar("g_cloaked");
1071 g_cloaked = 1; // always enable cloak in CTS
1072 g_jump_grunt = cvar("g_jump_grunt");
1073 g_footsteps = cvar("g_footsteps");
1074 g_grappling_hook = cvar("g_grappling_hook");
1075 g_jetpack = cvar("g_jetpack");
1076 g_midair = cvar("g_midair");
1077 g_norecoil = cvar("g_norecoil");
1078 g_bloodloss = cvar("g_bloodloss");
1079 sv_maxidle = cvar("sv_maxidle");
1080 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1081 sv_autotaunt = cvar("sv_autotaunt");
1082 sv_taunt = cvar("sv_taunt");
1084 inWarmupStage = cvar("g_warmup");
1085 g_warmup_limit = cvar("g_warmup_limit");
1086 g_warmup_allguns = cvar("g_warmup_allguns");
1087 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1089 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1090 inWarmupStage = 0; // these modes cannot work together, sorry
1092 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1093 g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
1094 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1095 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1096 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1097 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1098 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1099 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1100 g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
1101 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1102 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1103 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1104 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1105 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1107 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1108 g_weaponratefactor = cvar("g_weaponratefactor");
1109 g_weapondamagefactor = cvar("g_weapondamagefactor");
1110 g_weaponforcefactor = cvar("g_weaponforcefactor");
1111 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1113 g_pickup_shells = cvar("g_pickup_shells");
1114 g_pickup_shells_max = cvar("g_pickup_shells_max");
1115 g_pickup_nails = cvar("g_pickup_nails");
1116 g_pickup_nails_max = cvar("g_pickup_nails_max");
1117 g_pickup_rockets = cvar("g_pickup_rockets");
1118 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1119 g_pickup_cells = cvar("g_pickup_cells");
1120 g_pickup_cells_max = cvar("g_pickup_cells_max");
1121 g_pickup_fuel = cvar("g_pickup_fuel");
1122 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1123 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1124 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1125 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1126 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1127 g_pickup_armormedium = cvar("g_pickup_armormedium");
1128 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1129 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1130 g_pickup_armorbig = cvar("g_pickup_armorbig");
1131 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1132 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1133 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1134 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1135 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1136 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1137 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1138 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1139 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1140 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1141 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1142 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1143 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1144 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1145 g_pickup_healthmega = cvar("g_pickup_healthmega");
1146 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1147 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1149 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1150 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1152 g_pinata = cvar("g_pinata");
1154 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1156 g_weapon_stay = cvar("g_weapon_stay");
1158 if not(inWarmupStage && !g_ca)
1159 game_starttime = cvar("g_start_delay");
1161 readplayerstartcvars();
1167 string precache_sound (string s) = #19;
1168 float precache_sound_index (string s) = #19;
1170 #define SND_VOLUME 1
1171 #define SND_ATTENUATION 2
1172 #define SND_LARGEENTITY 8
1173 #define SND_LARGESOUND 16
1175 float sound_allowed(float dest, entity e)
1177 // sounds from world may always pass
1180 if (e.classname == "body")
1182 else if (e.realowner && e.realowner != e)
1184 else if (e.owner && e.owner != e)
1189 // sounds to self may always pass
1190 if (dest == MSG_ONE)
1191 if (e == msg_entity)
1193 // sounds by players can be removed
1194 if (autocvar_bot_sound_monopoly)
1195 if (clienttype(e) == CLIENTTYPE_REAL)
1197 // anything else may pass
1201 #ifdef COMPAT_XON010_CHANNELS
1202 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1203 void sound(entity e, float chan, string samp, float vol, float atten)
1205 if (!sound_allowed(MSG_BROADCAST, e))
1207 builtin_sound(e, chan, samp, vol, atten);
1211 void sound(entity e, float chan, string samp, float vol, float atten)
1213 if (!sound_allowed(MSG_BROADCAST, e))
1215 sound7(e, chan, samp, vol, atten, 0, 0);
1219 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1223 if (!sound_allowed(dest, e))
1226 entno = num_for_edict(e);
1227 idx = precache_sound_index(samp);
1232 atten = floor(atten * 64);
1233 vol = floor(vol * 255);
1236 sflags |= SND_VOLUME;
1238 sflags |= SND_ATTENUATION;
1239 if (entno >= 8192 || chan < 0 || chan > 7)
1240 sflags |= SND_LARGEENTITY;
1242 sflags |= SND_LARGESOUND;
1244 WriteByte(dest, SVC_SOUND);
1245 WriteByte(dest, sflags);
1246 if (sflags & SND_VOLUME)
1247 WriteByte(dest, vol);
1248 if (sflags & SND_ATTENUATION)
1249 WriteByte(dest, atten);
1250 if (sflags & SND_LARGEENTITY)
1252 WriteShort(dest, entno);
1253 WriteByte(dest, chan);
1257 WriteShort(dest, entno * 8 + chan);
1259 if (sflags & SND_LARGESOUND)
1260 WriteShort(dest, idx);
1262 WriteByte(dest, idx);
1264 WriteCoord(dest, o_x);
1265 WriteCoord(dest, o_y);
1266 WriteCoord(dest, o_z);
1268 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1272 if (!sound_allowed(dest, e))
1275 o = e.origin + 0.5 * (e.mins + e.maxs);
1276 soundtoat(dest, e, o, chan, samp, vol, atten);
1278 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1280 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1282 void stopsoundto(float dest, entity e, float chan)
1286 if (!sound_allowed(dest, e))
1289 entno = num_for_edict(e);
1291 if (entno >= 8192 || chan < 0 || chan > 7)
1294 idx = precache_sound_index("misc/null.wav");
1295 sflags = SND_LARGEENTITY;
1297 sflags |= SND_LARGESOUND;
1298 WriteByte(dest, SVC_SOUND);
1299 WriteByte(dest, sflags);
1300 WriteShort(dest, entno);
1301 WriteByte(dest, chan);
1302 if (sflags & SND_LARGESOUND)
1303 WriteShort(dest, idx);
1305 WriteByte(dest, idx);
1306 WriteCoord(dest, e.origin_x);
1307 WriteCoord(dest, e.origin_y);
1308 WriteCoord(dest, e.origin_z);
1312 WriteByte(dest, SVC_STOPSOUND);
1313 WriteShort(dest, entno * 8 + chan);
1316 void stopsound(entity e, float chan)
1318 if (!sound_allowed(MSG_BROADCAST, e))
1321 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1322 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1325 void play2(entity e, string filename)
1327 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1329 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1332 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1334 float spamsound(entity e, float chan, string samp, float vol, float atten)
1336 if (!sound_allowed(MSG_BROADCAST, e))
1339 if (time > e.spamtime)
1342 sound(e, chan, samp, vol, atten);
1348 void play2team(float t, string filename)
1352 if (autocvar_bot_sound_monopoly)
1355 FOR_EACH_REALPLAYER(head)
1358 play2(head, filename);
1362 void play2all(string samp)
1364 if (autocvar_bot_sound_monopoly)
1367 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1370 void PrecachePlayerSounds(string f);
1371 void precache_playermodel(string m)
1373 float globhandle, i, n;
1376 if(substring(m, -9,5) == "_lod1")
1378 if(substring(m, -9,5) == "_lod2")
1381 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1384 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1388 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1391 n = search_getsize(globhandle);
1392 for (i = 0; i < n; ++i)
1394 //print(search_getfilename(globhandle, i), "\n");
1395 f = search_getfilename(globhandle, i);
1396 PrecachePlayerSounds(f);
1398 search_end(globhandle);
1400 void precache_all_playermodels(string pattern)
1402 float globhandle, i, n;
1405 globhandle = search_begin(pattern, TRUE, FALSE);
1408 n = search_getsize(globhandle);
1409 for (i = 0; i < n; ++i)
1411 //print(search_getfilename(globhandle, i), "\n");
1412 f = search_getfilename(globhandle, i);
1413 precache_playermodel(f);
1415 search_end(globhandle);
1420 // gamemode related things
1421 precache_model ("models/misc/chatbubble.spr");
1424 precache_model ("models/runematch/curse.mdl");
1425 precache_model ("models/runematch/rune.mdl");
1428 #ifdef TTURRETS_ENABLED
1429 if (autocvar_g_turrets)
1433 // Precache all player models if desired
1434 if (autocvar_sv_precacheplayermodels)
1436 PrecachePlayerSounds("sound/player/default.sounds");
1437 precache_all_playermodels("models/player/*.zym");
1438 precache_all_playermodels("models/player/*.dpm");
1439 precache_all_playermodels("models/player/*.md3");
1440 precache_all_playermodels("models/player/*.psk");
1441 precache_all_playermodels("models/player/*.iqm");
1444 if (autocvar_sv_defaultcharacter)
1447 s = autocvar_sv_defaultplayermodel_red;
1449 precache_playermodel(s);
1450 s = autocvar_sv_defaultplayermodel_blue;
1452 precache_playermodel(s);
1453 s = autocvar_sv_defaultplayermodel_yellow;
1455 precache_playermodel(s);
1456 s = autocvar_sv_defaultplayermodel_pink;
1458 precache_playermodel(s);
1459 s = autocvar_sv_defaultplayermodel;
1461 precache_playermodel(s);
1466 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1467 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1470 // gore and miscellaneous sounds
1471 //precache_sound ("misc/h2ohit.wav");
1472 precache_model ("models/hook.md3");
1473 precache_sound ("misc/armorimpact.wav");
1474 precache_sound ("misc/bodyimpact1.wav");
1475 precache_sound ("misc/bodyimpact2.wav");
1476 precache_sound ("misc/gib.wav");
1477 precache_sound ("misc/gib_splat01.wav");
1478 precache_sound ("misc/gib_splat02.wav");
1479 precache_sound ("misc/gib_splat03.wav");
1480 precache_sound ("misc/gib_splat04.wav");
1481 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1482 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1483 precache_sound ("misc/null.wav");
1484 precache_sound ("misc/spawn.wav");
1485 precache_sound ("misc/talk.wav");
1486 precache_sound ("misc/teleport.wav");
1487 precache_sound ("misc/poweroff.wav");
1488 precache_sound ("player/lava.wav");
1489 precache_sound ("player/slime.wav");
1491 precache_model ("models/sprites/0.spr32");
1492 precache_model ("models/sprites/1.spr32");
1493 precache_model ("models/sprites/2.spr32");
1494 precache_model ("models/sprites/3.spr32");
1495 precache_model ("models/sprites/4.spr32");
1496 precache_model ("models/sprites/5.spr32");
1497 precache_model ("models/sprites/6.spr32");
1498 precache_model ("models/sprites/7.spr32");
1499 precache_model ("models/sprites/8.spr32");
1500 precache_model ("models/sprites/9.spr32");
1501 precache_model ("models/sprites/10.spr32");
1503 // common weapon precaches
1504 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1505 precache_sound ("weapons/weapon_switch.wav");
1506 precache_sound ("weapons/weaponpickup.wav");
1507 precache_sound ("weapons/unavailable.wav");
1508 precache_sound ("weapons/dryfire.wav");
1509 if (g_grappling_hook)
1511 precache_sound ("weapons/hook_fire.wav"); // hook
1512 precache_sound ("weapons/hook_impact.wav"); // hook
1515 if(autocvar_sv_precacheweapons)
1517 //precache weapon models/sounds
1520 while (wep <= WEP_LAST)
1522 weapon_action(wep, WR_PRECACHE);
1527 precache_model("models/elaser.mdl");
1528 precache_model("models/laser.mdl");
1529 precache_model("models/ebomb.mdl");
1532 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1534 if (!self.noise && self.music) // quake 3 uses the music field
1535 self.noise = self.music;
1537 // plays music for the level if there is any
1540 precache_sound (self.noise);
1541 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1546 // WARNING: this kills the trace globals
1547 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1548 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1550 #define INITPRIO_FIRST 0
1551 #define INITPRIO_GAMETYPE 0
1552 #define INITPRIO_GAMETYPE_FALLBACK 1
1553 #define INITPRIO_FINDTARGET 10
1554 #define INITPRIO_DROPTOFLOOR 20
1555 #define INITPRIO_SETLOCATION 90
1556 #define INITPRIO_LINKDOORS 91
1557 #define INITPRIO_LAST 99
1559 .void(void) initialize_entity;
1560 .float initialize_entity_order;
1561 .entity initialize_entity_next;
1562 entity initialize_entity_first;
1564 void make_safe_for_remove(entity e)
1566 if (e.initialize_entity)
1568 entity ent, prev = world;
1569 for (ent = initialize_entity_first; ent; )
1571 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1573 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1574 // skip it in linked list
1577 prev.initialize_entity_next = ent.initialize_entity_next;
1578 ent = prev.initialize_entity_next;
1582 initialize_entity_first = ent.initialize_entity_next;
1583 ent = initialize_entity_first;
1589 ent = ent.initialize_entity_next;
1595 void objerror(string s)
1597 make_safe_for_remove(self);
1598 builtin_objerror(s);
1601 .float remove_except_protected_forbidden;
1602 void remove_except_protected(entity e)
1604 if(e.remove_except_protected_forbidden)
1605 error("not allowed to remove this at this point");
1609 void remove_unsafely(entity e)
1611 if(e.classname == "spike")
1612 error("Removing spikes is forbidden (crylink bug), please report");
1616 void remove_safely(entity e)
1618 make_safe_for_remove(e);
1622 void InitializeEntity(entity e, void(void) func, float order)
1626 if (!e || e.initialize_entity)
1628 // make a proxy initializer entity
1632 e.classname = "initialize_entity";
1636 e.initialize_entity = func;
1637 e.initialize_entity_order = order;
1639 cur = initialize_entity_first;
1643 if (!cur || cur.initialize_entity_order > order)
1645 // insert between prev and cur
1647 prev.initialize_entity_next = e;
1649 initialize_entity_first = e;
1650 e.initialize_entity_next = cur;
1654 cur = cur.initialize_entity_next;
1657 void InitializeEntitiesRun()
1660 startoflist = initialize_entity_first;
1661 initialize_entity_first = world;
1662 remove = remove_except_protected;
1663 for (self = startoflist; self; self = self.initialize_entity_next)
1665 self.remove_except_protected_forbidden = 1;
1667 for (self = startoflist; self; )
1670 var void(void) func;
1671 e = self.initialize_entity_next;
1672 func = self.initialize_entity;
1673 self.initialize_entity_order = 0;
1674 self.initialize_entity = func_null;
1675 self.initialize_entity_next = world;
1676 self.remove_except_protected_forbidden = 0;
1677 if (self.classname == "initialize_entity")
1681 builtin_remove(self);
1684 //dprint("Delayed initialization: ", self.classname, "\n");
1690 backtrace(strcat("Null function in: ", self.classname, "\n"));
1694 remove = remove_unsafely;
1697 .float uncustomizeentityforclient_set;
1698 .void(void) uncustomizeentityforclient;
1699 void UncustomizeEntitiesRun()
1703 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1704 self.uncustomizeentityforclient();
1707 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1709 e.customizeentityforclient = customizer;
1710 e.uncustomizeentityforclient = uncustomizer;
1711 e.uncustomizeentityforclient_set = !!uncustomizer;
1715 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1718 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1722 if (e.classname == "")
1723 e.classname = "net_linked";
1725 if (e.model == "" || self.modelindex == 0)
1729 setmodel(e, "null");
1733 e.SendEntity = sendfunc;
1734 e.SendFlags = 0xFFFFFF;
1737 e.effects |= EF_NODEPTHTEST;
1741 e.nextthink = time + dt;
1742 e.think = SUB_Remove;
1746 void adaptor_think2touch()
1755 void adaptor_think2use()
1767 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1769 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
1770 self.projectiledeathtype |= HITTYPE_SPLASH;
1771 adaptor_think2use();
1774 // deferred dropping
1775 void DropToFloor_Handler()
1777 builtin_droptofloor();
1778 self.dropped_origin = self.origin;
1783 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1788 float trace_hits_box_a0, trace_hits_box_a1;
1790 float trace_hits_box_1d(float end, float thmi, float thma)
1794 // just check if x is in range
1802 // do the trace with respect to x
1803 // 0 -> end has to stay in thmi -> thma
1804 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1805 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1806 if (trace_hits_box_a0 > trace_hits_box_a1)
1812 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1817 // now it is a trace from 0 to end
1819 trace_hits_box_a0 = 0;
1820 trace_hits_box_a1 = 1;
1822 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1824 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1826 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1832 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1834 return trace_hits_box(start, end, thmi - ma, thma - mi);
1837 float SUB_NoImpactCheck()
1839 // zero hitcontents = this is not the real impact, but either the
1840 // mirror-impact of something hitting the projectile instead of the
1841 // projectile hitting the something, or a touchareagrid one. Neither of
1842 // these stop the projectile from moving, so...
1843 if(trace_dphitcontents == 0)
1845 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1846 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)));
1849 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1851 if (other == world && self.size != '0 0 0')
1854 tic = self.velocity * sys_frametime;
1855 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1856 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1857 if (trace_fraction >= 1)
1859 dprint("Odd... did not hit...?\n");
1861 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1863 dprint("Detected and prevented the sky-grapple bug.\n");
1871 #define SUB_OwnerCheck() (other && (other == self.owner))
1873 void RemoveGrapplingHook(entity pl);
1874 void W_Crylink_Dequeue(entity e);
1875 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1877 if(SUB_OwnerCheck())
1879 if(SUB_NoImpactCheck())
1881 if(self.classname == "grapplinghook")
1882 RemoveGrapplingHook(self.realowner);
1883 else if(self.classname == "spike")
1885 W_Crylink_Dequeue(self);
1892 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1893 UpdateCSQCProjectile(self);
1896 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1898 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
1899 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
1901 void URI_Get_Callback(float id, float status, string data)
1903 if(url_URI_Get_Callback(id, status, data))
1907 else if (id == URI_GET_DISCARD)
1911 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1914 Curl_URI_Get_Callback(id, status, data);
1916 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1919 OnlineBanList_URI_Get_Callback(id, status, data);
1923 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1927 string uid2name(string myuid) {
1929 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1931 // FIXME remove this later after 0.6 release
1932 // convert old style broken records to correct style
1935 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1938 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1939 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1944 s = "^1Unregistered Player";
1948 float race_readTime(string map, float pos)
1956 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
1959 string race_readUID(string map, float pos)
1967 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
1970 float race_readPos(string map, float t) {
1972 for (i = 1; i <= RANKINGS_CNT; ++i)
1973 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
1976 return 0; // pos is zero if unranked
1979 void race_writeTime(string map, float t, string myuid)
1988 newpos = race_readPos(map, t);
1990 float i, prevpos = 0;
1991 for(i = 1; i <= RANKINGS_CNT; ++i)
1993 if(race_readUID(map, i) == myuid)
1996 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
1997 for (i = prevpos; 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));
2001 } else { // player has no ranked record yet
2002 for (i = RANKINGS_CNT; i > newpos; --i) {
2003 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2004 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2008 // store new time itself
2009 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2010 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2013 string race_readName(string map, float pos)
2021 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2024 string race_placeName(float pos) {
2025 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2027 if(mod(pos, 10) == 1)
2028 return strcat(ftos(pos), "st");
2029 else if(mod(pos, 10) == 2)
2030 return strcat(ftos(pos), "nd");
2031 else if(mod(pos, 10) == 3)
2032 return strcat(ftos(pos), "rd");
2034 return strcat(ftos(pos), "th");
2037 return strcat(ftos(pos), "th");
2040 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2043 vector start, org, delta, end, enddown, mstart;
2046 m = e.dphitcontentsmask;
2047 e.dphitcontentsmask = goodcontents | badcontents;
2050 delta = world.maxs - world.mins;
2054 for (i = 0; i < attempts; ++i)
2056 start_x = org_x + random() * delta_x;
2057 start_y = org_y + random() * delta_y;
2058 start_z = org_z + random() * delta_z;
2060 // rule 1: start inside world bounds, and outside
2061 // solid, and don't start from somewhere where you can
2062 // fall down to evil
2063 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2064 if (trace_fraction >= 1)
2066 if (trace_startsolid)
2068 if (trace_dphitcontents & badcontents)
2070 if (trace_dphitq3surfaceflags & badsurfaceflags)
2073 // rule 2: if we are too high, lower the point
2074 if (trace_fraction * delta_z > maxaboveground)
2075 start = trace_endpos + '0 0 1' * maxaboveground;
2076 enddown = trace_endpos;
2078 // rule 3: make sure we aren't outside the map. This only works
2079 // for somewhat well formed maps. A good rule of thumb is that
2080 // the map should have a convex outside hull.
2081 // these can be traceLINES as we already verified the starting box
2082 mstart = start + 0.5 * (e.mins + e.maxs);
2083 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2084 if (trace_fraction >= 1)
2086 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2087 if (trace_fraction >= 1)
2089 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2090 if (trace_fraction >= 1)
2092 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2093 if (trace_fraction >= 1)
2095 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2096 if (trace_fraction >= 1)
2099 // rule 4: we must "see" some spawnpoint
2100 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2101 if(checkpvs(mstart, sp))
2105 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2106 if(checkpvs(mstart, sp))
2112 // find a random vector to "look at"
2113 end_x = org_x + random() * delta_x;
2114 end_y = org_y + random() * delta_y;
2115 end_z = org_z + random() * delta_z;
2116 end = start + normalize(end - start) * vlen(delta);
2118 // rule 4: start TO end must not be too short
2119 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2120 if (trace_startsolid)
2122 if (trace_fraction < minviewdistance / vlen(delta))
2125 // rule 5: don't want to look at sky
2126 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2129 // rule 6: we must not end up in trigger_hurt
2130 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2136 e.dphitcontentsmask = m;
2140 setorigin(e, start);
2141 e.angles = vectoangles(end - start);
2142 dprint("Needed ", ftos(i + 1), " attempts\n");
2149 float zcurveparticles_effectno;
2150 vector zcurveparticles_start;
2151 float zcurveparticles_spd;
2153 void endzcurveparticles()
2155 if(zcurveparticles_effectno)
2158 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2160 zcurveparticles_effectno = 0;
2163 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2165 spd = bound(0, floor(spd / 16), 32767);
2166 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2168 endzcurveparticles();
2169 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2170 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2171 WriteShort(MSG_BROADCAST, effectno);
2172 WriteCoord(MSG_BROADCAST, start_x);
2173 WriteCoord(MSG_BROADCAST, start_y);
2174 WriteCoord(MSG_BROADCAST, start_z);
2175 zcurveparticles_effectno = effectno;
2176 zcurveparticles_start = start;
2179 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2180 WriteCoord(MSG_BROADCAST, end_x);
2181 WriteCoord(MSG_BROADCAST, end_y);
2182 WriteCoord(MSG_BROADCAST, end_z);
2183 WriteCoord(MSG_BROADCAST, end_dz);
2184 zcurveparticles_spd = spd;
2187 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2190 vector vecxy, velxy;
2192 vecxy = end - start;
2197 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2199 endzcurveparticles();
2200 trailparticles(world, effectno, start, end);
2204 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2205 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2208 void write_recordmarker(entity pl, float tstart, float dt)
2210 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2212 // also write a marker into demo files for demotc-race-record-extractor to find
2215 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2216 " ", ftos(tstart), " ", ftos(dt), "\n"));
2219 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2232 if(allowcenter) // 2: allow center handedness
2245 if(allowcenter) // 2: allow center handedness
2261 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2266 if (autocvar_g_shootfromeye)
2270 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
2271 else { vecs_y = 0; vecs_z -= 2; }
2279 else if (autocvar_g_shootfromcenter)
2284 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2294 else if (autocvar_g_shootfromclient)
2296 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2301 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2303 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2307 void attach_sameorigin(entity e, entity to, string tag)
2309 vector org, t_forward, t_left, t_up, e_forward, e_up;
2312 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2313 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2314 t_forward = v_forward * tagscale;
2315 t_left = v_right * -tagscale;
2316 t_up = v_up * tagscale;
2318 e.origin_x = org * t_forward;
2319 e.origin_y = org * t_left;
2320 e.origin_z = org * t_up;
2322 // current forward and up directions
2323 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2324 e.angles = AnglesTransform_FromVAngles(e.angles);
2326 e.angles = AnglesTransform_FromAngles(e.angles);
2327 fixedmakevectors(e.angles);
2329 // untransform forward, up!
2330 e_forward_x = v_forward * t_forward;
2331 e_forward_y = v_forward * t_left;
2332 e_forward_z = v_forward * t_up;
2333 e_up_x = v_up * t_forward;
2334 e_up_y = v_up * t_left;
2335 e_up_z = v_up * t_up;
2337 e.angles = fixedvectoangles2(e_forward, e_up);
2338 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2339 e.angles = AnglesTransform_ToVAngles(e.angles);
2341 e.angles = AnglesTransform_ToAngles(e.angles);
2343 setattachment(e, to, tag);
2344 setorigin(e, e.origin);
2347 void detach_sameorigin(entity e)
2350 org = gettaginfo(e, 0);
2351 e.angles = fixedvectoangles2(v_forward, v_up);
2352 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2353 e.angles = AnglesTransform_ToVAngles(e.angles);
2355 e.angles = AnglesTransform_ToAngles(e.angles);
2357 setattachment(e, world, "");
2358 setorigin(e, e.origin);
2361 void follow_sameorigin(entity e, entity to)
2363 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2364 e.aiment = to; // make the hole follow bmodel
2365 e.punchangle = to.angles; // the original angles of bmodel
2366 e.view_ofs = e.origin - to.origin; // relative origin
2367 e.v_angle = e.angles - to.angles; // relative angles
2370 void unfollow_sameorigin(entity e)
2372 e.movetype = MOVETYPE_NONE;
2375 entity gettaginfo_relative_ent;
2376 vector gettaginfo_relative(entity e, float tag)
2378 if (!gettaginfo_relative_ent)
2380 gettaginfo_relative_ent = spawn();
2381 gettaginfo_relative_ent.effects = EF_NODRAW;
2383 gettaginfo_relative_ent.model = e.model;
2384 gettaginfo_relative_ent.modelindex = e.modelindex;
2385 gettaginfo_relative_ent.frame = e.frame;
2386 return gettaginfo(gettaginfo_relative_ent, tag);
2391 float modeleffect_SendEntity(entity to, float sf)
2394 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2397 if(self.velocity != '0 0 0')
2399 if(self.angles != '0 0 0')
2401 if(self.avelocity != '0 0 0')
2404 WriteByte(MSG_ENTITY, f);
2405 WriteShort(MSG_ENTITY, self.modelindex);
2406 WriteByte(MSG_ENTITY, self.skin);
2407 WriteByte(MSG_ENTITY, self.frame);
2408 WriteCoord(MSG_ENTITY, self.origin_x);
2409 WriteCoord(MSG_ENTITY, self.origin_y);
2410 WriteCoord(MSG_ENTITY, self.origin_z);
2413 WriteCoord(MSG_ENTITY, self.velocity_x);
2414 WriteCoord(MSG_ENTITY, self.velocity_y);
2415 WriteCoord(MSG_ENTITY, self.velocity_z);
2419 WriteCoord(MSG_ENTITY, self.angles_x);
2420 WriteCoord(MSG_ENTITY, self.angles_y);
2421 WriteCoord(MSG_ENTITY, self.angles_z);
2425 WriteCoord(MSG_ENTITY, self.avelocity_x);
2426 WriteCoord(MSG_ENTITY, self.avelocity_y);
2427 WriteCoord(MSG_ENTITY, self.avelocity_z);
2429 WriteShort(MSG_ENTITY, self.scale * 256.0);
2430 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2431 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2432 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2433 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2438 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)
2443 e.classname = "modeleffect";
2451 e.teleport_time = t1;
2455 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2459 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2462 sz = max(e.scale, e.scale2);
2463 setsize(e, e.mins * sz, e.maxs * sz);
2464 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2467 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2469 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2472 float randombit(float bits)
2474 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2483 for(f = 1; f <= bits; f *= 2)
2492 r = (r - 1) / (n - 1);
2499 float randombits(float bits, float k, float error_return)
2503 while(k > 0 && bits != r)
2505 r += randombit(bits - r);
2514 void randombit_test(float bits, float iter)
2518 print(ftos(randombit(bits)), "\n");
2523 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2525 if(halflifedist > 0)
2526 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2527 else if(halflifedist < 0)
2528 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2537 #define cvar_string_normal builtin_cvar_string
2538 #define cvar_normal builtin_cvar
2540 string cvar_string_normal(string n)
2542 if not(cvar_type(n) & 1)
2543 backtrace(strcat("Attempt to access undefined cvar: ", n));
2544 return builtin_cvar_string(n);
2547 float cvar_normal(string n)
2549 return stof(cvar_string_normal(n));
2552 #define cvar_set_normal builtin_cvar_set
2560 oself.think = SUB_Remove;
2561 oself.nextthink = time;
2567 Execute func() after time + fdelay.
2568 self when func is executed = self when defer is called
2570 void defer(float fdelay, void() func)
2577 e.think = defer_think;
2578 e.nextthink = time + fdelay;
2581 .string aiment_classname;
2582 .float aiment_deadflag;
2583 void SetMovetypeFollow(entity ent, entity e)
2585 // FIXME this may not be warpzone aware
2586 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2587 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.
2588 ent.aiment = e; // make the hole follow bmodel
2589 ent.punchangle = e.angles; // the original angles of bmodel
2590 ent.view_ofs = ent.origin - e.origin; // relative origin
2591 ent.v_angle = ent.angles - e.angles; // relative angles
2592 ent.aiment_classname = strzone(e.classname);
2593 ent.aiment_deadflag = e.deadflag;
2595 void UnsetMovetypeFollow(entity ent)
2597 ent.movetype = MOVETYPE_FLY;
2598 PROJECTILE_MAKETRIGGER(ent);
2601 float LostMovetypeFollow(entity ent)
2604 if(ent.movetype != MOVETYPE_FOLLOW)
2610 if(ent.aiment.classname != ent.aiment_classname)
2612 if(ent.aiment.deadflag != ent.aiment_deadflag)
2618 float isPushable(entity e)
2627 case "droppedweapon":
2628 case "keepawayball":
2629 case "nexball_basketball":
2630 case "nexball_football":
2632 case "bullet": // antilagged bullets can't hit this either
2635 if (e.projectiledeathtype)
2640 void dedicated_print(string input) // print(), but only print if the server is not local
2642 if not(server_is_local) { print(input); }