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)
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_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
458 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
459 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
465 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
466 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
467 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
468 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
469 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
470 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
471 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
472 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
473 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
474 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
475 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
476 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
478 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
479 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
481 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
482 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
483 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
484 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
485 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
487 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
490 if (s == "cl_weaponpriority")
491 self.switchweapon = w_getbestweapon(self);
492 if (s == "cl_allow_uidtracking")
493 PlayerStats_GameReport_AddPlayer(self);
497 // decolorizes and team colors the player name when needed
498 string playername(entity p)
501 if (teamplay && !intermission_running && IS_PLAYER(p))
503 t = Team_ColorCode(p.team);
504 return strcat(t, strdecolorize(p.netname));
510 vector randompos(vector m1, vector m2)
514 v.x = m2_x * random() + m1_x;
515 v.y = m2_y * random() + m1_y;
516 v.z = m2_z * random() + m1_z;
520 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
522 int i = weaponinfo.weapon;
528 if (g_lms || g_ca || allguns)
530 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
536 d = (i == WEP_SHOTGUN);
538 d = 0; // weapon is set a few lines later
540 d = !(!weaponinfo.weaponstart);
542 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
543 d |= (i == WEP_HOOK);
544 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
547 float t = weaponinfo.weaponstartoverride;
549 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
554 // 4: is set by default?
563 void readplayerstartcvars()
569 // initialize starting values for players
570 start_weapons = '0 0 0';
571 start_weapons_default = '0 0 0';
572 start_weapons_defaultmask = '0 0 0';
574 start_ammo_shells = 0;
575 start_ammo_nails = 0;
576 start_ammo_rockets = 0;
577 start_ammo_cells = 0;
578 start_ammo_plasma = 0;
579 start_health = cvar("g_balance_health_start");
580 start_armorvalue = cvar("g_balance_armor_start");
583 g_weaponarena_weapons = '0 0 0';
585 s = cvar_string("g_weaponarena");
586 if (s == "0" || s == "")
592 if (s == "0" || s == "")
598 // forcibly turn off weaponarena
600 else if (s == "all" || s == "1")
603 g_weaponarena_list = "All Weapons";
604 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
606 e = get_weaponinfo(j);
607 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
608 g_weaponarena_weapons |= WepSet_FromWeapon(j);
611 else if (s == "most")
614 g_weaponarena_list = "Most Weapons";
615 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
617 e = get_weaponinfo(j);
618 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
619 if (e.spawnflags & WEP_FLAG_NORMAL)
620 g_weaponarena_weapons |= WepSet_FromWeapon(j);
623 else if (s == "none")
626 g_weaponarena_list = "No Weapons";
631 t = tokenize_console(s);
632 g_weaponarena_list = "";
633 for (i = 0; i < t; ++i)
636 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
638 e = get_weaponinfo(j);
641 g_weaponarena_weapons |= WepSet_FromWeapon(j);
642 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
648 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
651 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
655 g_weaponarena_random = cvar("g_weaponarena_random");
657 g_weaponarena_random = 0;
658 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
662 g_weapon_stay = 0; // incompatible
663 start_weapons = g_weaponarena_weapons;
664 start_items |= IT_UNLIMITED_AMMO;
668 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
670 e = get_weaponinfo(i);
671 int w = want_weapon(e, false);
673 start_weapons |= WepSet_FromWeapon(i);
675 start_weapons_default |= WepSet_FromWeapon(i);
677 start_weapons_defaultmask |= WepSet_FromWeapon(i);
681 if(!cvar("g_use_ammunition"))
682 start_items |= IT_UNLIMITED_AMMO;
684 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
686 start_ammo_shells = 999;
687 start_ammo_nails = 999;
688 start_ammo_rockets = 999;
689 start_ammo_cells = 999;
690 start_ammo_plasma = 999;
691 start_ammo_fuel = 999;
695 start_ammo_shells = cvar("g_start_ammo_shells");
696 start_ammo_nails = cvar("g_start_ammo_nails");
697 start_ammo_rockets = cvar("g_start_ammo_rockets");
698 start_ammo_cells = cvar("g_start_ammo_cells");
699 start_ammo_plasma = cvar("g_start_ammo_plasma");
700 start_ammo_fuel = cvar("g_start_ammo_fuel");
705 warmup_start_ammo_shells = start_ammo_shells;
706 warmup_start_ammo_nails = start_ammo_nails;
707 warmup_start_ammo_rockets = start_ammo_rockets;
708 warmup_start_ammo_cells = start_ammo_cells;
709 warmup_start_ammo_plasma = start_ammo_plasma;
710 warmup_start_ammo_fuel = start_ammo_fuel;
711 warmup_start_health = start_health;
712 warmup_start_armorvalue = start_armorvalue;
713 warmup_start_weapons = start_weapons;
714 warmup_start_weapons_default = start_weapons_default;
715 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
717 if (!g_weaponarena && !g_ca)
719 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
720 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
721 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
722 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
723 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
724 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
725 warmup_start_health = cvar("g_warmup_start_health");
726 warmup_start_armorvalue = cvar("g_warmup_start_armor");
727 warmup_start_weapons = '0 0 0';
728 warmup_start_weapons_default = '0 0 0';
729 warmup_start_weapons_defaultmask = '0 0 0';
730 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
732 e = get_weaponinfo(i);
733 int w = want_weapon(e, g_warmup_allguns);
735 warmup_start_weapons |= WepSet_FromWeapon(i);
737 warmup_start_weapons_default |= WepSet_FromWeapon(i);
739 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
745 start_items |= IT_JETPACK;
747 MUTATOR_CALLHOOK(SetStartItems);
749 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
751 start_items |= IT_FUEL_REGEN;
752 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
753 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
756 WepSet precache_weapons = start_weapons;
757 if (g_warmup_allguns != 1)
758 precache_weapons |= warmup_start_weapons;
759 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
761 e = get_weaponinfo(i);
762 if(precache_weapons & WepSet_FromWeapon(i))
763 WEP_ACTION(i, WR_INIT);
766 start_ammo_shells = max(0, start_ammo_shells);
767 start_ammo_nails = max(0, start_ammo_nails);
768 start_ammo_rockets = max(0, start_ammo_rockets);
769 start_ammo_cells = max(0, start_ammo_cells);
770 start_ammo_plasma = max(0, start_ammo_plasma);
771 start_ammo_fuel = max(0, start_ammo_fuel);
773 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
774 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
775 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
776 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
777 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
778 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
781 float sound_allowed(float _dest, entity e)
783 // sounds from world may always pass
786 if (e.classname == "body")
788 else if (e.realowner && e.realowner != e)
790 else if (e.owner && e.owner != e)
795 // sounds to self may always pass
796 if (_dest == MSG_ONE)
799 // sounds by players can be removed
800 if (autocvar_bot_sound_monopoly)
801 if (IS_REAL_CLIENT(e))
803 // anything else may pass
808 void sound(entity e, float chan, string samp, float vol, float _atten)
810 if (!sound_allowed(MSG_BROADCAST, e))
812 sound7(e, chan, samp, vol, _atten, 0, 0);
815 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
819 if (!sound_allowed(_dest, e))
822 entno = num_for_edict(e);
823 idx = precache_sound_index(samp);
828 _atten = floor(_atten * 64);
829 vol = floor(vol * 255);
832 sflags |= SND_VOLUME;
834 sflags |= SND_ATTENUATION;
835 if (entno >= 8192 || chan < 0 || chan > 7)
836 sflags |= SND_LARGEENTITY;
838 sflags |= SND_LARGESOUND;
840 WriteByte(_dest, SVC_SOUND);
841 WriteByte(_dest, sflags);
842 if (sflags & SND_VOLUME)
843 WriteByte(_dest, vol);
844 if (sflags & SND_ATTENUATION)
845 WriteByte(_dest, _atten);
846 if (sflags & SND_LARGEENTITY)
848 WriteShort(_dest, entno);
849 WriteByte(_dest, chan);
853 WriteShort(_dest, entno * 8 + chan);
855 if (sflags & SND_LARGESOUND)
856 WriteShort(_dest, idx);
858 WriteByte(_dest, idx);
860 WriteCoord(_dest, o.x);
861 WriteCoord(_dest, o.y);
862 WriteCoord(_dest, o.z);
864 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
868 if (!sound_allowed(_dest, e))
871 o = e.origin + 0.5 * (e.mins + e.maxs);
872 soundtoat(_dest, e, o, chan, samp, vol, _atten);
874 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
876 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
878 void stopsoundto(float _dest, entity e, float chan)
882 if (!sound_allowed(_dest, e))
885 entno = num_for_edict(e);
887 if (entno >= 8192 || chan < 0 || chan > 7)
890 idx = precache_sound_index("misc/null.wav");
891 sflags = SND_LARGEENTITY;
893 sflags |= SND_LARGESOUND;
894 WriteByte(_dest, SVC_SOUND);
895 WriteByte(_dest, sflags);
896 WriteShort(_dest, entno);
897 WriteByte(_dest, chan);
898 if (sflags & SND_LARGESOUND)
899 WriteShort(_dest, idx);
901 WriteByte(_dest, idx);
902 WriteCoord(_dest, e.origin.x);
903 WriteCoord(_dest, e.origin.y);
904 WriteCoord(_dest, e.origin.z);
908 WriteByte(_dest, SVC_STOPSOUND);
909 WriteShort(_dest, entno * 8 + chan);
912 void stopsound(entity e, float chan)
914 if (!sound_allowed(MSG_BROADCAST, e))
917 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
918 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
921 void play2(entity e, string filename)
923 //stuffcmd(e, strcat("play2 ", filename, "\n"));
925 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
928 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
930 float spamsound(entity e, float chan, string samp, float vol, float _atten)
932 if (!sound_allowed(MSG_BROADCAST, e))
935 if (time > e.spamtime)
938 sound(e, chan, samp, vol, _atten);
944 void play2team(float t, string filename)
948 if (autocvar_bot_sound_monopoly)
951 FOR_EACH_REALPLAYER(head)
954 play2(head, filename);
958 void play2all(string samp)
960 if (autocvar_bot_sound_monopoly)
963 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
966 void PrecachePlayerSounds(string f);
967 void precache_playermodel(string m)
969 float globhandle, i, n;
972 if(substring(m, -9,5) == "_lod1")
974 if(substring(m, -9,5) == "_lod2")
977 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
980 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
984 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
987 n = search_getsize(globhandle);
988 for (i = 0; i < n; ++i)
990 //print(search_getfilename(globhandle, i), "\n");
991 f = search_getfilename(globhandle, i);
992 PrecachePlayerSounds(f);
994 search_end(globhandle);
996 void precache_all_playermodels(string pattern)
998 float globhandle, i, n;
1001 globhandle = search_begin(pattern, true, false);
1004 n = search_getsize(globhandle);
1005 for (i = 0; i < n; ++i)
1007 //print(search_getfilename(globhandle, i), "\n");
1008 f = search_getfilename(globhandle, i);
1009 precache_playermodel(f);
1011 search_end(globhandle);
1016 // gamemode related things
1017 precache_model ("models/misc/chatbubble.spr");
1018 precache_model("models/ice/ice.md3");
1020 #ifdef TTURRETS_ENABLED
1021 if (autocvar_g_turrets)
1025 // Precache all player models if desired
1026 if (autocvar_sv_precacheplayermodels)
1028 PrecachePlayerSounds("sound/player/default.sounds");
1029 precache_all_playermodels("models/player/*.zym");
1030 precache_all_playermodels("models/player/*.dpm");
1031 precache_all_playermodels("models/player/*.md3");
1032 precache_all_playermodels("models/player/*.psk");
1033 precache_all_playermodels("models/player/*.iqm");
1036 if (autocvar_sv_defaultcharacter)
1039 s = autocvar_sv_defaultplayermodel_red;
1041 precache_playermodel(s);
1042 s = autocvar_sv_defaultplayermodel_blue;
1044 precache_playermodel(s);
1045 s = autocvar_sv_defaultplayermodel_yellow;
1047 precache_playermodel(s);
1048 s = autocvar_sv_defaultplayermodel_pink;
1050 precache_playermodel(s);
1051 s = autocvar_sv_defaultplayermodel;
1053 precache_playermodel(s);
1058 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1059 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1062 // gore and miscellaneous sounds
1063 //precache_sound ("misc/h2ohit.wav");
1064 precache_model ("models/hook.md3");
1065 precache_sound ("misc/armorimpact.wav");
1066 precache_sound ("misc/bodyimpact1.wav");
1067 precache_sound ("misc/bodyimpact2.wav");
1068 precache_sound ("misc/gib.wav");
1069 precache_sound ("misc/gib_splat01.wav");
1070 precache_sound ("misc/gib_splat02.wav");
1071 precache_sound ("misc/gib_splat03.wav");
1072 precache_sound ("misc/gib_splat04.wav");
1073 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1074 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1075 precache_sound ("misc/null.wav");
1076 precache_sound ("misc/spawn.wav");
1077 precache_sound ("misc/talk.wav");
1078 precache_sound ("misc/teleport.wav");
1079 precache_sound ("misc/poweroff.wav");
1080 precache_sound ("player/lava.wav");
1081 precache_sound ("player/slime.wav");
1083 precache_model ("models/sprites/0.spr32");
1084 precache_model ("models/sprites/1.spr32");
1085 precache_model ("models/sprites/2.spr32");
1086 precache_model ("models/sprites/3.spr32");
1087 precache_model ("models/sprites/4.spr32");
1088 precache_model ("models/sprites/5.spr32");
1089 precache_model ("models/sprites/6.spr32");
1090 precache_model ("models/sprites/7.spr32");
1091 precache_model ("models/sprites/8.spr32");
1092 precache_model ("models/sprites/9.spr32");
1093 precache_model ("models/sprites/10.spr32");
1095 // common weapon precaches
1096 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1097 precache_sound ("weapons/weapon_switch.wav");
1098 precache_sound ("weapons/weaponpickup.wav");
1099 precache_sound ("weapons/unavailable.wav");
1100 precache_sound ("weapons/dryfire.wav");
1101 if (g_grappling_hook)
1103 precache_sound ("weapons/hook_fire.wav"); // hook
1104 precache_sound ("weapons/hook_impact.wav"); // hook
1107 precache_model("models/elaser.mdl");
1108 precache_model("models/laser.mdl");
1109 precache_model("models/ebomb.mdl");
1112 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1114 if (!self.noise && self.music) // quake 3 uses the music field
1115 self.noise = self.music;
1117 // plays music for the level if there is any
1120 precache_sound (self.noise);
1121 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1125 #include "precache-for-csqc.inc"
1129 void make_safe_for_remove(entity e)
1131 if (e.initialize_entity)
1133 entity ent, prev = world;
1134 for (ent = initialize_entity_first; ent; )
1136 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1138 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1139 // skip it in linked list
1142 prev.initialize_entity_next = ent.initialize_entity_next;
1143 ent = prev.initialize_entity_next;
1147 initialize_entity_first = ent.initialize_entity_next;
1148 ent = initialize_entity_first;
1154 ent = ent.initialize_entity_next;
1160 void objerror(string s)
1162 make_safe_for_remove(self);
1163 builtin_objerror(s);
1166 .float remove_except_protected_forbidden;
1167 void remove_except_protected(entity e)
1169 if(e.remove_except_protected_forbidden)
1170 error("not allowed to remove this at this point");
1174 void remove_unsafely(entity e)
1176 if(e.classname == "spike")
1177 error("Removing spikes is forbidden (crylink bug), please report");
1181 void remove_safely(entity e)
1183 make_safe_for_remove(e);
1187 void InitializeEntity(entity e, void(void) func, float order)
1191 if (!e || e.initialize_entity)
1193 // make a proxy initializer entity
1197 e.classname = "initialize_entity";
1201 e.initialize_entity = func;
1202 e.initialize_entity_order = order;
1204 cur = initialize_entity_first;
1208 if (!cur || cur.initialize_entity_order > order)
1210 // insert between prev and cur
1212 prev.initialize_entity_next = e;
1214 initialize_entity_first = e;
1215 e.initialize_entity_next = cur;
1219 cur = cur.initialize_entity_next;
1222 void InitializeEntitiesRun()
1225 startoflist = initialize_entity_first;
1226 initialize_entity_first = world;
1227 remove = remove_except_protected;
1228 for (self = startoflist; self; self = self.initialize_entity_next)
1230 self.remove_except_protected_forbidden = 1;
1232 for (self = startoflist; self; )
1235 var void(void) func;
1236 e = self.initialize_entity_next;
1237 func = self.initialize_entity;
1238 self.initialize_entity_order = 0;
1239 self.initialize_entity = func_null;
1240 self.initialize_entity_next = world;
1241 self.remove_except_protected_forbidden = 0;
1242 if (self.classname == "initialize_entity")
1246 builtin_remove(self);
1249 //dprint("Delayed initialization: ", self.classname, "\n");
1255 backtrace(strcat("Null function in: ", self.classname, "\n"));
1259 remove = remove_unsafely;
1262 void UncustomizeEntitiesRun()
1266 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1267 self.uncustomizeentityforclient();
1270 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1272 e.customizeentityforclient = customizer;
1273 e.uncustomizeentityforclient = uncustomizer;
1274 e.uncustomizeentityforclient_set = !!uncustomizer;
1278 void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) 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, int 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)