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_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 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
512 int i = weaponinfo.weapon;
518 if (g_lms || g_ca || allguns)
520 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
526 d = (i == WEP_SHOTGUN);
528 d = 0; // weapon is set a few lines later
530 d = !(!weaponinfo.weaponstart);
532 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
533 d |= (i == WEP_HOOK);
534 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
537 float t = weaponinfo.weaponstartoverride;
539 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
544 // 4: is set by default?
553 void readplayerstartcvars()
559 // initialize starting values for players
560 start_weapons = '0 0 0';
561 start_weapons_default = '0 0 0';
562 start_weapons_defaultmask = '0 0 0';
564 start_ammo_shells = 0;
565 start_ammo_nails = 0;
566 start_ammo_rockets = 0;
567 start_ammo_cells = 0;
568 start_ammo_plasma = 0;
569 start_health = cvar("g_balance_health_start");
570 start_armorvalue = cvar("g_balance_armor_start");
573 g_weaponarena_weapons = '0 0 0';
575 s = cvar_string("g_weaponarena");
576 if (s == "0" || s == "")
582 if (s == "0" || s == "")
588 // forcibly turn off weaponarena
590 else if (s == "all" || s == "1")
593 g_weaponarena_list = "All Weapons";
594 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
596 e = get_weaponinfo(j);
597 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
598 g_weaponarena_weapons |= WepSet_FromWeapon(j);
601 else if (s == "most")
604 g_weaponarena_list = "Most Weapons";
605 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
607 e = get_weaponinfo(j);
608 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
609 if (e.spawnflags & WEP_FLAG_NORMAL)
610 g_weaponarena_weapons |= WepSet_FromWeapon(j);
613 else if (s == "none")
616 g_weaponarena_list = "No Weapons";
621 t = tokenize_console(s);
622 g_weaponarena_list = "";
623 for (i = 0; i < t; ++i)
626 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
628 e = get_weaponinfo(j);
631 g_weaponarena_weapons |= WepSet_FromWeapon(j);
632 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
638 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
641 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
645 g_weaponarena_random = cvar("g_weaponarena_random");
647 g_weaponarena_random = 0;
648 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
652 g_weapon_stay = 0; // incompatible
653 start_weapons = g_weaponarena_weapons;
654 start_items |= IT_UNLIMITED_AMMO;
658 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
660 e = get_weaponinfo(i);
661 int w = want_weapon(e, false);
663 start_weapons |= WepSet_FromWeapon(i);
665 start_weapons_default |= WepSet_FromWeapon(i);
667 start_weapons_defaultmask |= WepSet_FromWeapon(i);
671 if(!cvar("g_use_ammunition"))
672 start_items |= IT_UNLIMITED_AMMO;
674 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
676 start_ammo_shells = 999;
677 start_ammo_nails = 999;
678 start_ammo_rockets = 999;
679 start_ammo_cells = 999;
680 start_ammo_plasma = 999;
681 start_ammo_fuel = 999;
685 start_ammo_shells = cvar("g_start_ammo_shells");
686 start_ammo_nails = cvar("g_start_ammo_nails");
687 start_ammo_rockets = cvar("g_start_ammo_rockets");
688 start_ammo_cells = cvar("g_start_ammo_cells");
689 start_ammo_plasma = cvar("g_start_ammo_plasma");
690 start_ammo_fuel = cvar("g_start_ammo_fuel");
695 warmup_start_ammo_shells = start_ammo_shells;
696 warmup_start_ammo_nails = start_ammo_nails;
697 warmup_start_ammo_rockets = start_ammo_rockets;
698 warmup_start_ammo_cells = start_ammo_cells;
699 warmup_start_ammo_plasma = start_ammo_plasma;
700 warmup_start_ammo_fuel = start_ammo_fuel;
701 warmup_start_health = start_health;
702 warmup_start_armorvalue = start_armorvalue;
703 warmup_start_weapons = start_weapons;
704 warmup_start_weapons_default = start_weapons_default;
705 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
707 if (!g_weaponarena && !g_ca)
709 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
710 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
711 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
712 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
713 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
714 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
715 warmup_start_health = cvar("g_warmup_start_health");
716 warmup_start_armorvalue = cvar("g_warmup_start_armor");
717 warmup_start_weapons = '0 0 0';
718 warmup_start_weapons_default = '0 0 0';
719 warmup_start_weapons_defaultmask = '0 0 0';
720 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
722 e = get_weaponinfo(i);
723 int w = want_weapon(e, g_warmup_allguns);
725 warmup_start_weapons |= WepSet_FromWeapon(i);
727 warmup_start_weapons_default |= WepSet_FromWeapon(i);
729 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
735 start_items |= IT_JETPACK;
737 MUTATOR_CALLHOOK(SetStartItems);
739 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
741 start_items |= IT_FUEL_REGEN;
742 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
743 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
746 WepSet precache_weapons = start_weapons;
747 if (g_warmup_allguns != 1)
748 precache_weapons |= warmup_start_weapons;
749 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
751 e = get_weaponinfo(i);
752 if(precache_weapons & WepSet_FromWeapon(i))
753 WEP_ACTION(i, WR_INIT);
756 start_ammo_shells = max(0, start_ammo_shells);
757 start_ammo_nails = max(0, start_ammo_nails);
758 start_ammo_rockets = max(0, start_ammo_rockets);
759 start_ammo_cells = max(0, start_ammo_cells);
760 start_ammo_plasma = max(0, start_ammo_plasma);
761 start_ammo_fuel = max(0, start_ammo_fuel);
763 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
764 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
765 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
766 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
767 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
768 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
771 float sound_allowed(float _dest, entity e)
773 // sounds from world may always pass
776 if (e.classname == "body")
778 else if (e.realowner && e.realowner != e)
780 else if (e.owner && e.owner != e)
785 // sounds to self may always pass
786 if (_dest == MSG_ONE)
789 // sounds by players can be removed
790 if (autocvar_bot_sound_monopoly)
791 if (IS_REAL_CLIENT(e))
793 // anything else may pass
798 void sound(entity e, float chan, string samp, float vol, float _atten)
800 if (!sound_allowed(MSG_BROADCAST, e))
802 sound7(e, chan, samp, vol, _atten, 0, 0);
805 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
809 if (!sound_allowed(_dest, e))
812 entno = num_for_edict(e);
813 idx = precache_sound_index(samp);
818 _atten = floor(_atten * 64);
819 vol = floor(vol * 255);
822 sflags |= SND_VOLUME;
824 sflags |= SND_ATTENUATION;
825 if (entno >= 8192 || chan < 0 || chan > 7)
826 sflags |= SND_LARGEENTITY;
828 sflags |= SND_LARGESOUND;
830 WriteByte(_dest, SVC_SOUND);
831 WriteByte(_dest, sflags);
832 if (sflags & SND_VOLUME)
833 WriteByte(_dest, vol);
834 if (sflags & SND_ATTENUATION)
835 WriteByte(_dest, _atten);
836 if (sflags & SND_LARGEENTITY)
838 WriteShort(_dest, entno);
839 WriteByte(_dest, chan);
843 WriteShort(_dest, entno * 8 + chan);
845 if (sflags & SND_LARGESOUND)
846 WriteShort(_dest, idx);
848 WriteByte(_dest, idx);
850 WriteCoord(_dest, o.x);
851 WriteCoord(_dest, o.y);
852 WriteCoord(_dest, o.z);
854 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
858 if (!sound_allowed(_dest, e))
861 o = e.origin + 0.5 * (e.mins + e.maxs);
862 soundtoat(_dest, e, o, chan, samp, vol, _atten);
864 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
866 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
868 void stopsoundto(float _dest, entity e, float chan)
872 if (!sound_allowed(_dest, e))
875 entno = num_for_edict(e);
877 if (entno >= 8192 || chan < 0 || chan > 7)
880 idx = precache_sound_index("misc/null.wav");
881 sflags = SND_LARGEENTITY;
883 sflags |= SND_LARGESOUND;
884 WriteByte(_dest, SVC_SOUND);
885 WriteByte(_dest, sflags);
886 WriteShort(_dest, entno);
887 WriteByte(_dest, chan);
888 if (sflags & SND_LARGESOUND)
889 WriteShort(_dest, idx);
891 WriteByte(_dest, idx);
892 WriteCoord(_dest, e.origin.x);
893 WriteCoord(_dest, e.origin.y);
894 WriteCoord(_dest, e.origin.z);
898 WriteByte(_dest, SVC_STOPSOUND);
899 WriteShort(_dest, entno * 8 + chan);
902 void stopsound(entity e, float chan)
904 if (!sound_allowed(MSG_BROADCAST, e))
907 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
908 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
911 void play2(entity e, string filename)
913 //stuffcmd(e, strcat("play2 ", filename, "\n"));
915 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
918 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
920 float spamsound(entity e, float chan, string samp, float vol, float _atten)
922 if (!sound_allowed(MSG_BROADCAST, e))
925 if (time > e.spamtime)
928 sound(e, chan, samp, vol, _atten);
934 void play2team(float t, string filename)
938 if (autocvar_bot_sound_monopoly)
941 FOR_EACH_REALPLAYER(head)
944 play2(head, filename);
948 void play2all(string samp)
950 if (autocvar_bot_sound_monopoly)
953 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
956 void PrecachePlayerSounds(string f);
957 void precache_playermodel(string m)
959 float globhandle, i, n;
962 if(substring(m, -9,5) == "_lod1")
964 if(substring(m, -9,5) == "_lod2")
967 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
970 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
974 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
977 n = search_getsize(globhandle);
978 for (i = 0; i < n; ++i)
980 //print(search_getfilename(globhandle, i), "\n");
981 f = search_getfilename(globhandle, i);
982 PrecachePlayerSounds(f);
984 search_end(globhandle);
986 void precache_all_playermodels(string pattern)
988 float globhandle, i, n;
991 globhandle = search_begin(pattern, true, false);
994 n = search_getsize(globhandle);
995 for (i = 0; i < n; ++i)
997 //print(search_getfilename(globhandle, i), "\n");
998 f = search_getfilename(globhandle, i);
999 precache_playermodel(f);
1001 search_end(globhandle);
1006 // gamemode related things
1007 precache_model ("models/misc/chatbubble.spr");
1008 precache_model("models/ice/ice.md3");
1010 #ifdef TTURRETS_ENABLED
1011 if (autocvar_g_turrets)
1015 // Precache all player models if desired
1016 if (autocvar_sv_precacheplayermodels)
1018 PrecachePlayerSounds("sound/player/default.sounds");
1019 precache_all_playermodels("models/player/*.zym");
1020 precache_all_playermodels("models/player/*.dpm");
1021 precache_all_playermodels("models/player/*.md3");
1022 precache_all_playermodels("models/player/*.psk");
1023 precache_all_playermodels("models/player/*.iqm");
1026 if (autocvar_sv_defaultcharacter)
1029 s = autocvar_sv_defaultplayermodel_red;
1031 precache_playermodel(s);
1032 s = autocvar_sv_defaultplayermodel_blue;
1034 precache_playermodel(s);
1035 s = autocvar_sv_defaultplayermodel_yellow;
1037 precache_playermodel(s);
1038 s = autocvar_sv_defaultplayermodel_pink;
1040 precache_playermodel(s);
1041 s = autocvar_sv_defaultplayermodel;
1043 precache_playermodel(s);
1048 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1049 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1052 // gore and miscellaneous sounds
1053 //precache_sound ("misc/h2ohit.wav");
1054 precache_model ("models/hook.md3");
1055 precache_sound ("misc/armorimpact.wav");
1056 precache_sound ("misc/bodyimpact1.wav");
1057 precache_sound ("misc/bodyimpact2.wav");
1058 precache_sound ("misc/gib.wav");
1059 precache_sound ("misc/gib_splat01.wav");
1060 precache_sound ("misc/gib_splat02.wav");
1061 precache_sound ("misc/gib_splat03.wav");
1062 precache_sound ("misc/gib_splat04.wav");
1063 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1064 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1065 precache_sound ("misc/null.wav");
1066 precache_sound ("misc/spawn.wav");
1067 precache_sound ("misc/talk.wav");
1068 precache_sound ("misc/teleport.wav");
1069 precache_sound ("misc/poweroff.wav");
1070 precache_sound ("player/lava.wav");
1071 precache_sound ("player/slime.wav");
1073 precache_model ("models/sprites/0.spr32");
1074 precache_model ("models/sprites/1.spr32");
1075 precache_model ("models/sprites/2.spr32");
1076 precache_model ("models/sprites/3.spr32");
1077 precache_model ("models/sprites/4.spr32");
1078 precache_model ("models/sprites/5.spr32");
1079 precache_model ("models/sprites/6.spr32");
1080 precache_model ("models/sprites/7.spr32");
1081 precache_model ("models/sprites/8.spr32");
1082 precache_model ("models/sprites/9.spr32");
1083 precache_model ("models/sprites/10.spr32");
1085 // common weapon precaches
1086 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1087 precache_sound ("weapons/weapon_switch.wav");
1088 precache_sound ("weapons/weaponpickup.wav");
1089 precache_sound ("weapons/unavailable.wav");
1090 precache_sound ("weapons/dryfire.wav");
1091 if (g_grappling_hook)
1093 precache_sound ("weapons/hook_fire.wav"); // hook
1094 precache_sound ("weapons/hook_impact.wav"); // hook
1097 precache_model("models/elaser.mdl");
1098 precache_model("models/laser.mdl");
1099 precache_model("models/ebomb.mdl");
1102 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1104 if (!self.noise && self.music) // quake 3 uses the music field
1105 self.noise = self.music;
1107 // plays music for the level if there is any
1110 precache_sound (self.noise);
1111 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1115 #include "precache-for-csqc.inc"
1119 void make_safe_for_remove(entity e)
1121 if (e.initialize_entity)
1123 entity ent, prev = world;
1124 for (ent = initialize_entity_first; ent; )
1126 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1128 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1129 // skip it in linked list
1132 prev.initialize_entity_next = ent.initialize_entity_next;
1133 ent = prev.initialize_entity_next;
1137 initialize_entity_first = ent.initialize_entity_next;
1138 ent = initialize_entity_first;
1144 ent = ent.initialize_entity_next;
1150 void objerror(string s)
1152 make_safe_for_remove(self);
1153 builtin_objerror(s);
1156 .float remove_except_protected_forbidden;
1157 void remove_except_protected(entity e)
1159 if(e.remove_except_protected_forbidden)
1160 error("not allowed to remove this at this point");
1164 void remove_unsafely(entity e)
1166 if(e.classname == "spike")
1167 error("Removing spikes is forbidden (crylink bug), please report");
1171 void remove_safely(entity e)
1173 make_safe_for_remove(e);
1177 void InitializeEntity(entity e, void(void) func, float order)
1181 if (!e || e.initialize_entity)
1183 // make a proxy initializer entity
1187 e.classname = "initialize_entity";
1191 e.initialize_entity = func;
1192 e.initialize_entity_order = order;
1194 cur = initialize_entity_first;
1198 if (!cur || cur.initialize_entity_order > order)
1200 // insert between prev and cur
1202 prev.initialize_entity_next = e;
1204 initialize_entity_first = e;
1205 e.initialize_entity_next = cur;
1209 cur = cur.initialize_entity_next;
1212 void InitializeEntitiesRun()
1215 startoflist = initialize_entity_first;
1216 initialize_entity_first = world;
1217 remove = remove_except_protected;
1218 for (self = startoflist; self; self = self.initialize_entity_next)
1220 self.remove_except_protected_forbidden = 1;
1222 for (self = startoflist; self; )
1225 var void(void) func;
1226 e = self.initialize_entity_next;
1227 func = self.initialize_entity;
1228 self.initialize_entity_order = 0;
1229 self.initialize_entity = func_null;
1230 self.initialize_entity_next = world;
1231 self.remove_except_protected_forbidden = 0;
1232 if (self.classname == "initialize_entity")
1236 builtin_remove(self);
1239 //dprint("Delayed initialization: ", self.classname, "\n");
1245 backtrace(strcat("Null function in: ", self.classname, "\n"));
1249 remove = remove_unsafely;
1252 void UncustomizeEntitiesRun()
1256 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1257 self.uncustomizeentityforclient();
1260 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1262 e.customizeentityforclient = customizer;
1263 e.uncustomizeentityforclient = uncustomizer;
1264 e.uncustomizeentityforclient_set = !!uncustomizer;
1268 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1272 if (e.classname == "")
1273 e.classname = "net_linked";
1275 if (e.model == "" || self.modelindex == 0)
1279 setmodel(e, "null");
1283 e.SendEntity = sendfunc;
1284 e.SendFlags = 0xFFFFFF;
1287 e.effects |= EF_NODEPTHTEST;
1291 e.nextthink = time + dt;
1292 e.think = SUB_Remove;
1297 entity eliminatedPlayers;
1298 .float(entity) isEliminated;
1299 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1303 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1304 WriteByte(MSG_ENTITY, sendflags);
1308 for(i = 1; i <= maxclients; i += 8)
1310 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1312 if(eliminatedPlayers.isEliminated(e))
1315 WriteByte(MSG_ENTITY, f);
1322 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1324 if(eliminatedPlayers)
1326 backtrace("Can't spawn eliminatedPlayers again!");
1329 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1330 eliminatedPlayers.isEliminated = isEliminated_func;
1334 void adaptor_think2touch()
1343 void adaptor_think2use()
1355 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1357 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
1358 self.projectiledeathtype |= HITTYPE_SPLASH;
1359 adaptor_think2use();
1362 // deferred dropping
1363 void DropToFloor_Handler()
1365 builtin_droptofloor();
1366 self.dropped_origin = self.origin;
1371 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1376 float trace_hits_box_a0, trace_hits_box_a1;
1378 float trace_hits_box_1d(float end, float thmi, float thma)
1382 // just check if x is in range
1390 // do the trace with respect to x
1391 // 0 -> end has to stay in thmi -> thma
1392 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1393 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1394 if (trace_hits_box_a0 > trace_hits_box_a1)
1400 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1405 // now it is a trace from 0 to end
1407 trace_hits_box_a0 = 0;
1408 trace_hits_box_a1 = 1;
1410 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1412 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1414 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1420 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1422 return trace_hits_box(start, end, thmi - ma, thma - mi);
1425 float SUB_NoImpactCheck()
1427 // zero hitcontents = this is not the real impact, but either the
1428 // mirror-impact of something hitting the projectile instead of the
1429 // projectile hitting the something, or a touchareagrid one. Neither of
1430 // these stop the projectile from moving, so...
1431 if(trace_dphitcontents == 0)
1433 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1434 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));
1437 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1439 if (other == world && self.size != '0 0 0')
1442 tic = self.velocity * sys_frametime;
1443 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1444 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1445 if (trace_fraction >= 1)
1447 dprint("Odd... did not hit...?\n");
1449 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1451 dprint("Detected and prevented the sky-grapple bug.\n");
1459 #define SUB_OwnerCheck() (other && (other == self.owner))
1461 void RemoveGrapplingHook(entity pl);
1462 void W_Crylink_Dequeue(entity e);
1463 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1465 if(SUB_OwnerCheck())
1467 if(SUB_NoImpactCheck())
1469 if(self.classname == "nade")
1470 return false; // no checks here
1471 else if(self.classname == "grapplinghook")
1472 RemoveGrapplingHook(self.realowner);
1473 else if(self.classname == "spike")
1475 W_Crylink_Dequeue(self);
1482 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1483 UpdateCSQCProjectile(self);
1488 void URI_Get_Callback(float id, float status, string data)
1490 if(url_URI_Get_Callback(id, status, data))
1494 else if (id == URI_GET_DISCARD)
1498 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1501 Curl_URI_Get_Callback(id, status, data);
1503 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1506 OnlineBanList_URI_Get_Callback(id, status, data);
1510 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1514 string uid2name(string myuid) {
1516 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1518 // FIXME remove this later after 0.6 release
1519 // convert old style broken records to correct style
1522 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1525 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1526 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1531 s = "^1Unregistered Player";
1535 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1538 vector start, org, delta, end, enddown, mstart;
1541 m = e.dphitcontentsmask;
1542 e.dphitcontentsmask = goodcontents | badcontents;
1545 delta = world.maxs - world.mins;
1549 for (i = 0; i < attempts; ++i)
1551 start.x = org.x + random() * delta.x;
1552 start.y = org.y + random() * delta.y;
1553 start.z = org.z + random() * delta.z;
1555 // rule 1: start inside world bounds, and outside
1556 // solid, and don't start from somewhere where you can
1557 // fall down to evil
1558 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1559 if (trace_fraction >= 1)
1561 if (trace_startsolid)
1563 if (trace_dphitcontents & badcontents)
1565 if (trace_dphitq3surfaceflags & badsurfaceflags)
1568 // rule 2: if we are too high, lower the point
1569 if (trace_fraction * delta.z > maxaboveground)
1570 start = trace_endpos + '0 0 1' * maxaboveground;
1571 enddown = trace_endpos;
1573 // rule 3: make sure we aren't outside the map. This only works
1574 // for somewhat well formed maps. A good rule of thumb is that
1575 // the map should have a convex outside hull.
1576 // these can be traceLINES as we already verified the starting box
1577 mstart = start + 0.5 * (e.mins + e.maxs);
1578 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1579 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1581 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1582 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1584 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1585 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1587 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1588 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1590 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1591 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1594 // rule 4: we must "see" some spawnpoint or item
1595 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1596 if(checkpvs(mstart, sp))
1597 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1601 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1602 if(checkpvs(mstart, sp))
1603 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1609 // find a random vector to "look at"
1610 end.x = org.x + random() * delta.x;
1611 end.y = org.y + random() * delta.y;
1612 end.z = org.z + random() * delta.z;
1613 end = start + normalize(end - start) * vlen(delta);
1615 // rule 4: start TO end must not be too short
1616 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1617 if (trace_startsolid)
1619 if (trace_fraction < minviewdistance / vlen(delta))
1622 // rule 5: don't want to look at sky
1623 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1626 // rule 6: we must not end up in trigger_hurt
1627 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1633 e.dphitcontentsmask = m;
1637 setorigin(e, start);
1638 e.angles = vectoangles(end - start);
1639 dprint("Needed ", ftos(i + 1), " attempts\n");
1646 void write_recordmarker(entity pl, float tstart, float dt)
1648 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1650 // also write a marker into demo files for demotc-race-record-extractor to find
1653 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1654 " ", ftos(tstart), " ", ftos(dt), "\n"));
1657 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1670 if(allowcenter) // 2: allow center handedness
1683 if(allowcenter) // 2: allow center handedness
1699 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1704 if (autocvar_g_shootfromeye)
1708 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1709 else { vecs.y = 0; vecs.z -= 2; }
1717 else if (autocvar_g_shootfromcenter)
1722 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1732 else if (autocvar_g_shootfromclient)
1734 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1739 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1741 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1745 void attach_sameorigin(entity e, entity to, string tag)
1747 vector org, t_forward, t_left, t_up, e_forward, e_up;
1750 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1751 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1752 t_forward = v_forward * tagscale;
1753 t_left = v_right * -tagscale;
1754 t_up = v_up * tagscale;
1756 e.origin_x = org * t_forward;
1757 e.origin_y = org * t_left;
1758 e.origin_z = org * t_up;
1760 // current forward and up directions
1761 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1762 e.angles = AnglesTransform_FromVAngles(e.angles);
1764 e.angles = AnglesTransform_FromAngles(e.angles);
1765 fixedmakevectors(e.angles);
1767 // untransform forward, up!
1768 e_forward.x = v_forward * t_forward;
1769 e_forward.y = v_forward * t_left;
1770 e_forward.z = v_forward * t_up;
1771 e_up.x = v_up * t_forward;
1772 e_up.y = v_up * t_left;
1773 e_up.z = v_up * t_up;
1775 e.angles = fixedvectoangles2(e_forward, e_up);
1776 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1777 e.angles = AnglesTransform_ToVAngles(e.angles);
1779 e.angles = AnglesTransform_ToAngles(e.angles);
1781 setattachment(e, to, tag);
1782 setorigin(e, e.origin);
1785 void detach_sameorigin(entity e)
1788 org = gettaginfo(e, 0);
1789 e.angles = fixedvectoangles2(v_forward, v_up);
1790 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1791 e.angles = AnglesTransform_ToVAngles(e.angles);
1793 e.angles = AnglesTransform_ToAngles(e.angles);
1795 setattachment(e, world, "");
1796 setorigin(e, e.origin);
1799 void follow_sameorigin(entity e, entity to)
1801 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1802 e.aiment = to; // make the hole follow bmodel
1803 e.punchangle = to.angles; // the original angles of bmodel
1804 e.view_ofs = e.origin - to.origin; // relative origin
1805 e.v_angle = e.angles - to.angles; // relative angles
1808 void unfollow_sameorigin(entity e)
1810 e.movetype = MOVETYPE_NONE;
1813 entity gettaginfo_relative_ent;
1814 vector gettaginfo_relative(entity e, float tag)
1816 if (!gettaginfo_relative_ent)
1818 gettaginfo_relative_ent = spawn();
1819 gettaginfo_relative_ent.effects = EF_NODRAW;
1821 gettaginfo_relative_ent.model = e.model;
1822 gettaginfo_relative_ent.modelindex = e.modelindex;
1823 gettaginfo_relative_ent.frame = e.frame;
1824 return gettaginfo(gettaginfo_relative_ent, tag);
1829 float modeleffect_SendEntity(entity to, float sf)
1832 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1835 if(self.velocity != '0 0 0')
1837 if(self.angles != '0 0 0')
1839 if(self.avelocity != '0 0 0')
1842 WriteByte(MSG_ENTITY, f);
1843 WriteShort(MSG_ENTITY, self.modelindex);
1844 WriteByte(MSG_ENTITY, self.skin);
1845 WriteByte(MSG_ENTITY, self.frame);
1846 WriteCoord(MSG_ENTITY, self.origin.x);
1847 WriteCoord(MSG_ENTITY, self.origin.y);
1848 WriteCoord(MSG_ENTITY, self.origin.z);
1851 WriteCoord(MSG_ENTITY, self.velocity.x);
1852 WriteCoord(MSG_ENTITY, self.velocity.y);
1853 WriteCoord(MSG_ENTITY, self.velocity.z);
1857 WriteCoord(MSG_ENTITY, self.angles.x);
1858 WriteCoord(MSG_ENTITY, self.angles.y);
1859 WriteCoord(MSG_ENTITY, self.angles.z);
1863 WriteCoord(MSG_ENTITY, self.avelocity.x);
1864 WriteCoord(MSG_ENTITY, self.avelocity.y);
1865 WriteCoord(MSG_ENTITY, self.avelocity.z);
1867 WriteShort(MSG_ENTITY, self.scale * 256.0);
1868 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1869 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1870 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1871 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1876 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)
1881 e.classname = "modeleffect";
1889 e.teleport_time = t1;
1893 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1897 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1900 sz = max(e.scale, e.scale2);
1901 setsize(e, e.mins * sz, e.maxs * sz);
1902 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1905 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1907 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1910 float randombit(float bits)
1912 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1921 for(f = 1; f <= bits; f *= 2)
1930 r = (r - 1) / (n - 1);
1937 float randombits(float bits, float k, float error_return)
1941 while(k > 0 && bits != r)
1943 r += randombit(bits - r);
1952 void randombit_test(float bits, float iter)
1956 print(ftos(randombit(bits)), "\n");
1961 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1963 if(halflifedist > 0)
1964 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1965 else if(halflifedist < 0)
1966 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1978 oself.think = SUB_Remove;
1979 oself.nextthink = time;
1985 Execute func() after time + fdelay.
1986 self when func is executed = self when defer is called
1988 void defer(float fdelay, void() func)
1995 e.think = defer_think;
1996 e.nextthink = time + fdelay;
1999 .string aiment_classname;
2000 .float aiment_deadflag;
2001 void SetMovetypeFollow(entity ent, entity e)
2003 // FIXME this may not be warpzone aware
2004 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2005 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.
2006 ent.aiment = e; // make the hole follow bmodel
2007 ent.punchangle = e.angles; // the original angles of bmodel
2008 ent.view_ofs = ent.origin - e.origin; // relative origin
2009 ent.v_angle = ent.angles - e.angles; // relative angles
2010 ent.aiment_classname = strzone(e.classname);
2011 ent.aiment_deadflag = e.deadflag;
2013 void UnsetMovetypeFollow(entity ent)
2015 ent.movetype = MOVETYPE_FLY;
2016 PROJECTILE_MAKETRIGGER(ent);
2019 float LostMovetypeFollow(entity ent)
2022 if(ent.movetype != MOVETYPE_FOLLOW)
2028 if(ent.aiment.classname != ent.aiment_classname)
2030 if(ent.aiment.deadflag != ent.aiment_deadflag)
2036 float isPushable(entity e)
2045 case "droppedweapon":
2046 case "keepawayball":
2047 case "nexball_basketball":
2048 case "nexball_football":
2050 case "bullet": // antilagged bullets can't hit this either
2053 if (e.projectiledeathtype)