4 #include "miscfunctions.qh"
5 #include "../dpdefs/progsdefs.qh"
6 #include "../dpdefs/dpextensions.qh"
7 #include "../common/playerstats.qh"
8 #include "../warpzonelib/anglestransform.qh"
9 #include "../warpzonelib/server.qh"
10 #include "../common/constants.qh"
11 #include "../common/teams.qh"
12 #include "../common/util.qh"
13 #include "../common/urllib.qh"
14 #include "../common/command/generic.qh"
15 #include "../common/weapons/weapons.qh"
16 #include "weapons/accuracy.qh"
17 #include "weapons/csqcprojectile.qh"
18 #include "weapons/selection.qh"
20 #include "autocvars.qh"
21 #include "constants.qh"
23 #include "../common/notifications.qh"
24 #include "../common/deathtypes.qh"
25 #include "../common/triggers/subs.qh"
26 #include "mutators/mutators_include.qh"
27 #include "tturrets/include/turrets_early.qh"
28 #include "../common/mapinfo.qh"
29 #include "command/common.qh"
30 #include "../csqcmodellib/sv_model.qh"
34 void crosshair_trace(entity pl)
36 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));
38 void crosshair_trace_plusvisibletriggers(entity pl)
42 first = findchainfloat(solid, SOLID_TRIGGER);
44 for (e = first; e; e = e.chain)
50 for (e = first; e; e = e.chain)
51 e.solid = SOLID_TRIGGER;
53 void WarpZone_crosshair_trace(entity pl)
55 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));
59 string admin_name(void)
61 if(autocvar_sv_adminnick != "")
62 return autocvar_sv_adminnick;
64 return "SERVER ADMIN";
67 void DistributeEvenly_Init(float amount, float totalweight)
69 if (DistributeEvenly_amount)
71 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
72 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
75 DistributeEvenly_amount = 0;
77 DistributeEvenly_amount = amount;
78 DistributeEvenly_totalweight = totalweight;
80 float DistributeEvenly_Get(float weight)
85 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
86 DistributeEvenly_totalweight -= weight;
87 DistributeEvenly_amount -= f;
90 float DistributeEvenly_GetRandomized(float weight)
95 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
96 DistributeEvenly_totalweight -= weight;
97 DistributeEvenly_amount -= f;
102 void GameLogEcho(string s)
107 if (autocvar_sv_eventlog_files)
112 matches = autocvar_sv_eventlog_files_counter + 1;
113 cvar_set("sv_eventlog_files_counter", ftos(matches));
116 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
117 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
118 logfile = fopen(fn, FILE_APPEND);
119 fputs(logfile, ":logversion:3\n");
123 if (autocvar_sv_eventlog_files_timestamps)
124 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
126 fputs(logfile, strcat(s, "\n"));
129 if (autocvar_sv_eventlog_console)
138 // will be opened later
143 if (logfile_open && logfile >= 0)
150 entity findnearest(vector point, .string field, string value, vector axismod)
161 localhead = find(world, field, value);
164 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
165 dist = localhead.oldorigin;
167 dist = localhead.origin;
169 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
172 for (i = 0; i < num_nearest; ++i)
174 if (len < nearest_length[i])
178 // now i tells us where to insert at
179 // INSERTION SORT! YOU'VE SEEN IT! RUN!
180 if (i < NUM_NEAREST_ENTITIES)
182 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
184 nearest_length[j + 1] = nearest_length[j];
185 nearest_entity[j + 1] = nearest_entity[j];
187 nearest_length[i] = len;
188 nearest_entity[i] = localhead;
189 if (num_nearest < NUM_NEAREST_ENTITIES)
190 num_nearest = num_nearest + 1;
193 localhead = find(localhead, field, value);
196 // now use the first one from our list that we can see
197 for (i = 0; i < num_nearest; ++i)
199 traceline(point, nearest_entity[i].origin, true, world);
200 if (trace_fraction == 1)
204 dprint("Nearest point (");
205 dprint(nearest_entity[0].netname);
206 dprint(") is not visible, using a visible one.\n");
208 return nearest_entity[i];
212 if (num_nearest == 0)
215 dprint("Not seeing any location point, using nearest as fallback.\n");
217 dprint("Candidates were: ");
218 for(j = 0; j < num_nearest; ++j)
222 dprint(nearest_entity[j].netname);
227 return nearest_entity[0];
230 void spawnfunc_target_location()
232 self.classname = "target_location";
233 // location name in netname
234 // eventually support: count, teamgame selectors, line of sight?
237 void spawnfunc_info_location()
239 self.classname = "target_location";
240 self.message = self.netname;
243 string NearestLocation(vector p)
248 loc = findnearest(p, classname, "target_location", '1 1 1');
255 loc = findnearest(p, target, "###item###", '1 1 4');
262 string formatmessage(string msg)
273 WarpZone_crosshair_trace(self);
274 cursor = trace_endpos;
275 cursor_ent = trace_ent;
279 break; // too many replacements
282 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
283 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
296 replacement = substring(msg, p, 2);
297 escape = substring(msg, p + 1, 1);
301 else if (escape == "\\")
303 else if (escape == "n")
305 else if (escape == "a")
306 replacement = ftos(floor(self.armorvalue));
307 else if (escape == "h")
308 replacement = ftos(floor(self.health));
309 else if (escape == "l")
310 replacement = NearestLocation(self.origin);
311 else if (escape == "y")
312 replacement = NearestLocation(cursor);
313 else if (escape == "d")
314 replacement = NearestLocation(self.death_origin);
315 else if (escape == "w") {
319 wep = self.switchweapon;
322 replacement = WEP_NAME(wep);
323 } else if (escape == "W") {
324 if (self.items & IT_SHELLS) replacement = "shells";
325 else if (self.items & IT_NAILS) replacement = "bullets";
326 else if (self.items & IT_ROCKETS) replacement = "rockets";
327 else if (self.items & IT_CELLS) replacement = "cells";
328 else if (self.items & IT_PLASMA) replacement = "plasma";
329 else replacement = "batteries"; // ;)
330 } else if (escape == "x") {
331 replacement = cursor_ent.netname;
332 if (replacement == "" || !cursor_ent)
333 replacement = "nothing";
334 } else if (escape == "s")
335 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
336 else if (escape == "S")
337 replacement = ftos(vlen(self.velocity));
339 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
340 p = p + strlen(replacement);
345 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
346 return (value == 0) ? false : true;
355 >0: receives a cvar from name=argv(f) value=argv(f+1)
357 void GetCvars_handleString(string thisname, float f, .string field, string name)
362 strunzone(self.field);
363 self.field = string_null;
367 if (thisname == name)
370 strunzone(self.field);
371 self.field = strzone(argv(f + 1));
375 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
377 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
379 GetCvars_handleString(thisname, f, field, name);
380 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
381 if (thisname == name)
384 s = func(strcat1(self.field));
387 strunzone(self.field);
388 self.field = strzone(s);
392 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
399 if (thisname == name)
400 self.field = stof(argv(f + 1));
403 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
405 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
412 if (thisname == name)
416 self.field = stof(argv(f + 1));
425 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
428 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
431 o = W_FixWeaponOrder_ForceComplete(wo);
432 if(self.weaponorder_byimpulse)
434 strunzone(self.weaponorder_byimpulse);
435 self.weaponorder_byimpulse = string_null;
437 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
440 void GetCvars(float f)
442 string s = string_null;
445 s = strcat1(argv(f));
450 MUTATOR_CALLHOOK(GetCvars);
452 Notification_GetCvars();
454 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
455 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
456 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
457 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
458 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
459 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
465 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
466 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
467 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
468 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
469 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
470 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
471 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
472 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
473 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
474 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
475 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
476 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
477 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
479 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
480 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
482 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
483 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
484 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
485 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
486 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
488 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
491 if (s == "cl_weaponpriority")
492 self.switchweapon = w_getbestweapon(self);
493 if (s == "cl_allow_uidtracking")
494 PlayerStats_GameReport_AddPlayer(self);
498 // decolorizes and team colors the player name when needed
499 string playername(entity p)
502 if (teamplay && !intermission_running && IS_PLAYER(p))
504 t = Team_ColorCode(p.team);
505 return strcat(t, strdecolorize(p.netname));
511 vector randompos(vector m1, vector m2)
515 v.x = m2_x * random() + m1_x;
516 v.y = m2_y * random() + m1_y;
517 v.z = m2_z * random() + m1_z;
521 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
523 int i = weaponinfo.weapon;
529 if (g_lms || g_ca || allguns)
531 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
537 d = (i == WEP_SHOTGUN);
539 d = 0; // weapon is set a few lines later
541 d = !(!weaponinfo.weaponstart);
543 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
544 d |= (i == WEP_HOOK);
545 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
548 float t = weaponinfo.weaponstartoverride;
550 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
555 // 4: is set by default?
564 void readplayerstartcvars()
570 // initialize starting values for players
571 start_weapons = '0 0 0';
572 start_weapons_default = '0 0 0';
573 start_weapons_defaultmask = '0 0 0';
575 start_ammo_shells = 0;
576 start_ammo_nails = 0;
577 start_ammo_rockets = 0;
578 start_ammo_cells = 0;
579 start_ammo_plasma = 0;
580 start_health = cvar("g_balance_health_start");
581 start_armorvalue = cvar("g_balance_armor_start");
584 g_weaponarena_weapons = '0 0 0';
586 s = cvar_string("g_weaponarena");
587 if (s == "0" || s == "")
593 if (s == "0" || s == "")
599 // forcibly turn off weaponarena
601 else if (s == "all" || s == "1")
604 g_weaponarena_list = "All Weapons";
605 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
607 e = get_weaponinfo(j);
608 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
609 g_weaponarena_weapons |= WepSet_FromWeapon(j);
612 else if (s == "most")
615 g_weaponarena_list = "Most Weapons";
616 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
618 e = get_weaponinfo(j);
619 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
620 if (e.spawnflags & WEP_FLAG_NORMAL)
621 g_weaponarena_weapons |= WepSet_FromWeapon(j);
624 else if (s == "none")
627 g_weaponarena_list = "No Weapons";
632 t = tokenize_console(s);
633 g_weaponarena_list = "";
634 for (i = 0; i < t; ++i)
637 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
639 e = get_weaponinfo(j);
642 g_weaponarena_weapons |= WepSet_FromWeapon(j);
643 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
649 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
652 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
656 g_weaponarena_random = cvar("g_weaponarena_random");
658 g_weaponarena_random = 0;
659 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
663 g_weapon_stay = 0; // incompatible
664 start_weapons = g_weaponarena_weapons;
665 start_items |= IT_UNLIMITED_AMMO;
669 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
671 e = get_weaponinfo(i);
672 int w = want_weapon(e, false);
674 start_weapons |= WepSet_FromWeapon(i);
676 start_weapons_default |= WepSet_FromWeapon(i);
678 start_weapons_defaultmask |= WepSet_FromWeapon(i);
682 if(!cvar("g_use_ammunition"))
683 start_items |= IT_UNLIMITED_AMMO;
685 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
687 start_ammo_shells = 999;
688 start_ammo_nails = 999;
689 start_ammo_rockets = 999;
690 start_ammo_cells = 999;
691 start_ammo_plasma = 999;
692 start_ammo_fuel = 999;
696 start_ammo_shells = cvar("g_start_ammo_shells");
697 start_ammo_nails = cvar("g_start_ammo_nails");
698 start_ammo_rockets = cvar("g_start_ammo_rockets");
699 start_ammo_cells = cvar("g_start_ammo_cells");
700 start_ammo_plasma = cvar("g_start_ammo_plasma");
701 start_ammo_fuel = cvar("g_start_ammo_fuel");
706 warmup_start_ammo_shells = start_ammo_shells;
707 warmup_start_ammo_nails = start_ammo_nails;
708 warmup_start_ammo_rockets = start_ammo_rockets;
709 warmup_start_ammo_cells = start_ammo_cells;
710 warmup_start_ammo_plasma = start_ammo_plasma;
711 warmup_start_ammo_fuel = start_ammo_fuel;
712 warmup_start_health = start_health;
713 warmup_start_armorvalue = start_armorvalue;
714 warmup_start_weapons = start_weapons;
715 warmup_start_weapons_default = start_weapons_default;
716 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
718 if (!g_weaponarena && !g_ca)
720 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
721 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
722 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
723 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
724 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
725 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
726 warmup_start_health = cvar("g_warmup_start_health");
727 warmup_start_armorvalue = cvar("g_warmup_start_armor");
728 warmup_start_weapons = '0 0 0';
729 warmup_start_weapons_default = '0 0 0';
730 warmup_start_weapons_defaultmask = '0 0 0';
731 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
733 e = get_weaponinfo(i);
734 int w = want_weapon(e, g_warmup_allguns);
736 warmup_start_weapons |= WepSet_FromWeapon(i);
738 warmup_start_weapons_default |= WepSet_FromWeapon(i);
740 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
746 start_items |= IT_JETPACK;
748 MUTATOR_CALLHOOK(SetStartItems);
750 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
752 start_items |= IT_FUEL_REGEN;
753 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
754 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
757 WepSet precache_weapons = start_weapons;
758 if (g_warmup_allguns != 1)
759 precache_weapons |= warmup_start_weapons;
760 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
762 e = get_weaponinfo(i);
763 if(precache_weapons & WepSet_FromWeapon(i))
764 WEP_ACTION(i, WR_INIT);
767 start_ammo_shells = max(0, start_ammo_shells);
768 start_ammo_nails = max(0, start_ammo_nails);
769 start_ammo_rockets = max(0, start_ammo_rockets);
770 start_ammo_cells = max(0, start_ammo_cells);
771 start_ammo_plasma = max(0, start_ammo_plasma);
772 start_ammo_fuel = max(0, start_ammo_fuel);
774 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
775 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
776 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
777 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
778 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
779 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
782 float sound_allowed(float destin, entity e)
784 // sounds from world may always pass
787 if (e.classname == "body")
789 else if (e.realowner && e.realowner != e)
791 else if (e.owner && e.owner != e)
796 // sounds to self may always pass
797 if (destin == MSG_ONE)
800 // sounds by players can be removed
801 if (autocvar_bot_sound_monopoly)
802 if (IS_REAL_CLIENT(e))
804 // anything else may pass
809 void sound(entity e, float chan, string samp, float vol, float attenu)
811 if (!sound_allowed(MSG_BROADCAST, e))
813 sound7(e, chan, samp, vol, attenu, 0, 0);
816 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
820 if (!sound_allowed(_dest, e))
823 entno = num_for_edict(e);
824 idx = precache_sound_index(samp);
829 attenu = floor(attenu * 64);
830 vol = floor(vol * 255);
833 sflags |= SND_VOLUME;
835 sflags |= SND_ATTENUATION;
836 if (entno >= 8192 || chan < 0 || chan > 7)
837 sflags |= SND_LARGEENTITY;
839 sflags |= SND_LARGESOUND;
841 WriteByte(_dest, SVC_SOUND);
842 WriteByte(_dest, sflags);
843 if (sflags & SND_VOLUME)
844 WriteByte(_dest, vol);
845 if (sflags & SND_ATTENUATION)
846 WriteByte(_dest, attenu);
847 if (sflags & SND_LARGEENTITY)
849 WriteShort(_dest, entno);
850 WriteByte(_dest, chan);
854 WriteShort(_dest, entno * 8 + chan);
856 if (sflags & SND_LARGESOUND)
857 WriteShort(_dest, idx);
859 WriteByte(_dest, idx);
861 WriteCoord(_dest, o.x);
862 WriteCoord(_dest, o.y);
863 WriteCoord(_dest, o.z);
865 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
869 if (!sound_allowed(_dest, e))
872 o = e.origin + 0.5 * (e.mins + e.maxs);
873 soundtoat(_dest, e, o, chan, samp, vol, _atten);
875 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
877 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
879 void stopsoundto(float _dest, entity e, float chan)
883 if (!sound_allowed(_dest, e))
886 entno = num_for_edict(e);
888 if (entno >= 8192 || chan < 0 || chan > 7)
891 idx = precache_sound_index("misc/null.wav");
892 sflags = SND_LARGEENTITY;
894 sflags |= SND_LARGESOUND;
895 WriteByte(_dest, SVC_SOUND);
896 WriteByte(_dest, sflags);
897 WriteShort(_dest, entno);
898 WriteByte(_dest, chan);
899 if (sflags & SND_LARGESOUND)
900 WriteShort(_dest, idx);
902 WriteByte(_dest, idx);
903 WriteCoord(_dest, e.origin.x);
904 WriteCoord(_dest, e.origin.y);
905 WriteCoord(_dest, e.origin.z);
909 WriteByte(_dest, SVC_STOPSOUND);
910 WriteShort(_dest, entno * 8 + chan);
913 void stopsound(entity e, float chan)
915 if (!sound_allowed(MSG_BROADCAST, e))
918 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
919 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
922 void play2(entity e, string filename)
924 //stuffcmd(e, strcat("play2 ", filename, "\n"));
926 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
929 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
931 float spamsound(entity e, float chan, string samp, float vol, float _atten)
933 if (!sound_allowed(MSG_BROADCAST, e))
936 if (time > e.spamtime)
939 sound(e, chan, samp, vol, _atten);
945 void play2team(float t, string filename)
949 if (autocvar_bot_sound_monopoly)
952 FOR_EACH_REALPLAYER(head)
955 play2(head, filename);
959 void play2all(string samp)
961 if (autocvar_bot_sound_monopoly)
964 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
967 void PrecachePlayerSounds(string f);
968 void precache_playermodel(string m)
970 float globhandle, i, n;
973 if(substring(m, -9,5) == "_lod1")
975 if(substring(m, -9,5) == "_lod2")
978 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
981 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
985 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
988 n = search_getsize(globhandle);
989 for (i = 0; i < n; ++i)
991 //print(search_getfilename(globhandle, i), "\n");
992 f = search_getfilename(globhandle, i);
993 PrecachePlayerSounds(f);
995 search_end(globhandle);
997 void precache_all_playermodels(string pattern)
999 float globhandle, i, n;
1002 globhandle = search_begin(pattern, true, false);
1005 n = search_getsize(globhandle);
1006 for (i = 0; i < n; ++i)
1008 //print(search_getfilename(globhandle, i), "\n");
1009 f = search_getfilename(globhandle, i);
1010 precache_playermodel(f);
1012 search_end(globhandle);
1017 // gamemode related things
1018 precache_model ("models/misc/chatbubble.spr");
1019 precache_model("models/ice/ice.md3");
1021 #ifdef TTURRETS_ENABLED
1022 if (autocvar_g_turrets)
1026 // Precache all player models if desired
1027 if (autocvar_sv_precacheplayermodels)
1029 PrecachePlayerSounds("sound/player/default.sounds");
1030 precache_all_playermodels("models/player/*.zym");
1031 precache_all_playermodels("models/player/*.dpm");
1032 precache_all_playermodels("models/player/*.md3");
1033 precache_all_playermodels("models/player/*.psk");
1034 precache_all_playermodels("models/player/*.iqm");
1037 if (autocvar_sv_defaultcharacter)
1040 s = autocvar_sv_defaultplayermodel_red;
1042 precache_playermodel(s);
1043 s = autocvar_sv_defaultplayermodel_blue;
1045 precache_playermodel(s);
1046 s = autocvar_sv_defaultplayermodel_yellow;
1048 precache_playermodel(s);
1049 s = autocvar_sv_defaultplayermodel_pink;
1051 precache_playermodel(s);
1052 s = autocvar_sv_defaultplayermodel;
1054 precache_playermodel(s);
1059 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1060 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1063 // gore and miscellaneous sounds
1064 //precache_sound ("misc/h2ohit.wav");
1065 precache_model ("models/hook.md3");
1066 precache_sound ("misc/armorimpact.wav");
1067 precache_sound ("misc/bodyimpact1.wav");
1068 precache_sound ("misc/bodyimpact2.wav");
1069 precache_sound ("misc/gib.wav");
1070 precache_sound ("misc/gib_splat01.wav");
1071 precache_sound ("misc/gib_splat02.wav");
1072 precache_sound ("misc/gib_splat03.wav");
1073 precache_sound ("misc/gib_splat04.wav");
1074 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1075 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1076 precache_sound ("misc/null.wav");
1077 precache_sound ("misc/spawn.wav");
1078 precache_sound ("misc/talk.wav");
1079 precache_sound ("misc/teleport.wav");
1080 precache_sound ("misc/poweroff.wav");
1081 precache_sound ("player/lava.wav");
1082 precache_sound ("player/slime.wav");
1084 precache_model ("models/sprites/0.spr32");
1085 precache_model ("models/sprites/1.spr32");
1086 precache_model ("models/sprites/2.spr32");
1087 precache_model ("models/sprites/3.spr32");
1088 precache_model ("models/sprites/4.spr32");
1089 precache_model ("models/sprites/5.spr32");
1090 precache_model ("models/sprites/6.spr32");
1091 precache_model ("models/sprites/7.spr32");
1092 precache_model ("models/sprites/8.spr32");
1093 precache_model ("models/sprites/9.spr32");
1094 precache_model ("models/sprites/10.spr32");
1096 // common weapon precaches
1097 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1098 precache_sound ("weapons/weapon_switch.wav");
1099 precache_sound ("weapons/weaponpickup.wav");
1100 precache_sound ("weapons/unavailable.wav");
1101 precache_sound ("weapons/dryfire.wav");
1102 if (g_grappling_hook)
1104 precache_sound ("weapons/hook_fire.wav"); // hook
1105 precache_sound ("weapons/hook_impact.wav"); // hook
1108 precache_model("models/elaser.mdl");
1109 precache_model("models/laser.mdl");
1110 precache_model("models/ebomb.mdl");
1113 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1115 if (!self.noise && self.music) // quake 3 uses the music field
1116 self.noise = self.music;
1118 // plays music for the level if there is any
1121 precache_sound (self.noise);
1122 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1126 #include "precache-for-csqc.inc"
1130 void make_safe_for_remove(entity e)
1132 if (e.initialize_entity)
1134 entity ent, prev = world;
1135 for (ent = initialize_entity_first; ent; )
1137 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1139 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1140 // skip it in linked list
1143 prev.initialize_entity_next = ent.initialize_entity_next;
1144 ent = prev.initialize_entity_next;
1148 initialize_entity_first = ent.initialize_entity_next;
1149 ent = initialize_entity_first;
1155 ent = ent.initialize_entity_next;
1161 void objerror(string s)
1163 make_safe_for_remove(self);
1164 builtin_objerror(s);
1167 .float remove_except_protected_forbidden;
1168 void remove_except_protected(entity e)
1170 if(e.remove_except_protected_forbidden)
1171 error("not allowed to remove this at this point");
1175 void remove_unsafely(entity e)
1177 if(e.classname == "spike")
1178 error("Removing spikes is forbidden (crylink bug), please report");
1182 void remove_safely(entity e)
1184 make_safe_for_remove(e);
1188 void InitializeEntity(entity e, void(void) func, float order)
1192 if (!e || e.initialize_entity)
1194 // make a proxy initializer entity
1198 e.classname = "initialize_entity";
1202 e.initialize_entity = func;
1203 e.initialize_entity_order = order;
1205 cur = initialize_entity_first;
1209 if (!cur || cur.initialize_entity_order > order)
1211 // insert between prev and cur
1213 prev.initialize_entity_next = e;
1215 initialize_entity_first = e;
1216 e.initialize_entity_next = cur;
1220 cur = cur.initialize_entity_next;
1223 void InitializeEntitiesRun()
1226 startoflist = initialize_entity_first;
1227 initialize_entity_first = world;
1228 remove = remove_except_protected;
1229 for (self = startoflist; self; self = self.initialize_entity_next)
1231 self.remove_except_protected_forbidden = 1;
1233 for (self = startoflist; self; )
1236 var void(void) func;
1237 e = self.initialize_entity_next;
1238 func = self.initialize_entity;
1239 self.initialize_entity_order = 0;
1240 self.initialize_entity = func_null;
1241 self.initialize_entity_next = world;
1242 self.remove_except_protected_forbidden = 0;
1243 if (self.classname == "initialize_entity")
1247 builtin_remove(self);
1250 //dprint("Delayed initialization: ", self.classname, "\n");
1256 backtrace(strcat("Null function in: ", self.classname, "\n"));
1260 remove = remove_unsafely;
1263 void UncustomizeEntitiesRun()
1267 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1268 self.uncustomizeentityforclient();
1271 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1273 e.customizeentityforclient = customizer;
1274 e.uncustomizeentityforclient = uncustomizer;
1275 e.uncustomizeentityforclient_set = !!uncustomizer;
1278 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1282 if (e.classname == "")
1283 e.classname = "net_linked";
1285 if (e.model == "" || self.modelindex == 0)
1289 setmodel(e, "null");
1293 e.SendEntity = sendfunc;
1294 e.SendFlags = 0xFFFFFF;
1297 e.effects |= EF_NODEPTHTEST;
1301 e.nextthink = time + dt;
1302 e.think = SUB_Remove;
1307 entity eliminatedPlayers;
1308 .float(entity) isEliminated;
1309 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1313 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1314 WriteByte(MSG_ENTITY, sendflags);
1318 for(i = 1; i <= maxclients; i += 8)
1320 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1322 if(eliminatedPlayers.isEliminated(e))
1325 WriteByte(MSG_ENTITY, f);
1332 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1334 if(eliminatedPlayers)
1336 backtrace("Can't spawn eliminatedPlayers again!");
1339 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1340 eliminatedPlayers.isEliminated = isEliminated_func;
1344 void adaptor_think2touch()
1353 void adaptor_think2use()
1365 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1367 if(!(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
1368 self.projectiledeathtype |= HITTYPE_SPLASH;
1369 adaptor_think2use();
1372 // deferred dropping
1373 void DropToFloor_Handler()
1375 builtin_droptofloor();
1376 self.dropped_origin = self.origin;
1381 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1386 float trace_hits_box_a0, trace_hits_box_a1;
1388 float trace_hits_box_1d(float end, float thmi, float thma)
1392 // just check if x is in range
1400 // do the trace with respect to x
1401 // 0 -> end has to stay in thmi -> thma
1402 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1403 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1404 if (trace_hits_box_a0 > trace_hits_box_a1)
1410 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1415 // now it is a trace from 0 to end
1417 trace_hits_box_a0 = 0;
1418 trace_hits_box_a1 = 1;
1420 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1422 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1424 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1430 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1432 return trace_hits_box(start, end, thmi - ma, thma - mi);
1435 float SUB_NoImpactCheck()
1437 // zero hitcontents = this is not the real impact, but either the
1438 // mirror-impact of something hitting the projectile instead of the
1439 // projectile hitting the something, or a touchareagrid one. Neither of
1440 // these stop the projectile from moving, so...
1441 if(trace_dphitcontents == 0)
1443 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1444 dprintf("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));
1447 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1449 if (other == world && self.size != '0 0 0')
1452 tic = self.velocity * sys_frametime;
1453 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1454 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1455 if (trace_fraction >= 1)
1457 dprint("Odd... did not hit...?\n");
1459 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1461 dprint("Detected and prevented the sky-grapple bug.\n");
1469 #define SUB_OwnerCheck() (other && (other == self.owner))
1471 void RemoveGrapplingHook(entity pl);
1472 void W_Crylink_Dequeue(entity e);
1473 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1475 if(SUB_OwnerCheck())
1477 if(SUB_NoImpactCheck())
1479 if(self.classname == "nade")
1480 return false; // no checks here
1481 else if(self.classname == "grapplinghook")
1482 RemoveGrapplingHook(self.realowner);
1483 else if(self.classname == "spike")
1485 W_Crylink_Dequeue(self);
1492 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1493 UpdateCSQCProjectile(self);
1498 void URI_Get_Callback(float id, float status, string data)
1500 if(url_URI_Get_Callback(id, status, data))
1504 else if (id == URI_GET_DISCARD)
1508 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1511 Curl_URI_Get_Callback(id, status, data);
1513 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1516 OnlineBanList_URI_Get_Callback(id, status, data);
1520 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1524 string uid2name(string myuid) {
1526 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1528 // FIXME remove this later after 0.6 release
1529 // convert old style broken records to correct style
1532 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1535 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1536 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1541 s = "^1Unregistered Player";
1545 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1548 vector start, org, delta, end, enddown, mstart;
1551 m = e.dphitcontentsmask;
1552 e.dphitcontentsmask = goodcontents | badcontents;
1555 delta = world.maxs - world.mins;
1559 for (i = 0; i < attempts; ++i)
1561 start.x = org.x + random() * delta.x;
1562 start.y = org.y + random() * delta.y;
1563 start.z = org.z + random() * delta.z;
1565 // rule 1: start inside world bounds, and outside
1566 // solid, and don't start from somewhere where you can
1567 // fall down to evil
1568 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1569 if (trace_fraction >= 1)
1571 if (trace_startsolid)
1573 if (trace_dphitcontents & badcontents)
1575 if (trace_dphitq3surfaceflags & badsurfaceflags)
1578 // rule 2: if we are too high, lower the point
1579 if (trace_fraction * delta.z > maxaboveground)
1580 start = trace_endpos + '0 0 1' * maxaboveground;
1581 enddown = trace_endpos;
1583 // rule 3: make sure we aren't outside the map. This only works
1584 // for somewhat well formed maps. A good rule of thumb is that
1585 // the map should have a convex outside hull.
1586 // these can be traceLINES as we already verified the starting box
1587 mstart = start + 0.5 * (e.mins + e.maxs);
1588 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1589 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1591 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1592 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1594 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1595 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1597 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1598 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1600 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1601 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1604 // rule 4: we must "see" some spawnpoint or item
1605 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1606 if(checkpvs(mstart, sp))
1607 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1611 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1612 if(checkpvs(mstart, sp))
1613 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1619 // find a random vector to "look at"
1620 end.x = org.x + random() * delta.x;
1621 end.y = org.y + random() * delta.y;
1622 end.z = org.z + random() * delta.z;
1623 end = start + normalize(end - start) * vlen(delta);
1625 // rule 4: start TO end must not be too short
1626 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1627 if (trace_startsolid)
1629 if (trace_fraction < minviewdistance / vlen(delta))
1632 // rule 5: don't want to look at sky
1633 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1636 // rule 6: we must not end up in trigger_hurt
1637 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1643 e.dphitcontentsmask = m;
1647 setorigin(e, start);
1648 e.angles = vectoangles(end - start);
1649 dprint("Needed ", ftos(i + 1), " attempts\n");
1656 void write_recordmarker(entity pl, float tstart, float dt)
1658 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1660 // also write a marker into demo files for demotc-race-record-extractor to find
1663 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1664 " ", ftos(tstart), " ", ftos(dt), "\n"));
1667 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1680 if(allowcenter) // 2: allow center handedness
1693 if(allowcenter) // 2: allow center handedness
1709 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1714 if (autocvar_g_shootfromeye)
1718 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1719 else { vecs.y = 0; vecs.z -= 2; }
1727 else if (autocvar_g_shootfromcenter)
1732 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1742 else if (autocvar_g_shootfromclient)
1744 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1749 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1751 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1755 void attach_sameorigin(entity e, entity to, string tag)
1757 vector org, t_forward, t_left, t_up, e_forward, e_up;
1760 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1761 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1762 t_forward = v_forward * tagscale;
1763 t_left = v_right * -tagscale;
1764 t_up = v_up * tagscale;
1766 e.origin_x = org * t_forward;
1767 e.origin_y = org * t_left;
1768 e.origin_z = org * t_up;
1770 // current forward and up directions
1771 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1772 e.angles = AnglesTransform_FromVAngles(e.angles);
1774 e.angles = AnglesTransform_FromAngles(e.angles);
1775 fixedmakevectors(e.angles);
1777 // untransform forward, up!
1778 e_forward.x = v_forward * t_forward;
1779 e_forward.y = v_forward * t_left;
1780 e_forward.z = v_forward * t_up;
1781 e_up.x = v_up * t_forward;
1782 e_up.y = v_up * t_left;
1783 e_up.z = v_up * t_up;
1785 e.angles = fixedvectoangles2(e_forward, e_up);
1786 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1787 e.angles = AnglesTransform_ToVAngles(e.angles);
1789 e.angles = AnglesTransform_ToAngles(e.angles);
1791 setattachment(e, to, tag);
1792 setorigin(e, e.origin);
1795 void detach_sameorigin(entity e)
1798 org = gettaginfo(e, 0);
1799 e.angles = fixedvectoangles2(v_forward, v_up);
1800 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1801 e.angles = AnglesTransform_ToVAngles(e.angles);
1803 e.angles = AnglesTransform_ToAngles(e.angles);
1805 setattachment(e, world, "");
1806 setorigin(e, e.origin);
1809 void follow_sameorigin(entity e, entity to)
1811 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1812 e.aiment = to; // make the hole follow bmodel
1813 e.punchangle = to.angles; // the original angles of bmodel
1814 e.view_ofs = e.origin - to.origin; // relative origin
1815 e.v_angle = e.angles - to.angles; // relative angles
1818 void unfollow_sameorigin(entity e)
1820 e.movetype = MOVETYPE_NONE;
1823 entity gettaginfo_relative_ent;
1824 vector gettaginfo_relative(entity e, float tag)
1826 if (!gettaginfo_relative_ent)
1828 gettaginfo_relative_ent = spawn();
1829 gettaginfo_relative_ent.effects = EF_NODRAW;
1831 gettaginfo_relative_ent.model = e.model;
1832 gettaginfo_relative_ent.modelindex = e.modelindex;
1833 gettaginfo_relative_ent.frame = e.frame;
1834 return gettaginfo(gettaginfo_relative_ent, tag);
1839 float modeleffect_SendEntity(entity to, float sf)
1842 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1845 if(self.velocity != '0 0 0')
1847 if(self.angles != '0 0 0')
1849 if(self.avelocity != '0 0 0')
1852 WriteByte(MSG_ENTITY, f);
1853 WriteShort(MSG_ENTITY, self.modelindex);
1854 WriteByte(MSG_ENTITY, self.skin);
1855 WriteByte(MSG_ENTITY, self.frame);
1856 WriteCoord(MSG_ENTITY, self.origin.x);
1857 WriteCoord(MSG_ENTITY, self.origin.y);
1858 WriteCoord(MSG_ENTITY, self.origin.z);
1861 WriteCoord(MSG_ENTITY, self.velocity.x);
1862 WriteCoord(MSG_ENTITY, self.velocity.y);
1863 WriteCoord(MSG_ENTITY, self.velocity.z);
1867 WriteCoord(MSG_ENTITY, self.angles.x);
1868 WriteCoord(MSG_ENTITY, self.angles.y);
1869 WriteCoord(MSG_ENTITY, self.angles.z);
1873 WriteCoord(MSG_ENTITY, self.avelocity.x);
1874 WriteCoord(MSG_ENTITY, self.avelocity.y);
1875 WriteCoord(MSG_ENTITY, self.avelocity.z);
1877 WriteShort(MSG_ENTITY, self.scale * 256.0);
1878 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1879 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1880 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1881 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1886 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)
1891 e.classname = "modeleffect";
1899 e.teleport_time = t1;
1903 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1907 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1910 sz = max(e.scale, e.scale2);
1911 setsize(e, e.mins * sz, e.maxs * sz);
1912 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1915 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1917 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1920 float randombit(float bits)
1922 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1931 for(f = 1; f <= bits; f *= 2)
1940 r = (r - 1) / (n - 1);
1947 float randombits(float bits, float k, float error_return)
1951 while(k > 0 && bits != r)
1953 r += randombit(bits - r);
1962 void randombit_test(float bits, float iter)
1966 print(ftos(randombit(bits)), "\n");
1971 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1973 if(halflifedist > 0)
1974 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1975 else if(halflifedist < 0)
1976 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1988 oself.think = SUB_Remove;
1989 oself.nextthink = time;
1995 Execute func() after time + fdelay.
1996 self when func is executed = self when defer is called
1998 void defer(float fdelay, void() func)
2005 e.think = defer_think;
2006 e.nextthink = time + fdelay;
2009 .string aiment_classname;
2010 .float aiment_deadflag;
2011 void SetMovetypeFollow(entity ent, entity e)
2013 // FIXME this may not be warpzone aware
2014 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2015 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.
2016 ent.aiment = e; // make the hole follow bmodel
2017 ent.punchangle = e.angles; // the original angles of bmodel
2018 ent.view_ofs = ent.origin - e.origin; // relative origin
2019 ent.v_angle = ent.angles - e.angles; // relative angles
2020 ent.aiment_classname = strzone(e.classname);
2021 ent.aiment_deadflag = e.deadflag;
2023 void UnsetMovetypeFollow(entity ent)
2025 ent.movetype = MOVETYPE_FLY;
2026 PROJECTILE_MAKETRIGGER(ent);
2029 float LostMovetypeFollow(entity ent)
2032 if(ent.movetype != MOVETYPE_FOLLOW)
2038 if(ent.aiment.classname != ent.aiment_classname)
2040 if(ent.aiment.deadflag != ent.aiment_deadflag)
2046 float isPushable(entity e)
2055 case "droppedweapon":
2056 case "keepawayball":
2057 case "nexball_basketball":
2058 case "nexball_football":
2060 case "bullet": // antilagged bullets can't hit this either
2063 if (e.projectiledeathtype)