1 #include "miscfunctions.qh"
5 #include "command/common.qh"
6 #include "constants.qh"
8 #include "mutators/mutators_include.qh"
9 #include "tturrets/include/turrets_early.qh"
11 #include "weapons/accuracy.qh"
12 #include "weapons/csqcprojectile.qh"
13 #include "weapons/selection.qh"
14 #include "../common/command/generic.qh"
15 #include "../common/constants.qh"
16 #include "../common/deathtypes.qh"
17 #include "../common/mapinfo.qh"
18 #include "../common/notifications.qh"
19 #include "../common/playerstats.qh"
20 #include "../common/teams.qh"
21 #include "../common/urllib.qh"
22 #include "../common/util.qh"
23 #include "../common/weapons/weapons.qh"
24 #include "../csqcmodellib/sv_model.qh"
25 #include "../warpzonelib/anglestransform.qh"
26 #include "../warpzonelib/server.qh"
28 void crosshair_trace(entity pl)
30 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));
32 void crosshair_trace_plusvisibletriggers(entity pl)
36 first = findchainfloat(solid, SOLID_TRIGGER);
38 for (e = first; e; e = e.chain)
44 for (e = first; e; e = e.chain)
45 e.solid = SOLID_TRIGGER;
47 void WarpZone_crosshair_trace(entity pl)
49 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));
53 string admin_name(void)
55 if(autocvar_sv_adminnick != "")
56 return autocvar_sv_adminnick;
58 return "SERVER ADMIN";
61 void DistributeEvenly_Init(float amount, float totalweight)
63 if (DistributeEvenly_amount)
65 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
66 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
69 DistributeEvenly_amount = 0;
71 DistributeEvenly_amount = amount;
72 DistributeEvenly_totalweight = totalweight;
74 float DistributeEvenly_Get(float weight)
79 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
80 DistributeEvenly_totalweight -= weight;
81 DistributeEvenly_amount -= f;
84 float DistributeEvenly_GetRandomized(float weight)
89 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
90 DistributeEvenly_totalweight -= weight;
91 DistributeEvenly_amount -= f;
96 void GameLogEcho(string s)
101 if (autocvar_sv_eventlog_files)
106 matches = autocvar_sv_eventlog_files_counter + 1;
107 cvar_set("sv_eventlog_files_counter", itos(matches));
110 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
111 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
112 logfile = fopen(fn, FILE_APPEND);
113 fputs(logfile, ":logversion:3\n");
117 if (autocvar_sv_eventlog_files_timestamps)
118 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
120 fputs(logfile, strcat(s, "\n"));
123 if (autocvar_sv_eventlog_console)
132 // will be opened later
137 if (logfile_open && logfile >= 0)
144 entity findnearest(vector point, .string field, string value, vector axismod)
155 localhead = find(world, field, value);
158 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
159 dist = localhead.oldorigin;
161 dist = localhead.origin;
163 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
166 for (i = 0; i < num_nearest; ++i)
168 if (len < nearest_length[i])
172 // now i tells us where to insert at
173 // INSERTION SORT! YOU'VE SEEN IT! RUN!
174 if (i < NUM_NEAREST_ENTITIES)
176 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
178 nearest_length[j + 1] = nearest_length[j];
179 nearest_entity[j + 1] = nearest_entity[j];
181 nearest_length[i] = len;
182 nearest_entity[i] = localhead;
183 if (num_nearest < NUM_NEAREST_ENTITIES)
184 num_nearest = num_nearest + 1;
187 localhead = find(localhead, field, value);
190 // now use the first one from our list that we can see
191 for (i = 0; i < num_nearest; ++i)
193 traceline(point, nearest_entity[i].origin, true, world);
194 if (trace_fraction == 1)
198 dprint("Nearest point (");
199 dprint(nearest_entity[0].netname);
200 dprint(") is not visible, using a visible one.\n");
202 return nearest_entity[i];
206 if (num_nearest == 0)
209 dprint("Not seeing any location point, using nearest as fallback.\n");
211 dprint("Candidates were: ");
212 for(j = 0; j < num_nearest; ++j)
216 dprint(nearest_entity[j].netname);
221 return nearest_entity[0];
224 void spawnfunc_target_location()
226 self.classname = "target_location";
227 // location name in netname
228 // eventually support: count, teamgame selectors, line of sight?
231 void spawnfunc_info_location()
233 self.classname = "target_location";
234 self.message = self.netname;
237 string NearestLocation(vector p)
242 loc = findnearest(p, classname, "target_location", '1 1 1');
249 loc = findnearest(p, target, "###item###", '1 1 4');
256 string formatmessage(string msg)
267 WarpZone_crosshair_trace(self);
268 cursor = trace_endpos;
269 cursor_ent = trace_ent;
273 break; // too many replacements
276 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
277 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
290 replacement = substring(msg, p, 2);
291 escape = substring(msg, p + 1, 1);
295 else if (escape == "\\")
297 else if (escape == "n")
299 else if (escape == "a")
300 replacement = ftos(floor(self.armorvalue));
301 else if (escape == "h")
302 replacement = ftos(floor(self.health));
303 else if (escape == "l")
304 replacement = NearestLocation(self.origin);
305 else if (escape == "y")
306 replacement = NearestLocation(cursor);
307 else if (escape == "d")
308 replacement = NearestLocation(self.death_origin);
309 else if (escape == "w") {
313 wep = self.switchweapon;
316 replacement = WEP_NAME(wep);
317 } else if (escape == "W") {
318 if (self.items & IT_SHELLS) replacement = "shells";
319 else if (self.items & IT_NAILS) replacement = "bullets";
320 else if (self.items & IT_ROCKETS) replacement = "rockets";
321 else if (self.items & IT_CELLS) replacement = "cells";
322 else if (self.items & IT_PLASMA) replacement = "plasma";
323 else replacement = "batteries"; // ;)
324 } else if (escape == "x") {
325 replacement = cursor_ent.netname;
326 if (replacement == "" || !cursor_ent)
327 replacement = "nothing";
328 } else if (escape == "s")
329 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
330 else if (escape == "S")
331 replacement = ftos(vlen(self.velocity));
333 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
334 p = p + strlen(replacement);
339 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
340 return (value == 0) ? false : true;
349 >0: receives a cvar from name=argv(f) value=argv(f+1)
351 void GetCvars_handleString(string thisname, float f, .string field, string name)
356 strunzone(self.(field));
357 self.(field) = string_null;
361 if (thisname == name)
364 strunzone(self.(field));
365 self.(field) = strzone(argv(f + 1));
369 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
371 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
373 GetCvars_handleString(thisname, f, field, name);
374 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
375 if (thisname == name)
377 string s = func(strcat1(self.(field)));
378 if (s != self.(field))
380 strunzone(self.(field));
381 self.(field) = strzone(s);
385 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
392 if (thisname == name)
393 self.(field) = stof(argv(f + 1));
396 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
398 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
405 if (thisname == name)
409 self.(field) = stof(argv(f + 1));
418 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
421 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
424 o = W_FixWeaponOrder_ForceComplete(wo);
425 if(self.weaponorder_byimpulse)
427 strunzone(self.weaponorder_byimpulse);
428 self.weaponorder_byimpulse = string_null;
430 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
433 void GetCvars(float f)
435 string s = string_null;
438 s = strcat1(argv(f));
443 MUTATOR_CALLHOOK(GetCvars);
445 Notification_GetCvars();
447 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
448 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
449 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
450 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
451 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
452 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
453 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
454 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
455 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
456 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
457 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
458 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
459 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
465 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
466 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
467 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
468 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
469 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
470 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
472 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
473 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
475 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
476 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
477 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
478 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
479 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
481 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
484 if (s == "cl_weaponpriority")
485 self.switchweapon = w_getbestweapon(self);
486 if (s == "cl_allow_uidtracking")
487 PlayerStats_GameReport_AddPlayer(self);
491 // decolorizes and team colors the player name when needed
492 string playername(entity p)
495 if (teamplay && !intermission_running && IS_PLAYER(p))
497 t = Team_ColorCode(p.team);
498 return strcat(t, strdecolorize(p.netname));
504 vector randompos(vector m1, vector m2)
508 v.x = m2_x * random() + m1_x;
509 v.y = m2_y * random() + m1_y;
510 v.z = m2_z * random() + m1_z;
514 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
516 int i = weaponinfo.weapon;
522 if (g_lms || g_ca || allguns)
524 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
530 d = (i == WEP_SHOTGUN);
532 d = 0; // weapon is set a few lines later
534 d = !(!weaponinfo.weaponstart);
536 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
537 d |= (i == WEP_HOOK);
538 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
541 float t = weaponinfo.weaponstartoverride;
543 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
548 // 4: is set by default?
557 void readplayerstartcvars()
563 // initialize starting values for players
564 start_weapons = '0 0 0';
565 start_weapons_default = '0 0 0';
566 start_weapons_defaultmask = '0 0 0';
568 start_ammo_shells = 0;
569 start_ammo_nails = 0;
570 start_ammo_rockets = 0;
571 start_ammo_cells = 0;
572 start_ammo_plasma = 0;
573 start_health = cvar("g_balance_health_start");
574 start_armorvalue = cvar("g_balance_armor_start");
577 g_weaponarena_weapons = '0 0 0';
579 s = cvar_string("g_weaponarena");
580 if (s == "0" || s == "")
586 if (s == "0" || s == "")
592 // forcibly turn off weaponarena
594 else if (s == "all" || s == "1")
597 g_weaponarena_list = "All Weapons";
598 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
600 e = get_weaponinfo(j);
601 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
602 g_weaponarena_weapons |= WepSet_FromWeapon(j);
605 else if (s == "most")
608 g_weaponarena_list = "Most Weapons";
609 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
611 e = get_weaponinfo(j);
612 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
613 if (e.spawnflags & WEP_FLAG_NORMAL)
614 g_weaponarena_weapons |= WepSet_FromWeapon(j);
617 else if (s == "none")
620 g_weaponarena_list = "No Weapons";
625 t = tokenize_console(s);
626 g_weaponarena_list = "";
627 for (i = 0; i < t; ++i)
630 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
632 e = get_weaponinfo(j);
635 g_weaponarena_weapons |= WepSet_FromWeapon(j);
636 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
642 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
645 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
649 g_weaponarena_random = cvar("g_weaponarena_random");
651 g_weaponarena_random = 0;
652 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
656 g_weapon_stay = 0; // incompatible
657 start_weapons = g_weaponarena_weapons;
658 start_items |= IT_UNLIMITED_AMMO;
662 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
664 e = get_weaponinfo(i);
665 int w = want_weapon(e, false);
667 start_weapons |= WepSet_FromWeapon(i);
669 start_weapons_default |= WepSet_FromWeapon(i);
671 start_weapons_defaultmask |= WepSet_FromWeapon(i);
675 if(!cvar("g_use_ammunition"))
676 start_items |= IT_UNLIMITED_AMMO;
678 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
680 start_ammo_shells = 999;
681 start_ammo_nails = 999;
682 start_ammo_rockets = 999;
683 start_ammo_cells = 999;
684 start_ammo_plasma = 999;
685 start_ammo_fuel = 999;
689 start_ammo_shells = cvar("g_start_ammo_shells");
690 start_ammo_nails = cvar("g_start_ammo_nails");
691 start_ammo_rockets = cvar("g_start_ammo_rockets");
692 start_ammo_cells = cvar("g_start_ammo_cells");
693 start_ammo_plasma = cvar("g_start_ammo_plasma");
694 start_ammo_fuel = cvar("g_start_ammo_fuel");
699 warmup_start_ammo_shells = start_ammo_shells;
700 warmup_start_ammo_nails = start_ammo_nails;
701 warmup_start_ammo_rockets = start_ammo_rockets;
702 warmup_start_ammo_cells = start_ammo_cells;
703 warmup_start_ammo_plasma = start_ammo_plasma;
704 warmup_start_ammo_fuel = start_ammo_fuel;
705 warmup_start_health = start_health;
706 warmup_start_armorvalue = start_armorvalue;
707 warmup_start_weapons = start_weapons;
708 warmup_start_weapons_default = start_weapons_default;
709 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
711 if (!g_weaponarena && !g_ca)
713 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
714 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
715 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
716 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
717 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
718 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
719 warmup_start_health = cvar("g_warmup_start_health");
720 warmup_start_armorvalue = cvar("g_warmup_start_armor");
721 warmup_start_weapons = '0 0 0';
722 warmup_start_weapons_default = '0 0 0';
723 warmup_start_weapons_defaultmask = '0 0 0';
724 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
726 e = get_weaponinfo(i);
727 int w = want_weapon(e, g_warmup_allguns);
729 warmup_start_weapons |= WepSet_FromWeapon(i);
731 warmup_start_weapons_default |= WepSet_FromWeapon(i);
733 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
739 start_items |= IT_JETPACK;
741 MUTATOR_CALLHOOK(SetStartItems);
743 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
745 start_items |= IT_FUEL_REGEN;
746 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
747 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
750 WepSet precache_weapons = start_weapons;
751 if (g_warmup_allguns != 1)
752 precache_weapons |= warmup_start_weapons;
753 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
755 e = get_weaponinfo(i);
756 if(precache_weapons & WepSet_FromWeapon(i))
757 WEP_ACTION(i, WR_INIT);
760 start_ammo_shells = max(0, start_ammo_shells);
761 start_ammo_nails = max(0, start_ammo_nails);
762 start_ammo_rockets = max(0, start_ammo_rockets);
763 start_ammo_cells = max(0, start_ammo_cells);
764 start_ammo_plasma = max(0, start_ammo_plasma);
765 start_ammo_fuel = max(0, start_ammo_fuel);
767 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
768 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
769 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
770 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
771 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
772 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
775 float sound_allowed(float _dest, entity e)
777 // sounds from world may always pass
780 if (e.classname == "body")
782 else if (e.realowner && e.realowner != e)
784 else if (e.owner && e.owner != e)
789 // sounds to self may always pass
790 if (_dest == MSG_ONE)
793 // sounds by players can be removed
794 if (autocvar_bot_sound_monopoly)
795 if (IS_REAL_CLIENT(e))
797 // anything else may pass
802 void sound(entity e, float chan, string samp, float vol, float _atten)
804 if (!sound_allowed(MSG_BROADCAST, e))
806 sound7(e, chan, samp, vol, _atten, 0, 0);
809 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
813 if (!sound_allowed(_dest, e))
816 entno = num_for_edict(e);
817 idx = precache_sound_index(samp);
822 _atten = floor(_atten * 64);
823 vol = floor(vol * 255);
826 sflags |= SND_VOLUME;
828 sflags |= SND_ATTENUATION;
829 if (entno >= 8192 || chan < 0 || chan > 7)
830 sflags |= SND_LARGEENTITY;
832 sflags |= SND_LARGESOUND;
834 WriteByte(_dest, SVC_SOUND);
835 WriteByte(_dest, sflags);
836 if (sflags & SND_VOLUME)
837 WriteByte(_dest, vol);
838 if (sflags & SND_ATTENUATION)
839 WriteByte(_dest, _atten);
840 if (sflags & SND_LARGEENTITY)
842 WriteShort(_dest, entno);
843 WriteByte(_dest, chan);
847 WriteShort(_dest, entno * 8 + chan);
849 if (sflags & SND_LARGESOUND)
850 WriteShort(_dest, idx);
852 WriteByte(_dest, idx);
854 WriteCoord(_dest, o.x);
855 WriteCoord(_dest, o.y);
856 WriteCoord(_dest, o.z);
858 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
862 if (!sound_allowed(_dest, e))
865 o = e.origin + 0.5 * (e.mins + e.maxs);
866 soundtoat(_dest, e, o, chan, samp, vol, _atten);
868 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
870 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
872 void stopsoundto(float _dest, entity e, float chan)
876 if (!sound_allowed(_dest, e))
879 entno = num_for_edict(e);
881 if (entno >= 8192 || chan < 0 || chan > 7)
884 idx = precache_sound_index("misc/null.wav");
885 sflags = SND_LARGEENTITY;
887 sflags |= SND_LARGESOUND;
888 WriteByte(_dest, SVC_SOUND);
889 WriteByte(_dest, sflags);
890 WriteShort(_dest, entno);
891 WriteByte(_dest, chan);
892 if (sflags & SND_LARGESOUND)
893 WriteShort(_dest, idx);
895 WriteByte(_dest, idx);
896 WriteCoord(_dest, e.origin.x);
897 WriteCoord(_dest, e.origin.y);
898 WriteCoord(_dest, e.origin.z);
902 WriteByte(_dest, SVC_STOPSOUND);
903 WriteShort(_dest, entno * 8 + chan);
906 void stopsound(entity e, float chan)
908 if (!sound_allowed(MSG_BROADCAST, e))
911 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
912 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
915 void play2(entity e, string filename)
917 //stuffcmd(e, strcat("play2 ", filename, "\n"));
919 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
922 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
924 float spamsound(entity e, float chan, string samp, float vol, float _atten)
926 if (!sound_allowed(MSG_BROADCAST, e))
929 if (time > e.spamtime)
932 sound(e, chan, samp, vol, _atten);
938 void play2team(float t, string filename)
942 if (autocvar_bot_sound_monopoly)
945 FOR_EACH_REALPLAYER(head)
948 play2(head, filename);
952 void play2all(string samp)
954 if (autocvar_bot_sound_monopoly)
957 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
960 void PrecachePlayerSounds(string f);
961 void precache_playermodel(string m)
963 float globhandle, i, n;
966 if(substring(m, -9,5) == "_lod1")
968 if(substring(m, -9,5) == "_lod2")
971 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
974 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
978 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
981 n = search_getsize(globhandle);
982 for (i = 0; i < n; ++i)
984 //print(search_getfilename(globhandle, i), "\n");
985 f = search_getfilename(globhandle, i);
986 PrecachePlayerSounds(f);
988 search_end(globhandle);
990 void precache_all_playermodels(string pattern)
992 float globhandle, i, n;
995 globhandle = search_begin(pattern, true, false);
998 n = search_getsize(globhandle);
999 for (i = 0; i < n; ++i)
1001 //print(search_getfilename(globhandle, i), "\n");
1002 f = search_getfilename(globhandle, i);
1003 precache_playermodel(f);
1005 search_end(globhandle);
1010 // gamemode related things
1011 precache_model ("models/misc/chatbubble.spr");
1012 precache_model("models/ice/ice.md3");
1014 #ifdef TTURRETS_ENABLED
1015 if (autocvar_g_turrets)
1019 // Precache all player models if desired
1020 if (autocvar_sv_precacheplayermodels)
1022 PrecachePlayerSounds("sound/player/default.sounds");
1023 precache_all_playermodels("models/player/*.zym");
1024 precache_all_playermodels("models/player/*.dpm");
1025 precache_all_playermodels("models/player/*.md3");
1026 precache_all_playermodels("models/player/*.psk");
1027 precache_all_playermodels("models/player/*.iqm");
1030 if (autocvar_sv_defaultcharacter)
1033 s = autocvar_sv_defaultplayermodel_red;
1035 precache_playermodel(s);
1036 s = autocvar_sv_defaultplayermodel_blue;
1038 precache_playermodel(s);
1039 s = autocvar_sv_defaultplayermodel_yellow;
1041 precache_playermodel(s);
1042 s = autocvar_sv_defaultplayermodel_pink;
1044 precache_playermodel(s);
1045 s = autocvar_sv_defaultplayermodel;
1047 precache_playermodel(s);
1052 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1053 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1056 // gore and miscellaneous sounds
1057 //precache_sound ("misc/h2ohit.wav");
1058 precache_model ("models/hook.md3");
1059 precache_sound ("misc/armorimpact.wav");
1060 precache_sound ("misc/bodyimpact1.wav");
1061 precache_sound ("misc/bodyimpact2.wav");
1062 precache_sound ("misc/gib.wav");
1063 precache_sound ("misc/gib_splat01.wav");
1064 precache_sound ("misc/gib_splat02.wav");
1065 precache_sound ("misc/gib_splat03.wav");
1066 precache_sound ("misc/gib_splat04.wav");
1067 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1068 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1069 precache_sound ("misc/null.wav");
1070 precache_sound ("misc/spawn.wav");
1071 precache_sound ("misc/talk.wav");
1072 precache_sound ("misc/teleport.wav");
1073 precache_sound ("misc/poweroff.wav");
1074 precache_sound ("player/lava.wav");
1075 precache_sound ("player/slime.wav");
1077 precache_model ("models/sprites/0.spr32");
1078 precache_model ("models/sprites/1.spr32");
1079 precache_model ("models/sprites/2.spr32");
1080 precache_model ("models/sprites/3.spr32");
1081 precache_model ("models/sprites/4.spr32");
1082 precache_model ("models/sprites/5.spr32");
1083 precache_model ("models/sprites/6.spr32");
1084 precache_model ("models/sprites/7.spr32");
1085 precache_model ("models/sprites/8.spr32");
1086 precache_model ("models/sprites/9.spr32");
1087 precache_model ("models/sprites/10.spr32");
1089 // common weapon precaches
1090 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1091 precache_sound ("weapons/weapon_switch.wav");
1092 precache_sound ("weapons/weaponpickup.wav");
1093 precache_sound ("weapons/unavailable.wav");
1094 precache_sound ("weapons/dryfire.wav");
1095 if (g_grappling_hook)
1097 precache_sound ("weapons/hook_fire.wav"); // hook
1098 precache_sound ("weapons/hook_impact.wav"); // hook
1101 precache_model("models/elaser.mdl");
1102 precache_model("models/laser.mdl");
1103 precache_model("models/ebomb.mdl");
1106 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1108 if (!self.noise && self.music) // quake 3 uses the music field
1109 self.noise = self.music;
1111 // plays music for the level if there is any
1114 precache_sound (self.noise);
1115 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1119 #include "precache-for-csqc.inc"
1123 void make_safe_for_remove(entity e)
1125 if (e.initialize_entity)
1127 entity ent, prev = world;
1128 for (ent = initialize_entity_first; ent; )
1130 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1132 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1133 // skip it in linked list
1136 prev.initialize_entity_next = ent.initialize_entity_next;
1137 ent = prev.initialize_entity_next;
1141 initialize_entity_first = ent.initialize_entity_next;
1142 ent = initialize_entity_first;
1148 ent = ent.initialize_entity_next;
1154 void objerror(string s)
1156 make_safe_for_remove(self);
1157 builtin_objerror(s);
1160 .float remove_except_protected_forbidden;
1161 void remove_except_protected(entity e)
1163 if(e.remove_except_protected_forbidden)
1164 error("not allowed to remove this at this point");
1168 void remove_unsafely(entity e)
1170 if(e.classname == "spike")
1171 error("Removing spikes is forbidden (crylink bug), please report");
1175 void remove_safely(entity e)
1177 make_safe_for_remove(e);
1181 void InitializeEntity(entity e, void(void) func, float order)
1185 if (!e || e.initialize_entity)
1187 // make a proxy initializer entity
1191 e.classname = "initialize_entity";
1195 e.initialize_entity = func;
1196 e.initialize_entity_order = order;
1198 cur = initialize_entity_first;
1202 if (!cur || cur.initialize_entity_order > order)
1204 // insert between prev and cur
1206 prev.initialize_entity_next = e;
1208 initialize_entity_first = e;
1209 e.initialize_entity_next = cur;
1213 cur = cur.initialize_entity_next;
1216 void InitializeEntitiesRun()
1219 startoflist = initialize_entity_first;
1220 initialize_entity_first = world;
1221 remove = remove_except_protected;
1222 for (self = startoflist; self; self = self.initialize_entity_next)
1224 self.remove_except_protected_forbidden = 1;
1226 for (self = startoflist; self; )
1229 var void(void) func;
1230 e = self.initialize_entity_next;
1231 func = self.initialize_entity;
1232 self.initialize_entity_order = 0;
1233 self.initialize_entity = func_null;
1234 self.initialize_entity_next = world;
1235 self.remove_except_protected_forbidden = 0;
1236 if (self.classname == "initialize_entity")
1240 builtin_remove(self);
1243 //dprint("Delayed initialization: ", self.classname, "\n");
1249 backtrace(strcat("Null function in: ", self.classname, "\n"));
1253 remove = remove_unsafely;
1256 void UncustomizeEntitiesRun()
1260 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1261 self.uncustomizeentityforclient();
1264 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1266 e.customizeentityforclient = customizer;
1267 e.uncustomizeentityforclient = uncustomizer;
1268 e.uncustomizeentityforclient_set = !!uncustomizer;
1272 void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) sendfunc)
1276 if (e.classname == "")
1277 e.classname = "net_linked";
1279 if (e.model == "" || self.modelindex == 0)
1283 setmodel(e, "null");
1287 e.SendEntity = sendfunc;
1288 e.SendFlags = 0xFFFFFF;
1291 e.effects |= EF_NODEPTHTEST;
1295 e.nextthink = time + dt;
1296 e.think = SUB_Remove;
1301 .float(entity) isEliminated;
1302 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1306 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1307 WriteByte(MSG_ENTITY, sendflags);
1311 for(i = 1; i <= maxclients; i += 8)
1313 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1315 if(eliminatedPlayers.isEliminated(e))
1318 WriteByte(MSG_ENTITY, f);
1325 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1327 if(eliminatedPlayers)
1329 backtrace("Can't spawn eliminatedPlayers again!");
1332 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1333 eliminatedPlayers.isEliminated = isEliminated_func;
1337 void adaptor_think2touch()
1346 void adaptor_think2use()
1358 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1360 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
1361 self.projectiledeathtype |= HITTYPE_SPLASH;
1362 adaptor_think2use();
1365 // deferred dropping
1366 void DropToFloor_Handler()
1368 builtin_droptofloor();
1369 self.dropped_origin = self.origin;
1374 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1379 float trace_hits_box_a0, trace_hits_box_a1;
1381 float trace_hits_box_1d(float end, float thmi, float thma)
1385 // just check if x is in range
1393 // do the trace with respect to x
1394 // 0 -> end has to stay in thmi -> thma
1395 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1396 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1397 if (trace_hits_box_a0 > trace_hits_box_a1)
1403 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1408 // now it is a trace from 0 to end
1410 trace_hits_box_a0 = 0;
1411 trace_hits_box_a1 = 1;
1413 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1415 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1417 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1423 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1425 return trace_hits_box(start, end, thmi - ma, thma - mi);
1428 float SUB_NoImpactCheck()
1430 // zero hitcontents = this is not the real impact, but either the
1431 // mirror-impact of something hitting the projectile instead of the
1432 // projectile hitting the something, or a touchareagrid one. Neither of
1433 // these stop the projectile from moving, so...
1434 if(trace_dphitcontents == 0)
1436 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1437 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));
1440 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1442 if (other == world && self.size != '0 0 0')
1445 tic = self.velocity * sys_frametime;
1446 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1447 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1448 if (trace_fraction >= 1)
1450 dprint("Odd... did not hit...?\n");
1452 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1454 dprint("Detected and prevented the sky-grapple bug.\n");
1462 #define SUB_OwnerCheck() (other && (other == self.owner))
1464 void RemoveGrapplingHook(entity pl);
1465 void W_Crylink_Dequeue(entity e);
1466 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1468 if(SUB_OwnerCheck())
1470 if(SUB_NoImpactCheck())
1472 if(self.classname == "nade")
1473 return false; // no checks here
1474 else if(self.classname == "grapplinghook")
1475 RemoveGrapplingHook(self.realowner);
1476 else if(self.classname == "spike")
1478 W_Crylink_Dequeue(self);
1485 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1486 UpdateCSQCProjectile(self);
1491 void URI_Get_Callback(float id, float status, string data)
1493 if(url_URI_Get_Callback(id, status, data))
1497 else if (id == URI_GET_DISCARD)
1501 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1504 Curl_URI_Get_Callback(id, status, data);
1506 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1509 OnlineBanList_URI_Get_Callback(id, status, data);
1513 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1517 string uid2name(string myuid) {
1519 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1521 // FIXME remove this later after 0.6 release
1522 // convert old style broken records to correct style
1525 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1528 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1529 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1534 s = "^1Unregistered Player";
1538 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1541 vector start, org, delta, end, enddown, mstart;
1544 m = e.dphitcontentsmask;
1545 e.dphitcontentsmask = goodcontents | badcontents;
1548 delta = world.maxs - world.mins;
1552 for (i = 0; i < attempts; ++i)
1554 start.x = org.x + random() * delta.x;
1555 start.y = org.y + random() * delta.y;
1556 start.z = org.z + random() * delta.z;
1558 // rule 1: start inside world bounds, and outside
1559 // solid, and don't start from somewhere where you can
1560 // fall down to evil
1561 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1562 if (trace_fraction >= 1)
1564 if (trace_startsolid)
1566 if (trace_dphitcontents & badcontents)
1568 if (trace_dphitq3surfaceflags & badsurfaceflags)
1571 // rule 2: if we are too high, lower the point
1572 if (trace_fraction * delta.z > maxaboveground)
1573 start = trace_endpos + '0 0 1' * maxaboveground;
1574 enddown = trace_endpos;
1576 // rule 3: make sure we aren't outside the map. This only works
1577 // for somewhat well formed maps. A good rule of thumb is that
1578 // the map should have a convex outside hull.
1579 // these can be traceLINES as we already verified the starting box
1580 mstart = start + 0.5 * (e.mins + e.maxs);
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 - '1 0 0' * delta.x, 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 1 0' * delta.y, MOVE_NORMAL, e);
1591 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1593 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1594 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1597 // rule 4: we must "see" some spawnpoint or item
1598 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1599 if(checkpvs(mstart, sp))
1600 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1604 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1605 if(checkpvs(mstart, sp))
1606 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1612 // find a random vector to "look at"
1613 end.x = org.x + random() * delta.x;
1614 end.y = org.y + random() * delta.y;
1615 end.z = org.z + random() * delta.z;
1616 end = start + normalize(end - start) * vlen(delta);
1618 // rule 4: start TO end must not be too short
1619 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1620 if (trace_startsolid)
1622 if (trace_fraction < minviewdistance / vlen(delta))
1625 // rule 5: don't want to look at sky
1626 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1629 // rule 6: we must not end up in trigger_hurt
1630 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1636 e.dphitcontentsmask = m;
1640 setorigin(e, start);
1641 e.angles = vectoangles(end - start);
1642 dprint("Needed ", ftos(i + 1), " attempts\n");
1649 void write_recordmarker(entity pl, float tstart, float dt)
1651 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1653 // also write a marker into demo files for demotc-race-record-extractor to find
1656 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1657 " ", ftos(tstart), " ", ftos(dt), "\n"));
1660 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1673 if(allowcenter) // 2: allow center handedness
1686 if(allowcenter) // 2: allow center handedness
1702 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1707 if (autocvar_g_shootfromeye)
1711 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1712 else { vecs.y = 0; vecs.z -= 2; }
1720 else if (autocvar_g_shootfromcenter)
1725 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1735 else if (autocvar_g_shootfromclient)
1737 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1742 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1744 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1748 void attach_sameorigin(entity e, entity to, string tag)
1750 vector org, t_forward, t_left, t_up, e_forward, e_up;
1753 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1754 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1755 t_forward = v_forward * tagscale;
1756 t_left = v_right * -tagscale;
1757 t_up = v_up * tagscale;
1759 e.origin_x = org * t_forward;
1760 e.origin_y = org * t_left;
1761 e.origin_z = org * t_up;
1763 // current forward and up directions
1764 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1765 e.angles = AnglesTransform_FromVAngles(e.angles);
1767 e.angles = AnglesTransform_FromAngles(e.angles);
1768 fixedmakevectors(e.angles);
1770 // untransform forward, up!
1771 e_forward.x = v_forward * t_forward;
1772 e_forward.y = v_forward * t_left;
1773 e_forward.z = v_forward * t_up;
1774 e_up.x = v_up * t_forward;
1775 e_up.y = v_up * t_left;
1776 e_up.z = v_up * t_up;
1778 e.angles = fixedvectoangles2(e_forward, e_up);
1779 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1780 e.angles = AnglesTransform_ToVAngles(e.angles);
1782 e.angles = AnglesTransform_ToAngles(e.angles);
1784 setattachment(e, to, tag);
1785 setorigin(e, e.origin);
1788 void detach_sameorigin(entity e)
1791 org = gettaginfo(e, 0);
1792 e.angles = fixedvectoangles2(v_forward, v_up);
1793 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1794 e.angles = AnglesTransform_ToVAngles(e.angles);
1796 e.angles = AnglesTransform_ToAngles(e.angles);
1798 setattachment(e, world, "");
1799 setorigin(e, e.origin);
1802 void follow_sameorigin(entity e, entity to)
1804 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1805 e.aiment = to; // make the hole follow bmodel
1806 e.punchangle = to.angles; // the original angles of bmodel
1807 e.view_ofs = e.origin - to.origin; // relative origin
1808 e.v_angle = e.angles - to.angles; // relative angles
1811 void unfollow_sameorigin(entity e)
1813 e.movetype = MOVETYPE_NONE;
1816 entity gettaginfo_relative_ent;
1817 vector gettaginfo_relative(entity e, float tag)
1819 if (!gettaginfo_relative_ent)
1821 gettaginfo_relative_ent = spawn();
1822 gettaginfo_relative_ent.effects = EF_NODRAW;
1824 gettaginfo_relative_ent.model = e.model;
1825 gettaginfo_relative_ent.modelindex = e.modelindex;
1826 gettaginfo_relative_ent.frame = e.frame;
1827 return gettaginfo(gettaginfo_relative_ent, tag);
1832 float modeleffect_SendEntity(entity to, int sf)
1835 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1838 if(self.velocity != '0 0 0')
1840 if(self.angles != '0 0 0')
1842 if(self.avelocity != '0 0 0')
1845 WriteByte(MSG_ENTITY, f);
1846 WriteShort(MSG_ENTITY, self.modelindex);
1847 WriteByte(MSG_ENTITY, self.skin);
1848 WriteByte(MSG_ENTITY, self.frame);
1849 WriteCoord(MSG_ENTITY, self.origin.x);
1850 WriteCoord(MSG_ENTITY, self.origin.y);
1851 WriteCoord(MSG_ENTITY, self.origin.z);
1854 WriteCoord(MSG_ENTITY, self.velocity.x);
1855 WriteCoord(MSG_ENTITY, self.velocity.y);
1856 WriteCoord(MSG_ENTITY, self.velocity.z);
1860 WriteCoord(MSG_ENTITY, self.angles.x);
1861 WriteCoord(MSG_ENTITY, self.angles.y);
1862 WriteCoord(MSG_ENTITY, self.angles.z);
1866 WriteCoord(MSG_ENTITY, self.avelocity.x);
1867 WriteCoord(MSG_ENTITY, self.avelocity.y);
1868 WriteCoord(MSG_ENTITY, self.avelocity.z);
1870 WriteShort(MSG_ENTITY, self.scale * 256.0);
1871 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1872 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1873 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1874 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1879 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)
1884 e.classname = "modeleffect";
1892 e.teleport_time = t1;
1896 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1900 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1903 sz = max(e.scale, e.scale2);
1904 setsize(e, e.mins * sz, e.maxs * sz);
1905 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1908 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1910 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1913 float randombit(float bits)
1915 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1924 for(f = 1; f <= bits; f *= 2)
1933 r = (r - 1) / (n - 1);
1940 float randombits(float bits, float k, float error_return)
1944 while(k > 0 && bits != r)
1946 r += randombit(bits - r);
1955 void randombit_test(float bits, float iter)
1959 print(ftos(randombit(bits)), "\n");
1964 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1966 if(halflifedist > 0)
1967 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1968 else if(halflifedist < 0)
1969 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1981 oself.think = SUB_Remove;
1982 oself.nextthink = time;
1988 Execute func() after time + fdelay.
1989 self when func is executed = self when defer is called
1991 void defer(float fdelay, void() func)
1998 e.think = defer_think;
1999 e.nextthink = time + fdelay;
2002 .string aiment_classname;
2003 .float aiment_deadflag;
2004 void SetMovetypeFollow(entity ent, entity e)
2006 // FIXME this may not be warpzone aware
2007 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2008 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.
2009 ent.aiment = e; // make the hole follow bmodel
2010 ent.punchangle = e.angles; // the original angles of bmodel
2011 ent.view_ofs = ent.origin - e.origin; // relative origin
2012 ent.v_angle = ent.angles - e.angles; // relative angles
2013 ent.aiment_classname = strzone(e.classname);
2014 ent.aiment_deadflag = e.deadflag;
2016 void UnsetMovetypeFollow(entity ent)
2018 ent.movetype = MOVETYPE_FLY;
2019 PROJECTILE_MAKETRIGGER(ent);
2022 float LostMovetypeFollow(entity ent)
2025 if(ent.movetype != MOVETYPE_FOLLOW)
2031 if(ent.aiment.classname != ent.aiment_classname)
2033 if(ent.aiment.deadflag != ent.aiment_deadflag)
2039 float isPushable(entity e)
2048 case "droppedweapon":
2049 case "keepawayball":
2050 case "nexball_basketball":
2051 case "nexball_football":
2053 case "bullet": // antilagged bullets can't hit this either
2056 if (e.projectiledeathtype)