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", ftos(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)
383 s = func(strcat1(self.field));
386 strunzone(self.field);
387 self.field = strzone(s);
391 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
398 if (thisname == name)
399 self.field = stof(argv(f + 1));
402 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
404 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
411 if (thisname == name)
415 self.field = stof(argv(f + 1));
424 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
427 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
430 o = W_FixWeaponOrder_ForceComplete(wo);
431 if(self.weaponorder_byimpulse)
433 strunzone(self.weaponorder_byimpulse);
434 self.weaponorder_byimpulse = string_null;
436 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
439 void GetCvars(float f)
441 string s = string_null;
444 s = strcat1(argv(f));
449 MUTATOR_CALLHOOK(GetCvars);
451 Notification_GetCvars();
453 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
454 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
455 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
456 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
457 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
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 _dest, 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 (_dest == 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 _atten)
811 if (!sound_allowed(MSG_BROADCAST, e))
813 sound7(e, chan, samp, vol, _atten, 0, 0);
816 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
820 if (!sound_allowed(_dest, e))
823 entno = num_for_edict(e);
824 idx = precache_sound_index(samp);
829 _atten = floor(_atten * 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, _atten);
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;
1279 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1283 if (e.classname == "")
1284 e.classname = "net_linked";
1286 if (e.model == "" || self.modelindex == 0)
1290 setmodel(e, "null");
1294 e.SendEntity = sendfunc;
1295 e.SendFlags = 0xFFFFFF;
1298 e.effects |= EF_NODEPTHTEST;
1302 e.nextthink = time + dt;
1303 e.think = SUB_Remove;
1308 entity eliminatedPlayers;
1309 .float(entity) isEliminated;
1310 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1314 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1315 WriteByte(MSG_ENTITY, sendflags);
1319 for(i = 1; i <= maxclients; i += 8)
1321 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1323 if(eliminatedPlayers.isEliminated(e))
1326 WriteByte(MSG_ENTITY, f);
1333 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1335 if(eliminatedPlayers)
1337 backtrace("Can't spawn eliminatedPlayers again!");
1340 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1341 eliminatedPlayers.isEliminated = isEliminated_func;
1345 void adaptor_think2touch()
1354 void adaptor_think2use()
1366 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1368 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
1369 self.projectiledeathtype |= HITTYPE_SPLASH;
1370 adaptor_think2use();
1373 // deferred dropping
1374 void DropToFloor_Handler()
1376 builtin_droptofloor();
1377 self.dropped_origin = self.origin;
1382 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1387 float trace_hits_box_a0, trace_hits_box_a1;
1389 float trace_hits_box_1d(float end, float thmi, float thma)
1393 // just check if x is in range
1401 // do the trace with respect to x
1402 // 0 -> end has to stay in thmi -> thma
1403 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1404 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1405 if (trace_hits_box_a0 > trace_hits_box_a1)
1411 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1416 // now it is a trace from 0 to end
1418 trace_hits_box_a0 = 0;
1419 trace_hits_box_a1 = 1;
1421 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1423 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1425 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1431 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1433 return trace_hits_box(start, end, thmi - ma, thma - mi);
1436 float SUB_NoImpactCheck()
1438 // zero hitcontents = this is not the real impact, but either the
1439 // mirror-impact of something hitting the projectile instead of the
1440 // projectile hitting the something, or a touchareagrid one. Neither of
1441 // these stop the projectile from moving, so...
1442 if(trace_dphitcontents == 0)
1444 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1445 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));
1448 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1450 if (other == world && self.size != '0 0 0')
1453 tic = self.velocity * sys_frametime;
1454 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1455 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1456 if (trace_fraction >= 1)
1458 dprint("Odd... did not hit...?\n");
1460 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1462 dprint("Detected and prevented the sky-grapple bug.\n");
1470 #define SUB_OwnerCheck() (other && (other == self.owner))
1472 void RemoveGrapplingHook(entity pl);
1473 void W_Crylink_Dequeue(entity e);
1474 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1476 if(SUB_OwnerCheck())
1478 if(SUB_NoImpactCheck())
1480 if(self.classname == "nade")
1481 return false; // no checks here
1482 else if(self.classname == "grapplinghook")
1483 RemoveGrapplingHook(self.realowner);
1484 else if(self.classname == "spike")
1486 W_Crylink_Dequeue(self);
1493 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1494 UpdateCSQCProjectile(self);
1499 void URI_Get_Callback(float id, float status, string data)
1501 if(url_URI_Get_Callback(id, status, data))
1505 else if (id == URI_GET_DISCARD)
1509 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1512 Curl_URI_Get_Callback(id, status, data);
1514 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1517 OnlineBanList_URI_Get_Callback(id, status, data);
1521 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1525 string uid2name(string myuid) {
1527 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1529 // FIXME remove this later after 0.6 release
1530 // convert old style broken records to correct style
1533 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1536 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1537 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1542 s = "^1Unregistered Player";
1546 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1549 vector start, org, delta, end, enddown, mstart;
1552 m = e.dphitcontentsmask;
1553 e.dphitcontentsmask = goodcontents | badcontents;
1556 delta = world.maxs - world.mins;
1560 for (i = 0; i < attempts; ++i)
1562 start.x = org.x + random() * delta.x;
1563 start.y = org.y + random() * delta.y;
1564 start.z = org.z + random() * delta.z;
1566 // rule 1: start inside world bounds, and outside
1567 // solid, and don't start from somewhere where you can
1568 // fall down to evil
1569 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1570 if (trace_fraction >= 1)
1572 if (trace_startsolid)
1574 if (trace_dphitcontents & badcontents)
1576 if (trace_dphitq3surfaceflags & badsurfaceflags)
1579 // rule 2: if we are too high, lower the point
1580 if (trace_fraction * delta.z > maxaboveground)
1581 start = trace_endpos + '0 0 1' * maxaboveground;
1582 enddown = trace_endpos;
1584 // rule 3: make sure we aren't outside the map. This only works
1585 // for somewhat well formed maps. A good rule of thumb is that
1586 // the map should have a convex outside hull.
1587 // these can be traceLINES as we already verified the starting box
1588 mstart = start + 0.5 * (e.mins + e.maxs);
1589 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1590 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1592 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1593 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1595 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1596 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1598 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1599 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1601 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1602 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1605 // rule 4: we must "see" some spawnpoint or item
1606 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1607 if(checkpvs(mstart, sp))
1608 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1612 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1613 if(checkpvs(mstart, sp))
1614 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1620 // find a random vector to "look at"
1621 end.x = org.x + random() * delta.x;
1622 end.y = org.y + random() * delta.y;
1623 end.z = org.z + random() * delta.z;
1624 end = start + normalize(end - start) * vlen(delta);
1626 // rule 4: start TO end must not be too short
1627 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1628 if (trace_startsolid)
1630 if (trace_fraction < minviewdistance / vlen(delta))
1633 // rule 5: don't want to look at sky
1634 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1637 // rule 6: we must not end up in trigger_hurt
1638 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1644 e.dphitcontentsmask = m;
1648 setorigin(e, start);
1649 e.angles = vectoangles(end - start);
1650 dprint("Needed ", ftos(i + 1), " attempts\n");
1657 void write_recordmarker(entity pl, float tstart, float dt)
1659 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1661 // also write a marker into demo files for demotc-race-record-extractor to find
1664 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1665 " ", ftos(tstart), " ", ftos(dt), "\n"));
1668 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1681 if(allowcenter) // 2: allow center handedness
1694 if(allowcenter) // 2: allow center handedness
1710 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1715 if (autocvar_g_shootfromeye)
1719 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1720 else { vecs.y = 0; vecs.z -= 2; }
1728 else if (autocvar_g_shootfromcenter)
1733 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1743 else if (autocvar_g_shootfromclient)
1745 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1750 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1752 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1756 void attach_sameorigin(entity e, entity to, string tag)
1758 vector org, t_forward, t_left, t_up, e_forward, e_up;
1761 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1762 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1763 t_forward = v_forward * tagscale;
1764 t_left = v_right * -tagscale;
1765 t_up = v_up * tagscale;
1767 e.origin_x = org * t_forward;
1768 e.origin_y = org * t_left;
1769 e.origin_z = org * t_up;
1771 // current forward and up directions
1772 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1773 e.angles = AnglesTransform_FromVAngles(e.angles);
1775 e.angles = AnglesTransform_FromAngles(e.angles);
1776 fixedmakevectors(e.angles);
1778 // untransform forward, up!
1779 e_forward.x = v_forward * t_forward;
1780 e_forward.y = v_forward * t_left;
1781 e_forward.z = v_forward * t_up;
1782 e_up.x = v_up * t_forward;
1783 e_up.y = v_up * t_left;
1784 e_up.z = v_up * t_up;
1786 e.angles = fixedvectoangles2(e_forward, e_up);
1787 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1788 e.angles = AnglesTransform_ToVAngles(e.angles);
1790 e.angles = AnglesTransform_ToAngles(e.angles);
1792 setattachment(e, to, tag);
1793 setorigin(e, e.origin);
1796 void detach_sameorigin(entity e)
1799 org = gettaginfo(e, 0);
1800 e.angles = fixedvectoangles2(v_forward, v_up);
1801 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1802 e.angles = AnglesTransform_ToVAngles(e.angles);
1804 e.angles = AnglesTransform_ToAngles(e.angles);
1806 setattachment(e, world, "");
1807 setorigin(e, e.origin);
1810 void follow_sameorigin(entity e, entity to)
1812 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1813 e.aiment = to; // make the hole follow bmodel
1814 e.punchangle = to.angles; // the original angles of bmodel
1815 e.view_ofs = e.origin - to.origin; // relative origin
1816 e.v_angle = e.angles - to.angles; // relative angles
1819 void unfollow_sameorigin(entity e)
1821 e.movetype = MOVETYPE_NONE;
1824 entity gettaginfo_relative_ent;
1825 vector gettaginfo_relative(entity e, float tag)
1827 if (!gettaginfo_relative_ent)
1829 gettaginfo_relative_ent = spawn();
1830 gettaginfo_relative_ent.effects = EF_NODRAW;
1832 gettaginfo_relative_ent.model = e.model;
1833 gettaginfo_relative_ent.modelindex = e.modelindex;
1834 gettaginfo_relative_ent.frame = e.frame;
1835 return gettaginfo(gettaginfo_relative_ent, tag);
1840 float modeleffect_SendEntity(entity to, float sf)
1843 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1846 if(self.velocity != '0 0 0')
1848 if(self.angles != '0 0 0')
1850 if(self.avelocity != '0 0 0')
1853 WriteByte(MSG_ENTITY, f);
1854 WriteShort(MSG_ENTITY, self.modelindex);
1855 WriteByte(MSG_ENTITY, self.skin);
1856 WriteByte(MSG_ENTITY, self.frame);
1857 WriteCoord(MSG_ENTITY, self.origin.x);
1858 WriteCoord(MSG_ENTITY, self.origin.y);
1859 WriteCoord(MSG_ENTITY, self.origin.z);
1862 WriteCoord(MSG_ENTITY, self.velocity.x);
1863 WriteCoord(MSG_ENTITY, self.velocity.y);
1864 WriteCoord(MSG_ENTITY, self.velocity.z);
1868 WriteCoord(MSG_ENTITY, self.angles.x);
1869 WriteCoord(MSG_ENTITY, self.angles.y);
1870 WriteCoord(MSG_ENTITY, self.angles.z);
1874 WriteCoord(MSG_ENTITY, self.avelocity.x);
1875 WriteCoord(MSG_ENTITY, self.avelocity.y);
1876 WriteCoord(MSG_ENTITY, self.avelocity.z);
1878 WriteShort(MSG_ENTITY, self.scale * 256.0);
1879 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1880 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1881 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1882 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1887 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)
1892 e.classname = "modeleffect";
1900 e.teleport_time = t1;
1904 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1908 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1911 sz = max(e.scale, e.scale2);
1912 setsize(e, e.mins * sz, e.maxs * sz);
1913 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1916 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1918 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1921 float randombit(float bits)
1923 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1932 for(f = 1; f <= bits; f *= 2)
1941 r = (r - 1) / (n - 1);
1948 float randombits(float bits, float k, float error_return)
1952 while(k > 0 && bits != r)
1954 r += randombit(bits - r);
1963 void randombit_test(float bits, float iter)
1967 print(ftos(randombit(bits)), "\n");
1972 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1974 if(halflifedist > 0)
1975 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1976 else if(halflifedist < 0)
1977 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1989 oself.think = SUB_Remove;
1990 oself.nextthink = time;
1996 Execute func() after time + fdelay.
1997 self when func is executed = self when defer is called
1999 void defer(float fdelay, void() func)
2006 e.think = defer_think;
2007 e.nextthink = time + fdelay;
2010 .string aiment_classname;
2011 .float aiment_deadflag;
2012 void SetMovetypeFollow(entity ent, entity e)
2014 // FIXME this may not be warpzone aware
2015 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2016 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.
2017 ent.aiment = e; // make the hole follow bmodel
2018 ent.punchangle = e.angles; // the original angles of bmodel
2019 ent.view_ofs = ent.origin - e.origin; // relative origin
2020 ent.v_angle = ent.angles - e.angles; // relative angles
2021 ent.aiment_classname = strzone(e.classname);
2022 ent.aiment_deadflag = e.deadflag;
2024 void UnsetMovetypeFollow(entity ent)
2026 ent.movetype = MOVETYPE_FLY;
2027 PROJECTILE_MAKETRIGGER(ent);
2030 float LostMovetypeFollow(entity ent)
2033 if(ent.movetype != MOVETYPE_FOLLOW)
2039 if(ent.aiment.classname != ent.aiment_classname)
2041 if(ent.aiment.deadflag != ent.aiment_deadflag)
2047 float isPushable(entity e)
2056 case "droppedweapon":
2057 case "keepawayball":
2058 case "nexball_basketball":
2059 case "nexball_football":
2061 case "bullet": // antilagged bullets can't hit this either
2064 if (e.projectiledeathtype)