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 "mutators/mutators_include.qh"
26 #include "tturrets/include/turrets_early.qh"
27 #include "../common/mapinfo.qh"
28 #include "command/common.qh"
29 #include "../csqcmodellib/sv_model.qh"
33 void crosshair_trace(entity pl)
35 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));
37 void crosshair_trace_plusvisibletriggers(entity pl)
41 first = findchainfloat(solid, SOLID_TRIGGER);
43 for (e = first; e; e = e.chain)
49 for (e = first; e; e = e.chain)
50 e.solid = SOLID_TRIGGER;
52 void WarpZone_crosshair_trace(entity pl)
54 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));
58 string admin_name(void)
60 if(autocvar_sv_adminnick != "")
61 return autocvar_sv_adminnick;
63 return "SERVER ADMIN";
66 void DistributeEvenly_Init(float amount, float totalweight)
68 if (DistributeEvenly_amount)
70 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
71 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
74 DistributeEvenly_amount = 0;
76 DistributeEvenly_amount = amount;
77 DistributeEvenly_totalweight = totalweight;
79 float DistributeEvenly_Get(float weight)
84 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
85 DistributeEvenly_totalweight -= weight;
86 DistributeEvenly_amount -= f;
89 float DistributeEvenly_GetRandomized(float weight)
94 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
95 DistributeEvenly_totalweight -= weight;
96 DistributeEvenly_amount -= f;
101 void GameLogEcho(string s)
106 if (autocvar_sv_eventlog_files)
111 matches = autocvar_sv_eventlog_files_counter + 1;
112 cvar_set("sv_eventlog_files_counter", itos(matches));
115 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
116 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
117 logfile = fopen(fn, FILE_APPEND);
118 fputs(logfile, ":logversion:3\n");
122 if (autocvar_sv_eventlog_files_timestamps)
123 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
125 fputs(logfile, strcat(s, "\n"));
128 if (autocvar_sv_eventlog_console)
137 // will be opened later
142 if (logfile_open && logfile >= 0)
149 entity findnearest(vector point, .string field, string value, vector axismod)
160 localhead = find(world, field, value);
163 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
164 dist = localhead.oldorigin;
166 dist = localhead.origin;
168 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
171 for (i = 0; i < num_nearest; ++i)
173 if (len < nearest_length[i])
177 // now i tells us where to insert at
178 // INSERTION SORT! YOU'VE SEEN IT! RUN!
179 if (i < NUM_NEAREST_ENTITIES)
181 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
183 nearest_length[j + 1] = nearest_length[j];
184 nearest_entity[j + 1] = nearest_entity[j];
186 nearest_length[i] = len;
187 nearest_entity[i] = localhead;
188 if (num_nearest < NUM_NEAREST_ENTITIES)
189 num_nearest = num_nearest + 1;
192 localhead = find(localhead, field, value);
195 // now use the first one from our list that we can see
196 for (i = 0; i < num_nearest; ++i)
198 traceline(point, nearest_entity[i].origin, true, world);
199 if (trace_fraction == 1)
203 dprint("Nearest point (");
204 dprint(nearest_entity[0].netname);
205 dprint(") is not visible, using a visible one.\n");
207 return nearest_entity[i];
211 if (num_nearest == 0)
214 dprint("Not seeing any location point, using nearest as fallback.\n");
216 dprint("Candidates were: ");
217 for(j = 0; j < num_nearest; ++j)
221 dprint(nearest_entity[j].netname);
226 return nearest_entity[0];
229 void spawnfunc_target_location()
231 self.classname = "target_location";
232 // location name in netname
233 // eventually support: count, teamgame selectors, line of sight?
236 void spawnfunc_info_location()
238 self.classname = "target_location";
239 self.message = self.netname;
242 string NearestLocation(vector p)
247 loc = findnearest(p, classname, "target_location", '1 1 1');
254 loc = findnearest(p, target, "###item###", '1 1 4');
261 string formatmessage(string msg)
272 WarpZone_crosshair_trace(self);
273 cursor = trace_endpos;
274 cursor_ent = trace_ent;
278 break; // too many replacements
281 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
282 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
295 replacement = substring(msg, p, 2);
296 escape = substring(msg, p + 1, 1);
300 else if (escape == "\\")
302 else if (escape == "n")
304 else if (escape == "a")
305 replacement = ftos(floor(self.armorvalue));
306 else if (escape == "h")
307 replacement = ftos(floor(self.health));
308 else if (escape == "l")
309 replacement = NearestLocation(self.origin);
310 else if (escape == "y")
311 replacement = NearestLocation(cursor);
312 else if (escape == "d")
313 replacement = NearestLocation(self.death_origin);
314 else if (escape == "w") {
318 wep = self.switchweapon;
321 replacement = WEP_NAME(wep);
322 } else if (escape == "W") {
323 if (self.items & IT_SHELLS) replacement = "shells";
324 else if (self.items & IT_NAILS) replacement = "bullets";
325 else if (self.items & IT_ROCKETS) replacement = "rockets";
326 else if (self.items & IT_CELLS) replacement = "cells";
327 else if (self.items & IT_PLASMA) replacement = "plasma";
328 else replacement = "batteries"; // ;)
329 } else if (escape == "x") {
330 replacement = cursor_ent.netname;
331 if (replacement == "" || !cursor_ent)
332 replacement = "nothing";
333 } else if (escape == "s")
334 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
335 else if (escape == "S")
336 replacement = ftos(vlen(self.velocity));
338 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
339 p = p + strlen(replacement);
344 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
345 return (value == 0) ? false : true;
354 >0: receives a cvar from name=argv(f) value=argv(f+1)
356 void GetCvars_handleString(string thisname, float f, .string field, string name)
361 strunzone(self.(field));
362 self.(field) = string_null;
366 if (thisname == name)
369 strunzone(self.(field));
370 self.(field) = strzone(argv(f + 1));
374 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
376 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
378 GetCvars_handleString(thisname, f, field, name);
379 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
380 if (thisname == name)
382 string s = func(strcat1(self.(field)));
383 if (s != self.(field))
385 strunzone(self.(field));
386 self.(field) = strzone(s);
390 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
397 if (thisname == name)
398 self.(field) = stof(argv(f + 1));
401 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
403 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
410 if (thisname == name)
414 self.(field) = stof(argv(f + 1));
423 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
426 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
429 o = W_FixWeaponOrder_ForceComplete(wo);
430 if(self.weaponorder_byimpulse)
432 strunzone(self.weaponorder_byimpulse);
433 self.weaponorder_byimpulse = string_null;
435 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
438 void GetCvars(float f)
440 string s = string_null;
443 s = strcat1(argv(f));
448 MUTATOR_CALLHOOK(GetCvars);
450 Notification_GetCvars();
452 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
453 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
454 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
455 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
456 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
457 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
458 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
459 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
465 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
466 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
467 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
468 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
469 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
470 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
471 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
472 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
473 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
474 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
475 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
477 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
478 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
480 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
481 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
482 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
483 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
484 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
486 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
489 if (s == "cl_weaponpriority")
490 self.switchweapon = w_getbestweapon(self);
491 if (s == "cl_allow_uidtracking")
492 PlayerStats_GameReport_AddPlayer(self);
496 // decolorizes and team colors the player name when needed
497 string playername(entity p)
500 if (teamplay && !intermission_running && IS_PLAYER(p))
502 t = Team_ColorCode(p.team);
503 return strcat(t, strdecolorize(p.netname));
509 vector randompos(vector m1, vector m2)
513 v.x = m2_x * random() + m1_x;
514 v.y = m2_y * random() + m1_y;
515 v.z = m2_z * random() + m1_z;
519 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
521 int i = weaponinfo.weapon;
527 if (g_lms || g_ca || allguns)
529 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
535 d = (i == WEP_SHOTGUN);
537 d = 0; // weapon is set a few lines later
539 d = !(!weaponinfo.weaponstart);
541 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
542 d |= (i == WEP_HOOK);
543 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
546 float t = weaponinfo.weaponstartoverride;
548 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
553 // 4: is set by default?
562 void readplayerstartcvars()
568 // initialize starting values for players
569 start_weapons = '0 0 0';
570 start_weapons_default = '0 0 0';
571 start_weapons_defaultmask = '0 0 0';
573 start_ammo_shells = 0;
574 start_ammo_nails = 0;
575 start_ammo_rockets = 0;
576 start_ammo_cells = 0;
577 start_ammo_plasma = 0;
578 start_health = cvar("g_balance_health_start");
579 start_armorvalue = cvar("g_balance_armor_start");
582 g_weaponarena_weapons = '0 0 0';
584 s = cvar_string("g_weaponarena");
585 if (s == "0" || s == "")
591 if (s == "0" || s == "")
597 // forcibly turn off weaponarena
599 else if (s == "all" || s == "1")
602 g_weaponarena_list = "All Weapons";
603 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
605 e = get_weaponinfo(j);
606 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
607 g_weaponarena_weapons |= WepSet_FromWeapon(j);
610 else if (s == "most")
613 g_weaponarena_list = "Most Weapons";
614 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
616 e = get_weaponinfo(j);
617 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
618 if (e.spawnflags & WEP_FLAG_NORMAL)
619 g_weaponarena_weapons |= WepSet_FromWeapon(j);
622 else if (s == "none")
625 g_weaponarena_list = "No Weapons";
630 t = tokenize_console(s);
631 g_weaponarena_list = "";
632 for (i = 0; i < t; ++i)
635 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
637 e = get_weaponinfo(j);
640 g_weaponarena_weapons |= WepSet_FromWeapon(j);
641 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
647 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
650 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
654 g_weaponarena_random = cvar("g_weaponarena_random");
656 g_weaponarena_random = 0;
657 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
661 g_weapon_stay = 0; // incompatible
662 start_weapons = g_weaponarena_weapons;
663 start_items |= IT_UNLIMITED_AMMO;
667 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
669 e = get_weaponinfo(i);
670 int w = want_weapon(e, false);
672 start_weapons |= WepSet_FromWeapon(i);
674 start_weapons_default |= WepSet_FromWeapon(i);
676 start_weapons_defaultmask |= WepSet_FromWeapon(i);
680 if(!cvar("g_use_ammunition"))
681 start_items |= IT_UNLIMITED_AMMO;
683 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
685 start_ammo_shells = 999;
686 start_ammo_nails = 999;
687 start_ammo_rockets = 999;
688 start_ammo_cells = 999;
689 start_ammo_plasma = 999;
690 start_ammo_fuel = 999;
694 start_ammo_shells = cvar("g_start_ammo_shells");
695 start_ammo_nails = cvar("g_start_ammo_nails");
696 start_ammo_rockets = cvar("g_start_ammo_rockets");
697 start_ammo_cells = cvar("g_start_ammo_cells");
698 start_ammo_plasma = cvar("g_start_ammo_plasma");
699 start_ammo_fuel = cvar("g_start_ammo_fuel");
704 warmup_start_ammo_shells = start_ammo_shells;
705 warmup_start_ammo_nails = start_ammo_nails;
706 warmup_start_ammo_rockets = start_ammo_rockets;
707 warmup_start_ammo_cells = start_ammo_cells;
708 warmup_start_ammo_plasma = start_ammo_plasma;
709 warmup_start_ammo_fuel = start_ammo_fuel;
710 warmup_start_health = start_health;
711 warmup_start_armorvalue = start_armorvalue;
712 warmup_start_weapons = start_weapons;
713 warmup_start_weapons_default = start_weapons_default;
714 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
716 if (!g_weaponarena && !g_ca)
718 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
719 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
720 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
721 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
722 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
723 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
724 warmup_start_health = cvar("g_warmup_start_health");
725 warmup_start_armorvalue = cvar("g_warmup_start_armor");
726 warmup_start_weapons = '0 0 0';
727 warmup_start_weapons_default = '0 0 0';
728 warmup_start_weapons_defaultmask = '0 0 0';
729 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
731 e = get_weaponinfo(i);
732 int w = want_weapon(e, g_warmup_allguns);
734 warmup_start_weapons |= WepSet_FromWeapon(i);
736 warmup_start_weapons_default |= WepSet_FromWeapon(i);
738 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
744 start_items |= IT_JETPACK;
746 MUTATOR_CALLHOOK(SetStartItems);
748 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
750 start_items |= IT_FUEL_REGEN;
751 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
752 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
755 WepSet precache_weapons = start_weapons;
756 if (g_warmup_allguns != 1)
757 precache_weapons |= warmup_start_weapons;
758 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
760 e = get_weaponinfo(i);
761 if(precache_weapons & WepSet_FromWeapon(i))
762 WEP_ACTION(i, WR_INIT);
765 start_ammo_shells = max(0, start_ammo_shells);
766 start_ammo_nails = max(0, start_ammo_nails);
767 start_ammo_rockets = max(0, start_ammo_rockets);
768 start_ammo_cells = max(0, start_ammo_cells);
769 start_ammo_plasma = max(0, start_ammo_plasma);
770 start_ammo_fuel = max(0, start_ammo_fuel);
772 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
773 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
774 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
775 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
776 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
777 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
780 float sound_allowed(float _dest, entity e)
782 // sounds from world may always pass
785 if (e.classname == "body")
787 else if (e.realowner && e.realowner != e)
789 else if (e.owner && e.owner != e)
794 // sounds to self may always pass
795 if (_dest == MSG_ONE)
798 // sounds by players can be removed
799 if (autocvar_bot_sound_monopoly)
800 if (IS_REAL_CLIENT(e))
802 // anything else may pass
807 void sound(entity e, float chan, string samp, float vol, float _atten)
809 if (!sound_allowed(MSG_BROADCAST, e))
811 sound7(e, chan, samp, vol, _atten, 0, 0);
814 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
818 if (!sound_allowed(_dest, e))
821 entno = num_for_edict(e);
822 idx = precache_sound_index(samp);
827 _atten = floor(_atten * 64);
828 vol = floor(vol * 255);
831 sflags |= SND_VOLUME;
833 sflags |= SND_ATTENUATION;
834 if (entno >= 8192 || chan < 0 || chan > 7)
835 sflags |= SND_LARGEENTITY;
837 sflags |= SND_LARGESOUND;
839 WriteByte(_dest, SVC_SOUND);
840 WriteByte(_dest, sflags);
841 if (sflags & SND_VOLUME)
842 WriteByte(_dest, vol);
843 if (sflags & SND_ATTENUATION)
844 WriteByte(_dest, _atten);
845 if (sflags & SND_LARGEENTITY)
847 WriteShort(_dest, entno);
848 WriteByte(_dest, chan);
852 WriteShort(_dest, entno * 8 + chan);
854 if (sflags & SND_LARGESOUND)
855 WriteShort(_dest, idx);
857 WriteByte(_dest, idx);
859 WriteCoord(_dest, o.x);
860 WriteCoord(_dest, o.y);
861 WriteCoord(_dest, o.z);
863 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
867 if (!sound_allowed(_dest, e))
870 o = e.origin + 0.5 * (e.mins + e.maxs);
871 soundtoat(_dest, e, o, chan, samp, vol, _atten);
873 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
875 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
877 void stopsoundto(float _dest, entity e, float chan)
881 if (!sound_allowed(_dest, e))
884 entno = num_for_edict(e);
886 if (entno >= 8192 || chan < 0 || chan > 7)
889 idx = precache_sound_index("misc/null.wav");
890 sflags = SND_LARGEENTITY;
892 sflags |= SND_LARGESOUND;
893 WriteByte(_dest, SVC_SOUND);
894 WriteByte(_dest, sflags);
895 WriteShort(_dest, entno);
896 WriteByte(_dest, chan);
897 if (sflags & SND_LARGESOUND)
898 WriteShort(_dest, idx);
900 WriteByte(_dest, idx);
901 WriteCoord(_dest, e.origin.x);
902 WriteCoord(_dest, e.origin.y);
903 WriteCoord(_dest, e.origin.z);
907 WriteByte(_dest, SVC_STOPSOUND);
908 WriteShort(_dest, entno * 8 + chan);
911 void stopsound(entity e, float chan)
913 if (!sound_allowed(MSG_BROADCAST, e))
916 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
917 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
920 void play2(entity e, string filename)
922 //stuffcmd(e, strcat("play2 ", filename, "\n"));
924 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
927 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
929 float spamsound(entity e, float chan, string samp, float vol, float _atten)
931 if (!sound_allowed(MSG_BROADCAST, e))
934 if (time > e.spamtime)
937 sound(e, chan, samp, vol, _atten);
943 void play2team(float t, string filename)
947 if (autocvar_bot_sound_monopoly)
950 FOR_EACH_REALPLAYER(head)
953 play2(head, filename);
957 void play2all(string samp)
959 if (autocvar_bot_sound_monopoly)
962 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
965 void PrecachePlayerSounds(string f);
966 void precache_playermodel(string m)
968 float globhandle, i, n;
971 if(substring(m, -9,5) == "_lod1")
973 if(substring(m, -9,5) == "_lod2")
976 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
979 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
983 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
986 n = search_getsize(globhandle);
987 for (i = 0; i < n; ++i)
989 //print(search_getfilename(globhandle, i), "\n");
990 f = search_getfilename(globhandle, i);
991 PrecachePlayerSounds(f);
993 search_end(globhandle);
995 void precache_all_playermodels(string pattern)
997 float globhandle, i, n;
1000 globhandle = search_begin(pattern, true, false);
1003 n = search_getsize(globhandle);
1004 for (i = 0; i < n; ++i)
1006 //print(search_getfilename(globhandle, i), "\n");
1007 f = search_getfilename(globhandle, i);
1008 precache_playermodel(f);
1010 search_end(globhandle);
1015 // gamemode related things
1016 precache_model ("models/misc/chatbubble.spr");
1017 precache_model("models/ice/ice.md3");
1019 #ifdef TTURRETS_ENABLED
1020 if (autocvar_g_turrets)
1024 // Precache all player models if desired
1025 if (autocvar_sv_precacheplayermodels)
1027 PrecachePlayerSounds("sound/player/default.sounds");
1028 precache_all_playermodels("models/player/*.zym");
1029 precache_all_playermodels("models/player/*.dpm");
1030 precache_all_playermodels("models/player/*.md3");
1031 precache_all_playermodels("models/player/*.psk");
1032 precache_all_playermodels("models/player/*.iqm");
1035 if (autocvar_sv_defaultcharacter)
1038 s = autocvar_sv_defaultplayermodel_red;
1040 precache_playermodel(s);
1041 s = autocvar_sv_defaultplayermodel_blue;
1043 precache_playermodel(s);
1044 s = autocvar_sv_defaultplayermodel_yellow;
1046 precache_playermodel(s);
1047 s = autocvar_sv_defaultplayermodel_pink;
1049 precache_playermodel(s);
1050 s = autocvar_sv_defaultplayermodel;
1052 precache_playermodel(s);
1057 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1058 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1061 // gore and miscellaneous sounds
1062 //precache_sound ("misc/h2ohit.wav");
1063 precache_model ("models/hook.md3");
1064 precache_sound ("misc/armorimpact.wav");
1065 precache_sound ("misc/bodyimpact1.wav");
1066 precache_sound ("misc/bodyimpact2.wav");
1067 precache_sound ("misc/gib.wav");
1068 precache_sound ("misc/gib_splat01.wav");
1069 precache_sound ("misc/gib_splat02.wav");
1070 precache_sound ("misc/gib_splat03.wav");
1071 precache_sound ("misc/gib_splat04.wav");
1072 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1073 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1074 precache_sound ("misc/null.wav");
1075 precache_sound ("misc/spawn.wav");
1076 precache_sound ("misc/talk.wav");
1077 precache_sound ("misc/teleport.wav");
1078 precache_sound ("misc/poweroff.wav");
1079 precache_sound ("player/lava.wav");
1080 precache_sound ("player/slime.wav");
1082 precache_model ("models/sprites/0.spr32");
1083 precache_model ("models/sprites/1.spr32");
1084 precache_model ("models/sprites/2.spr32");
1085 precache_model ("models/sprites/3.spr32");
1086 precache_model ("models/sprites/4.spr32");
1087 precache_model ("models/sprites/5.spr32");
1088 precache_model ("models/sprites/6.spr32");
1089 precache_model ("models/sprites/7.spr32");
1090 precache_model ("models/sprites/8.spr32");
1091 precache_model ("models/sprites/9.spr32");
1092 precache_model ("models/sprites/10.spr32");
1094 // common weapon precaches
1095 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1096 precache_sound ("weapons/weapon_switch.wav");
1097 precache_sound ("weapons/weaponpickup.wav");
1098 precache_sound ("weapons/unavailable.wav");
1099 precache_sound ("weapons/dryfire.wav");
1100 if (g_grappling_hook)
1102 precache_sound ("weapons/hook_fire.wav"); // hook
1103 precache_sound ("weapons/hook_impact.wav"); // hook
1106 precache_model("models/elaser.mdl");
1107 precache_model("models/laser.mdl");
1108 precache_model("models/ebomb.mdl");
1111 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1113 if (!self.noise && self.music) // quake 3 uses the music field
1114 self.noise = self.music;
1116 // plays music for the level if there is any
1119 precache_sound (self.noise);
1120 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1124 #include "precache-for-csqc.inc"
1128 void make_safe_for_remove(entity e)
1130 if (e.initialize_entity)
1132 entity ent, prev = world;
1133 for (ent = initialize_entity_first; ent; )
1135 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1137 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1138 // skip it in linked list
1141 prev.initialize_entity_next = ent.initialize_entity_next;
1142 ent = prev.initialize_entity_next;
1146 initialize_entity_first = ent.initialize_entity_next;
1147 ent = initialize_entity_first;
1153 ent = ent.initialize_entity_next;
1159 void objerror(string s)
1161 make_safe_for_remove(self);
1162 builtin_objerror(s);
1165 .float remove_except_protected_forbidden;
1166 void remove_except_protected(entity e)
1168 if(e.remove_except_protected_forbidden)
1169 error("not allowed to remove this at this point");
1173 void remove_unsafely(entity e)
1175 if(e.classname == "spike")
1176 error("Removing spikes is forbidden (crylink bug), please report");
1180 void remove_safely(entity e)
1182 make_safe_for_remove(e);
1186 void InitializeEntity(entity e, void(void) func, float order)
1190 if (!e || e.initialize_entity)
1192 // make a proxy initializer entity
1196 e.classname = "initialize_entity";
1200 e.initialize_entity = func;
1201 e.initialize_entity_order = order;
1203 cur = initialize_entity_first;
1207 if (!cur || cur.initialize_entity_order > order)
1209 // insert between prev and cur
1211 prev.initialize_entity_next = e;
1213 initialize_entity_first = e;
1214 e.initialize_entity_next = cur;
1218 cur = cur.initialize_entity_next;
1221 void InitializeEntitiesRun()
1224 startoflist = initialize_entity_first;
1225 initialize_entity_first = world;
1226 remove = remove_except_protected;
1227 for (self = startoflist; self; self = self.initialize_entity_next)
1229 self.remove_except_protected_forbidden = 1;
1231 for (self = startoflist; self; )
1234 var void(void) func;
1235 e = self.initialize_entity_next;
1236 func = self.initialize_entity;
1237 self.initialize_entity_order = 0;
1238 self.initialize_entity = func_null;
1239 self.initialize_entity_next = world;
1240 self.remove_except_protected_forbidden = 0;
1241 if (self.classname == "initialize_entity")
1245 builtin_remove(self);
1248 //dprint("Delayed initialization: ", self.classname, "\n");
1254 backtrace(strcat("Null function in: ", self.classname, "\n"));
1258 remove = remove_unsafely;
1261 void UncustomizeEntitiesRun()
1265 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1266 self.uncustomizeentityforclient();
1269 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1271 e.customizeentityforclient = customizer;
1272 e.uncustomizeentityforclient = uncustomizer;
1273 e.uncustomizeentityforclient_set = !!uncustomizer;
1277 void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) sendfunc)
1281 if (e.classname == "")
1282 e.classname = "net_linked";
1284 if (e.model == "" || self.modelindex == 0)
1288 setmodel(e, "null");
1292 e.SendEntity = sendfunc;
1293 e.SendFlags = 0xFFFFFF;
1296 e.effects |= EF_NODEPTHTEST;
1300 e.nextthink = time + dt;
1301 e.think = SUB_Remove;
1306 entity eliminatedPlayers;
1307 .float(entity) isEliminated;
1308 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1312 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1313 WriteByte(MSG_ENTITY, sendflags);
1317 for(i = 1; i <= maxclients; i += 8)
1319 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1321 if(eliminatedPlayers.isEliminated(e))
1324 WriteByte(MSG_ENTITY, f);
1331 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1333 if(eliminatedPlayers)
1335 backtrace("Can't spawn eliminatedPlayers again!");
1338 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1339 eliminatedPlayers.isEliminated = isEliminated_func;
1343 void adaptor_think2touch()
1352 void adaptor_think2use()
1364 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1366 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
1367 self.projectiledeathtype |= HITTYPE_SPLASH;
1368 adaptor_think2use();
1371 // deferred dropping
1372 void DropToFloor_Handler()
1374 builtin_droptofloor();
1375 self.dropped_origin = self.origin;
1380 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1385 float trace_hits_box_a0, trace_hits_box_a1;
1387 float trace_hits_box_1d(float end, float thmi, float thma)
1391 // just check if x is in range
1399 // do the trace with respect to x
1400 // 0 -> end has to stay in thmi -> thma
1401 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1402 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1403 if (trace_hits_box_a0 > trace_hits_box_a1)
1409 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1414 // now it is a trace from 0 to end
1416 trace_hits_box_a0 = 0;
1417 trace_hits_box_a1 = 1;
1419 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1421 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1423 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1429 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1431 return trace_hits_box(start, end, thmi - ma, thma - mi);
1434 float SUB_NoImpactCheck()
1436 // zero hitcontents = this is not the real impact, but either the
1437 // mirror-impact of something hitting the projectile instead of the
1438 // projectile hitting the something, or a touchareagrid one. Neither of
1439 // these stop the projectile from moving, so...
1440 if(trace_dphitcontents == 0)
1442 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1443 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));
1446 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1448 if (other == world && self.size != '0 0 0')
1451 tic = self.velocity * sys_frametime;
1452 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1453 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1454 if (trace_fraction >= 1)
1456 dprint("Odd... did not hit...?\n");
1458 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1460 dprint("Detected and prevented the sky-grapple bug.\n");
1468 #define SUB_OwnerCheck() (other && (other == self.owner))
1470 void RemoveGrapplingHook(entity pl);
1471 void W_Crylink_Dequeue(entity e);
1472 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1474 if(SUB_OwnerCheck())
1476 if(SUB_NoImpactCheck())
1478 if(self.classname == "nade")
1479 return false; // no checks here
1480 else if(self.classname == "grapplinghook")
1481 RemoveGrapplingHook(self.realowner);
1482 else if(self.classname == "spike")
1484 W_Crylink_Dequeue(self);
1491 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1492 UpdateCSQCProjectile(self);
1497 void URI_Get_Callback(float id, float status, string data)
1499 if(url_URI_Get_Callback(id, status, data))
1503 else if (id == URI_GET_DISCARD)
1507 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1510 Curl_URI_Get_Callback(id, status, data);
1512 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1515 OnlineBanList_URI_Get_Callback(id, status, data);
1519 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1523 string uid2name(string myuid) {
1525 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1527 // FIXME remove this later after 0.6 release
1528 // convert old style broken records to correct style
1531 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1534 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1535 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1540 s = "^1Unregistered Player";
1544 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1547 vector start, org, delta, end, enddown, mstart;
1550 m = e.dphitcontentsmask;
1551 e.dphitcontentsmask = goodcontents | badcontents;
1554 delta = world.maxs - world.mins;
1558 for (i = 0; i < attempts; ++i)
1560 start.x = org.x + random() * delta.x;
1561 start.y = org.y + random() * delta.y;
1562 start.z = org.z + random() * delta.z;
1564 // rule 1: start inside world bounds, and outside
1565 // solid, and don't start from somewhere where you can
1566 // fall down to evil
1567 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1568 if (trace_fraction >= 1)
1570 if (trace_startsolid)
1572 if (trace_dphitcontents & badcontents)
1574 if (trace_dphitq3surfaceflags & badsurfaceflags)
1577 // rule 2: if we are too high, lower the point
1578 if (trace_fraction * delta.z > maxaboveground)
1579 start = trace_endpos + '0 0 1' * maxaboveground;
1580 enddown = trace_endpos;
1582 // rule 3: make sure we aren't outside the map. This only works
1583 // for somewhat well formed maps. A good rule of thumb is that
1584 // the map should have a convex outside hull.
1585 // these can be traceLINES as we already verified the starting box
1586 mstart = start + 0.5 * (e.mins + e.maxs);
1587 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1588 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1590 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1591 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1593 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1594 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1596 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1597 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1599 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1600 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1603 // rule 4: we must "see" some spawnpoint or item
1604 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1605 if(checkpvs(mstart, sp))
1606 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1610 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1611 if(checkpvs(mstart, sp))
1612 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1618 // find a random vector to "look at"
1619 end.x = org.x + random() * delta.x;
1620 end.y = org.y + random() * delta.y;
1621 end.z = org.z + random() * delta.z;
1622 end = start + normalize(end - start) * vlen(delta);
1624 // rule 4: start TO end must not be too short
1625 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1626 if (trace_startsolid)
1628 if (trace_fraction < minviewdistance / vlen(delta))
1631 // rule 5: don't want to look at sky
1632 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1635 // rule 6: we must not end up in trigger_hurt
1636 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1642 e.dphitcontentsmask = m;
1646 setorigin(e, start);
1647 e.angles = vectoangles(end - start);
1648 dprint("Needed ", ftos(i + 1), " attempts\n");
1655 void write_recordmarker(entity pl, float tstart, float dt)
1657 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1659 // also write a marker into demo files for demotc-race-record-extractor to find
1662 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1663 " ", ftos(tstart), " ", ftos(dt), "\n"));
1666 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1679 if(allowcenter) // 2: allow center handedness
1692 if(allowcenter) // 2: allow center handedness
1708 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1713 if (autocvar_g_shootfromeye)
1717 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1718 else { vecs.y = 0; vecs.z -= 2; }
1726 else if (autocvar_g_shootfromcenter)
1731 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1741 else if (autocvar_g_shootfromclient)
1743 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1748 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1750 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1754 void attach_sameorigin(entity e, entity to, string tag)
1756 vector org, t_forward, t_left, t_up, e_forward, e_up;
1759 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1760 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1761 t_forward = v_forward * tagscale;
1762 t_left = v_right * -tagscale;
1763 t_up = v_up * tagscale;
1765 e.origin_x = org * t_forward;
1766 e.origin_y = org * t_left;
1767 e.origin_z = org * t_up;
1769 // current forward and up directions
1770 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1771 e.angles = AnglesTransform_FromVAngles(e.angles);
1773 e.angles = AnglesTransform_FromAngles(e.angles);
1774 fixedmakevectors(e.angles);
1776 // untransform forward, up!
1777 e_forward.x = v_forward * t_forward;
1778 e_forward.y = v_forward * t_left;
1779 e_forward.z = v_forward * t_up;
1780 e_up.x = v_up * t_forward;
1781 e_up.y = v_up * t_left;
1782 e_up.z = v_up * t_up;
1784 e.angles = fixedvectoangles2(e_forward, e_up);
1785 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1786 e.angles = AnglesTransform_ToVAngles(e.angles);
1788 e.angles = AnglesTransform_ToAngles(e.angles);
1790 setattachment(e, to, tag);
1791 setorigin(e, e.origin);
1794 void detach_sameorigin(entity e)
1797 org = gettaginfo(e, 0);
1798 e.angles = fixedvectoangles2(v_forward, v_up);
1799 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1800 e.angles = AnglesTransform_ToVAngles(e.angles);
1802 e.angles = AnglesTransform_ToAngles(e.angles);
1804 setattachment(e, world, "");
1805 setorigin(e, e.origin);
1808 void follow_sameorigin(entity e, entity to)
1810 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1811 e.aiment = to; // make the hole follow bmodel
1812 e.punchangle = to.angles; // the original angles of bmodel
1813 e.view_ofs = e.origin - to.origin; // relative origin
1814 e.v_angle = e.angles - to.angles; // relative angles
1817 void unfollow_sameorigin(entity e)
1819 e.movetype = MOVETYPE_NONE;
1822 entity gettaginfo_relative_ent;
1823 vector gettaginfo_relative(entity e, float tag)
1825 if (!gettaginfo_relative_ent)
1827 gettaginfo_relative_ent = spawn();
1828 gettaginfo_relative_ent.effects = EF_NODRAW;
1830 gettaginfo_relative_ent.model = e.model;
1831 gettaginfo_relative_ent.modelindex = e.modelindex;
1832 gettaginfo_relative_ent.frame = e.frame;
1833 return gettaginfo(gettaginfo_relative_ent, tag);
1838 float modeleffect_SendEntity(entity to, int sf)
1841 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1844 if(self.velocity != '0 0 0')
1846 if(self.angles != '0 0 0')
1848 if(self.avelocity != '0 0 0')
1851 WriteByte(MSG_ENTITY, f);
1852 WriteShort(MSG_ENTITY, self.modelindex);
1853 WriteByte(MSG_ENTITY, self.skin);
1854 WriteByte(MSG_ENTITY, self.frame);
1855 WriteCoord(MSG_ENTITY, self.origin.x);
1856 WriteCoord(MSG_ENTITY, self.origin.y);
1857 WriteCoord(MSG_ENTITY, self.origin.z);
1860 WriteCoord(MSG_ENTITY, self.velocity.x);
1861 WriteCoord(MSG_ENTITY, self.velocity.y);
1862 WriteCoord(MSG_ENTITY, self.velocity.z);
1866 WriteCoord(MSG_ENTITY, self.angles.x);
1867 WriteCoord(MSG_ENTITY, self.angles.y);
1868 WriteCoord(MSG_ENTITY, self.angles.z);
1872 WriteCoord(MSG_ENTITY, self.avelocity.x);
1873 WriteCoord(MSG_ENTITY, self.avelocity.y);
1874 WriteCoord(MSG_ENTITY, self.avelocity.z);
1876 WriteShort(MSG_ENTITY, self.scale * 256.0);
1877 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1878 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1879 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1880 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1885 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)
1890 e.classname = "modeleffect";
1898 e.teleport_time = t1;
1902 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1906 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1909 sz = max(e.scale, e.scale2);
1910 setsize(e, e.mins * sz, e.maxs * sz);
1911 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1914 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1916 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1919 float randombit(float bits)
1921 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1930 for(f = 1; f <= bits; f *= 2)
1939 r = (r - 1) / (n - 1);
1946 float randombits(float bits, float k, float error_return)
1950 while(k > 0 && bits != r)
1952 r += randombit(bits - r);
1961 void randombit_test(float bits, float iter)
1965 print(ftos(randombit(bits)), "\n");
1970 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1972 if(halflifedist > 0)
1973 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1974 else if(halflifedist < 0)
1975 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1987 oself.think = SUB_Remove;
1988 oself.nextthink = time;
1994 Execute func() after time + fdelay.
1995 self when func is executed = self when defer is called
1997 void defer(float fdelay, void() func)
2004 e.think = defer_think;
2005 e.nextthink = time + fdelay;
2008 .string aiment_classname;
2009 .float aiment_deadflag;
2010 void SetMovetypeFollow(entity ent, entity e)
2012 // FIXME this may not be warpzone aware
2013 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2014 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.
2015 ent.aiment = e; // make the hole follow bmodel
2016 ent.punchangle = e.angles; // the original angles of bmodel
2017 ent.view_ofs = ent.origin - e.origin; // relative origin
2018 ent.v_angle = ent.angles - e.angles; // relative angles
2019 ent.aiment_classname = strzone(e.classname);
2020 ent.aiment_deadflag = e.deadflag;
2022 void UnsetMovetypeFollow(entity ent)
2024 ent.movetype = MOVETYPE_FLY;
2025 PROJECTILE_MAKETRIGGER(ent);
2028 float LostMovetypeFollow(entity ent)
2031 if(ent.movetype != MOVETYPE_FOLLOW)
2037 if(ent.aiment.classname != ent.aiment_classname)
2039 if(ent.aiment.deadflag != ent.aiment_deadflag)
2045 float isPushable(entity e)
2054 case "droppedweapon":
2055 case "keepawayball":
2056 case "nexball_basketball":
2057 case "nexball_football":
2059 case "bullet": // antilagged bullets can't hit this either
2062 if (e.projectiledeathtype)