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 "../common/mapinfo.qh"
27 #include "command/common.qh"
28 #include "../csqcmodellib/sv_model.qh"
32 void crosshair_trace(entity pl)
34 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));
36 void crosshair_trace_plusvisibletriggers(entity pl)
40 first = findchainfloat(solid, SOLID_TRIGGER);
42 for (e = first; e; e = e.chain)
48 for (e = first; e; e = e.chain)
49 e.solid = SOLID_TRIGGER;
51 void WarpZone_crosshair_trace(entity pl)
53 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));
57 string admin_name(void)
59 if(autocvar_sv_adminnick != "")
60 return autocvar_sv_adminnick;
62 return "SERVER ADMIN";
65 void DistributeEvenly_Init(float amount, float totalweight)
67 if (DistributeEvenly_amount)
69 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
70 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
73 DistributeEvenly_amount = 0;
75 DistributeEvenly_amount = amount;
76 DistributeEvenly_totalweight = totalweight;
78 float DistributeEvenly_Get(float weight)
83 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
84 DistributeEvenly_totalweight -= weight;
85 DistributeEvenly_amount -= f;
88 float DistributeEvenly_GetRandomized(float weight)
93 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
94 DistributeEvenly_totalweight -= weight;
95 DistributeEvenly_amount -= f;
100 void GameLogEcho(string s)
105 if (autocvar_sv_eventlog_files)
110 matches = autocvar_sv_eventlog_files_counter + 1;
111 cvar_set("sv_eventlog_files_counter", ftos(matches));
114 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
115 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
116 logfile = fopen(fn, FILE_APPEND);
117 fputs(logfile, ":logversion:3\n");
121 if (autocvar_sv_eventlog_files_timestamps)
122 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
124 fputs(logfile, strcat(s, "\n"));
127 if (autocvar_sv_eventlog_console)
136 // will be opened later
141 if (logfile_open && logfile >= 0)
148 entity findnearest(vector point, .string field, string value, vector axismod)
159 localhead = find(world, field, value);
162 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
163 dist = localhead.oldorigin;
165 dist = localhead.origin;
167 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
170 for (i = 0; i < num_nearest; ++i)
172 if (len < nearest_length[i])
176 // now i tells us where to insert at
177 // INSERTION SORT! YOU'VE SEEN IT! RUN!
178 if (i < NUM_NEAREST_ENTITIES)
180 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
182 nearest_length[j + 1] = nearest_length[j];
183 nearest_entity[j + 1] = nearest_entity[j];
185 nearest_length[i] = len;
186 nearest_entity[i] = localhead;
187 if (num_nearest < NUM_NEAREST_ENTITIES)
188 num_nearest = num_nearest + 1;
191 localhead = find(localhead, field, value);
194 // now use the first one from our list that we can see
195 for (i = 0; i < num_nearest; ++i)
197 traceline(point, nearest_entity[i].origin, true, world);
198 if (trace_fraction == 1)
202 dprint("Nearest point (");
203 dprint(nearest_entity[0].netname);
204 dprint(") is not visible, using a visible one.\n");
206 return nearest_entity[i];
210 if (num_nearest == 0)
213 dprint("Not seeing any location point, using nearest as fallback.\n");
215 dprint("Candidates were: ");
216 for(j = 0; j < num_nearest; ++j)
220 dprint(nearest_entity[j].netname);
225 return nearest_entity[0];
228 void spawnfunc_target_location()
230 self.classname = "target_location";
231 // location name in netname
232 // eventually support: count, teamgame selectors, line of sight?
235 void spawnfunc_info_location()
237 self.classname = "target_location";
238 self.message = self.netname;
241 string NearestLocation(vector p)
246 loc = findnearest(p, classname, "target_location", '1 1 1');
253 loc = findnearest(p, target, "###item###", '1 1 4');
260 string formatmessage(string msg)
271 WarpZone_crosshair_trace(self);
272 cursor = trace_endpos;
273 cursor_ent = trace_ent;
277 break; // too many replacements
280 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
281 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
294 replacement = substring(msg, p, 2);
295 escape = substring(msg, p + 1, 1);
299 else if (escape == "\\")
301 else if (escape == "n")
303 else if (escape == "a")
304 replacement = ftos(floor(self.armorvalue));
305 else if (escape == "h")
306 replacement = ftos(floor(self.health));
307 else if (escape == "l")
308 replacement = NearestLocation(self.origin);
309 else if (escape == "y")
310 replacement = NearestLocation(cursor);
311 else if (escape == "d")
312 replacement = NearestLocation(self.death_origin);
313 else if (escape == "w") {
317 wep = self.switchweapon;
320 replacement = WEP_NAME(wep);
321 } else if (escape == "W") {
322 if (self.items & IT_SHELLS) replacement = "shells";
323 else if (self.items & IT_NAILS) replacement = "bullets";
324 else if (self.items & IT_ROCKETS) replacement = "rockets";
325 else if (self.items & IT_CELLS) replacement = "cells";
326 else if (self.items & IT_PLASMA) replacement = "plasma";
327 else replacement = "batteries"; // ;)
328 } else if (escape == "x") {
329 replacement = cursor_ent.netname;
330 if (replacement == "" || !cursor_ent)
331 replacement = "nothing";
332 } else if (escape == "s")
333 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
334 else if (escape == "S")
335 replacement = ftos(vlen(self.velocity));
337 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
338 p = p + strlen(replacement);
343 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
344 return (value == 0) ? false : true;
353 >0: receives a cvar from name=argv(f) value=argv(f+1)
355 void GetCvars_handleString(string thisname, float f, .string field, string name)
360 strunzone(self.field);
361 self.field = string_null;
365 if (thisname == name)
368 strunzone(self.field);
369 self.field = strzone(argv(f + 1));
373 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
375 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
377 GetCvars_handleString(thisname, f, field, name);
378 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
379 if (thisname == name)
382 s = func(strcat1(self.field));
385 strunzone(self.field);
386 self.field = strzone(s);
390 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
397 if (thisname == name)
398 self.field = stof(argv(f + 1));
401 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
403 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
410 if (thisname == name)
414 self.field = stof(argv(f + 1));
423 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
426 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
429 o = W_FixWeaponOrder_ForceComplete(wo);
430 if(self.weaponorder_byimpulse)
432 strunzone(self.weaponorder_byimpulse);
433 self.weaponorder_byimpulse = string_null;
435 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
438 void GetCvars(float f)
440 string s = string_null;
443 s = strcat1(argv(f));
448 MUTATOR_CALLHOOK(GetCvars);
450 Notification_GetCvars();
452 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
453 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
454 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
455 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
456 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
457 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
458 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
459 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
465 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
466 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
467 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
468 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
469 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
470 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
471 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
472 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
473 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
474 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
475 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
477 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
478 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
480 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
481 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
482 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
483 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
484 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
486 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
489 if (s == "cl_weaponpriority")
490 self.switchweapon = w_getbestweapon(self);
491 if (s == "cl_allow_uidtracking")
492 PlayerStats_GameReport_AddPlayer(self);
496 // decolorizes and team colors the player name when needed
497 string playername(entity p)
500 if (teamplay && !intermission_running && IS_PLAYER(p))
502 t = Team_ColorCode(p.team);
503 return strcat(t, strdecolorize(p.netname));
509 vector randompos(vector m1, vector m2)
513 v.x = m2_x * random() + m1_x;
514 v.y = m2_y * random() + m1_y;
515 v.z = m2_z * random() + m1_z;
519 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
521 int i = weaponinfo.weapon;
527 if (g_lms || g_ca || allguns)
529 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
535 d = (i == WEP_SHOTGUN);
537 d = 0; // weapon is set a few lines later
539 d = !(!weaponinfo.weaponstart);
541 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
542 d |= (i == WEP_HOOK);
543 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
546 float t = weaponinfo.weaponstartoverride;
548 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
553 // 4: is set by default?
562 void readplayerstartcvars()
568 // initialize starting values for players
569 start_weapons = '0 0 0';
570 start_weapons_default = '0 0 0';
571 start_weapons_defaultmask = '0 0 0';
573 start_ammo_shells = 0;
574 start_ammo_nails = 0;
575 start_ammo_rockets = 0;
576 start_ammo_cells = 0;
577 start_ammo_plasma = 0;
578 start_health = cvar("g_balance_health_start");
579 start_armorvalue = cvar("g_balance_armor_start");
582 g_weaponarena_weapons = '0 0 0';
584 s = cvar_string("g_weaponarena");
585 if (s == "0" || s == "")
591 if (s == "0" || s == "")
597 // forcibly turn off weaponarena
599 else if (s == "all" || s == "1")
602 g_weaponarena_list = "All Weapons";
603 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
605 e = get_weaponinfo(j);
606 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
607 g_weaponarena_weapons |= WepSet_FromWeapon(j);
610 else if (s == "most")
613 g_weaponarena_list = "Most Weapons";
614 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
616 e = get_weaponinfo(j);
617 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
618 if (e.spawnflags & WEP_FLAG_NORMAL)
619 g_weaponarena_weapons |= WepSet_FromWeapon(j);
622 else if (s == "none")
625 g_weaponarena_list = "No Weapons";
630 t = tokenize_console(s);
631 g_weaponarena_list = "";
632 for (i = 0; i < t; ++i)
635 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
637 e = get_weaponinfo(j);
640 g_weaponarena_weapons |= WepSet_FromWeapon(j);
641 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
647 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
650 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
654 g_weaponarena_random = cvar("g_weaponarena_random");
656 g_weaponarena_random = 0;
657 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
661 g_weapon_stay = 0; // incompatible
662 start_weapons = g_weaponarena_weapons;
663 start_items |= IT_UNLIMITED_AMMO;
667 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
669 e = get_weaponinfo(i);
670 int w = want_weapon(e, false);
672 start_weapons |= WepSet_FromWeapon(i);
674 start_weapons_default |= WepSet_FromWeapon(i);
676 start_weapons_defaultmask |= WepSet_FromWeapon(i);
680 if(!cvar("g_use_ammunition"))
681 start_items |= IT_UNLIMITED_AMMO;
683 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
685 start_ammo_shells = 999;
686 start_ammo_nails = 999;
687 start_ammo_rockets = 999;
688 start_ammo_cells = 999;
689 start_ammo_plasma = 999;
690 start_ammo_fuel = 999;
694 start_ammo_shells = cvar("g_start_ammo_shells");
695 start_ammo_nails = cvar("g_start_ammo_nails");
696 start_ammo_rockets = cvar("g_start_ammo_rockets");
697 start_ammo_cells = cvar("g_start_ammo_cells");
698 start_ammo_plasma = cvar("g_start_ammo_plasma");
699 start_ammo_fuel = cvar("g_start_ammo_fuel");
704 warmup_start_ammo_shells = start_ammo_shells;
705 warmup_start_ammo_nails = start_ammo_nails;
706 warmup_start_ammo_rockets = start_ammo_rockets;
707 warmup_start_ammo_cells = start_ammo_cells;
708 warmup_start_ammo_plasma = start_ammo_plasma;
709 warmup_start_ammo_fuel = start_ammo_fuel;
710 warmup_start_health = start_health;
711 warmup_start_armorvalue = start_armorvalue;
712 warmup_start_weapons = start_weapons;
713 warmup_start_weapons_default = start_weapons_default;
714 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
716 if (!g_weaponarena && !g_ca)
718 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
719 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
720 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
721 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
722 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
723 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
724 warmup_start_health = cvar("g_warmup_start_health");
725 warmup_start_armorvalue = cvar("g_warmup_start_armor");
726 warmup_start_weapons = '0 0 0';
727 warmup_start_weapons_default = '0 0 0';
728 warmup_start_weapons_defaultmask = '0 0 0';
729 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
731 e = get_weaponinfo(i);
732 int w = want_weapon(e, g_warmup_allguns);
734 warmup_start_weapons |= WepSet_FromWeapon(i);
736 warmup_start_weapons_default |= WepSet_FromWeapon(i);
738 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
744 start_items |= IT_JETPACK;
746 MUTATOR_CALLHOOK(SetStartItems);
748 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
750 start_items |= IT_FUEL_REGEN;
751 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
752 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
755 WepSet precache_weapons = start_weapons;
756 if (g_warmup_allguns != 1)
757 precache_weapons |= warmup_start_weapons;
758 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
760 e = get_weaponinfo(i);
761 if(precache_weapons & WepSet_FromWeapon(i))
762 WEP_ACTION(i, WR_INIT);
765 start_ammo_shells = max(0, start_ammo_shells);
766 start_ammo_nails = max(0, start_ammo_nails);
767 start_ammo_rockets = max(0, start_ammo_rockets);
768 start_ammo_cells = max(0, start_ammo_cells);
769 start_ammo_plasma = max(0, start_ammo_plasma);
770 start_ammo_fuel = max(0, start_ammo_fuel);
772 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
773 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
774 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
775 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
776 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
777 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
780 float sound_allowed(float _dest, entity e)
782 // sounds from world may always pass
785 if (e.classname == "body")
787 else if (e.realowner && e.realowner != e)
789 else if (e.owner && e.owner != e)
794 // sounds to self may always pass
795 if (_dest == MSG_ONE)
798 // sounds by players can be removed
799 if (autocvar_bot_sound_monopoly)
800 if (IS_REAL_CLIENT(e))
802 // anything else may pass
807 void sound(entity e, float chan, string samp, float vol, float _atten)
809 if (!sound_allowed(MSG_BROADCAST, e))
811 sound7(e, chan, samp, vol, _atten, 0, 0);
814 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
818 if (!sound_allowed(_dest, e))
821 entno = num_for_edict(e);
822 idx = precache_sound_index(samp);
827 _atten = floor(_atten * 64);
828 vol = floor(vol * 255);
831 sflags |= SND_VOLUME;
833 sflags |= SND_ATTENUATION;
834 if (entno >= 8192 || chan < 0 || chan > 7)
835 sflags |= SND_LARGEENTITY;
837 sflags |= SND_LARGESOUND;
839 WriteByte(_dest, SVC_SOUND);
840 WriteByte(_dest, sflags);
841 if (sflags & SND_VOLUME)
842 WriteByte(_dest, vol);
843 if (sflags & SND_ATTENUATION)
844 WriteByte(_dest, _atten);
845 if (sflags & SND_LARGEENTITY)
847 WriteShort(_dest, entno);
848 WriteByte(_dest, chan);
852 WriteShort(_dest, entno * 8 + chan);
854 if (sflags & SND_LARGESOUND)
855 WriteShort(_dest, idx);
857 WriteByte(_dest, idx);
859 WriteCoord(_dest, o.x);
860 WriteCoord(_dest, o.y);
861 WriteCoord(_dest, o.z);
863 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
867 if (!sound_allowed(_dest, e))
870 o = e.origin + 0.5 * (e.mins + e.maxs);
871 soundtoat(_dest, e, o, chan, samp, vol, _atten);
873 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
875 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
877 void stopsoundto(float _dest, entity e, float chan)
881 if (!sound_allowed(_dest, e))
884 entno = num_for_edict(e);
886 if (entno >= 8192 || chan < 0 || chan > 7)
889 idx = precache_sound_index("misc/null.wav");
890 sflags = SND_LARGEENTITY;
892 sflags |= SND_LARGESOUND;
893 WriteByte(_dest, SVC_SOUND);
894 WriteByte(_dest, sflags);
895 WriteShort(_dest, entno);
896 WriteByte(_dest, chan);
897 if (sflags & SND_LARGESOUND)
898 WriteShort(_dest, idx);
900 WriteByte(_dest, idx);
901 WriteCoord(_dest, e.origin.x);
902 WriteCoord(_dest, e.origin.y);
903 WriteCoord(_dest, e.origin.z);
907 WriteByte(_dest, SVC_STOPSOUND);
908 WriteShort(_dest, entno * 8 + chan);
911 void stopsound(entity e, float chan)
913 if (!sound_allowed(MSG_BROADCAST, e))
916 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
917 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
920 void play2(entity e, string filename)
922 //stuffcmd(e, strcat("play2 ", filename, "\n"));
924 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
927 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
929 float spamsound(entity e, float chan, string samp, float vol, float _atten)
931 if (!sound_allowed(MSG_BROADCAST, e))
934 if (time > e.spamtime)
937 sound(e, chan, samp, vol, _atten);
943 void play2team(float t, string filename)
947 if (autocvar_bot_sound_monopoly)
950 FOR_EACH_REALPLAYER(head)
953 play2(head, filename);
957 void play2all(string samp)
959 if (autocvar_bot_sound_monopoly)
962 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
965 void PrecachePlayerSounds(string f);
966 void precache_playermodel(string m)
968 float globhandle, i, n;
971 if(substring(m, -9,5) == "_lod1")
973 if(substring(m, -9,5) == "_lod2")
976 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
979 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
983 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
986 n = search_getsize(globhandle);
987 for (i = 0; i < n; ++i)
989 //print(search_getfilename(globhandle, i), "\n");
990 f = search_getfilename(globhandle, i);
991 PrecachePlayerSounds(f);
993 search_end(globhandle);
995 void precache_all_playermodels(string pattern)
997 float globhandle, i, n;
1000 globhandle = search_begin(pattern, true, false);
1003 n = search_getsize(globhandle);
1004 for (i = 0; i < n; ++i)
1006 //print(search_getfilename(globhandle, i), "\n");
1007 f = search_getfilename(globhandle, i);
1008 precache_playermodel(f);
1010 search_end(globhandle);
1015 // gamemode related things
1016 precache_model ("models/misc/chatbubble.spr");
1017 precache_model("models/ice/ice.md3");
1019 // 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, float(entity, float) 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 entity eliminatedPlayers;
1302 .float(entity) isEliminated;
1303 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1307 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1308 WriteByte(MSG_ENTITY, sendflags);
1312 for(i = 1; i <= maxclients; i += 8)
1314 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1316 if(eliminatedPlayers.isEliminated(e))
1319 WriteByte(MSG_ENTITY, f);
1326 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1328 if(eliminatedPlayers)
1330 backtrace("Can't spawn eliminatedPlayers again!");
1333 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1334 eliminatedPlayers.isEliminated = isEliminated_func;
1338 void adaptor_think2touch()
1347 void adaptor_think2use()
1359 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1361 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
1362 self.projectiledeathtype |= HITTYPE_SPLASH;
1363 adaptor_think2use();
1366 // deferred dropping
1367 void DropToFloor_Handler()
1369 builtin_droptofloor();
1370 self.dropped_origin = self.origin;
1375 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1380 float trace_hits_box_a0, trace_hits_box_a1;
1382 float trace_hits_box_1d(float end, float thmi, float thma)
1386 // just check if x is in range
1394 // do the trace with respect to x
1395 // 0 -> end has to stay in thmi -> thma
1396 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1397 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1398 if (trace_hits_box_a0 > trace_hits_box_a1)
1404 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1409 // now it is a trace from 0 to end
1411 trace_hits_box_a0 = 0;
1412 trace_hits_box_a1 = 1;
1414 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1416 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1418 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1424 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1426 return trace_hits_box(start, end, thmi - ma, thma - mi);
1429 float SUB_NoImpactCheck()
1431 // zero hitcontents = this is not the real impact, but either the
1432 // mirror-impact of something hitting the projectile instead of the
1433 // projectile hitting the something, or a touchareagrid one. Neither of
1434 // these stop the projectile from moving, so...
1435 if(trace_dphitcontents == 0)
1437 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1438 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));
1441 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1443 if (other == world && self.size != '0 0 0')
1446 tic = self.velocity * sys_frametime;
1447 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1448 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1449 if (trace_fraction >= 1)
1451 dprint("Odd... did not hit...?\n");
1453 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1455 dprint("Detected and prevented the sky-grapple bug.\n");
1463 #define SUB_OwnerCheck() (other && (other == self.owner))
1465 void RemoveGrapplingHook(entity pl);
1466 void W_Crylink_Dequeue(entity e);
1467 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1469 if(SUB_OwnerCheck())
1471 if(SUB_NoImpactCheck())
1473 if(self.classname == "nade")
1474 return false; // no checks here
1475 else if(self.classname == "grapplinghook")
1476 RemoveGrapplingHook(self.realowner);
1477 else if(self.classname == "spike")
1479 W_Crylink_Dequeue(self);
1486 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1487 UpdateCSQCProjectile(self);
1492 void URI_Get_Callback(float id, float status, string data)
1494 if(url_URI_Get_Callback(id, status, data))
1498 else if (id == URI_GET_DISCARD)
1502 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1505 Curl_URI_Get_Callback(id, status, data);
1507 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1510 OnlineBanList_URI_Get_Callback(id, status, data);
1514 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1518 string uid2name(string myuid) {
1520 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1522 // FIXME remove this later after 0.6 release
1523 // convert old style broken records to correct style
1526 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1529 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1530 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1535 s = "^1Unregistered Player";
1539 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1542 vector start, org, delta, end, enddown, mstart;
1545 m = e.dphitcontentsmask;
1546 e.dphitcontentsmask = goodcontents | badcontents;
1549 delta = world.maxs - world.mins;
1553 for (i = 0; i < attempts; ++i)
1555 start.x = org.x + random() * delta.x;
1556 start.y = org.y + random() * delta.y;
1557 start.z = org.z + random() * delta.z;
1559 // rule 1: start inside world bounds, and outside
1560 // solid, and don't start from somewhere where you can
1561 // fall down to evil
1562 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1563 if (trace_fraction >= 1)
1565 if (trace_startsolid)
1567 if (trace_dphitcontents & badcontents)
1569 if (trace_dphitq3surfaceflags & badsurfaceflags)
1572 // rule 2: if we are too high, lower the point
1573 if (trace_fraction * delta.z > maxaboveground)
1574 start = trace_endpos + '0 0 1' * maxaboveground;
1575 enddown = trace_endpos;
1577 // rule 3: make sure we aren't outside the map. This only works
1578 // for somewhat well formed maps. A good rule of thumb is that
1579 // the map should have a convex outside hull.
1580 // these can be traceLINES as we already verified the starting box
1581 mstart = start + 0.5 * (e.mins + e.maxs);
1582 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1583 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1585 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1586 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1588 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1589 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1591 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1592 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1594 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1595 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1598 // rule 4: we must "see" some spawnpoint or item
1599 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1600 if(checkpvs(mstart, sp))
1601 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1605 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1606 if(checkpvs(mstart, sp))
1607 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1613 // find a random vector to "look at"
1614 end.x = org.x + random() * delta.x;
1615 end.y = org.y + random() * delta.y;
1616 end.z = org.z + random() * delta.z;
1617 end = start + normalize(end - start) * vlen(delta);
1619 // rule 4: start TO end must not be too short
1620 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1621 if (trace_startsolid)
1623 if (trace_fraction < minviewdistance / vlen(delta))
1626 // rule 5: don't want to look at sky
1627 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1630 // rule 6: we must not end up in trigger_hurt
1631 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1637 e.dphitcontentsmask = m;
1641 setorigin(e, start);
1642 e.angles = vectoangles(end - start);
1643 dprint("Needed ", ftos(i + 1), " attempts\n");
1650 void write_recordmarker(entity pl, float tstart, float dt)
1652 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1654 // also write a marker into demo files for demotc-race-record-extractor to find
1657 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1658 " ", ftos(tstart), " ", ftos(dt), "\n"));
1661 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1674 if(allowcenter) // 2: allow center handedness
1687 if(allowcenter) // 2: allow center handedness
1703 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1708 if (autocvar_g_shootfromeye)
1712 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1713 else { vecs.y = 0; vecs.z -= 2; }
1721 else if (autocvar_g_shootfromcenter)
1726 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1736 else if (autocvar_g_shootfromclient)
1738 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1743 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1745 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1749 void attach_sameorigin(entity e, entity to, string tag)
1751 vector org, t_forward, t_left, t_up, e_forward, e_up;
1754 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1755 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1756 t_forward = v_forward * tagscale;
1757 t_left = v_right * -tagscale;
1758 t_up = v_up * tagscale;
1760 e.origin_x = org * t_forward;
1761 e.origin_y = org * t_left;
1762 e.origin_z = org * t_up;
1764 // current forward and up directions
1765 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1766 e.angles = AnglesTransform_FromVAngles(e.angles);
1768 e.angles = AnglesTransform_FromAngles(e.angles);
1769 fixedmakevectors(e.angles);
1771 // untransform forward, up!
1772 e_forward.x = v_forward * t_forward;
1773 e_forward.y = v_forward * t_left;
1774 e_forward.z = v_forward * t_up;
1775 e_up.x = v_up * t_forward;
1776 e_up.y = v_up * t_left;
1777 e_up.z = v_up * t_up;
1779 e.angles = fixedvectoangles2(e_forward, e_up);
1780 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1781 e.angles = AnglesTransform_ToVAngles(e.angles);
1783 e.angles = AnglesTransform_ToAngles(e.angles);
1785 setattachment(e, to, tag);
1786 setorigin(e, e.origin);
1789 void detach_sameorigin(entity e)
1792 org = gettaginfo(e, 0);
1793 e.angles = fixedvectoangles2(v_forward, v_up);
1794 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1795 e.angles = AnglesTransform_ToVAngles(e.angles);
1797 e.angles = AnglesTransform_ToAngles(e.angles);
1799 setattachment(e, world, "");
1800 setorigin(e, e.origin);
1803 void follow_sameorigin(entity e, entity to)
1805 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1806 e.aiment = to; // make the hole follow bmodel
1807 e.punchangle = to.angles; // the original angles of bmodel
1808 e.view_ofs = e.origin - to.origin; // relative origin
1809 e.v_angle = e.angles - to.angles; // relative angles
1812 void unfollow_sameorigin(entity e)
1814 e.movetype = MOVETYPE_NONE;
1817 entity gettaginfo_relative_ent;
1818 vector gettaginfo_relative(entity e, float tag)
1820 if (!gettaginfo_relative_ent)
1822 gettaginfo_relative_ent = spawn();
1823 gettaginfo_relative_ent.effects = EF_NODRAW;
1825 gettaginfo_relative_ent.model = e.model;
1826 gettaginfo_relative_ent.modelindex = e.modelindex;
1827 gettaginfo_relative_ent.frame = e.frame;
1828 return gettaginfo(gettaginfo_relative_ent, tag);
1833 float modeleffect_SendEntity(entity to, float sf)
1836 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1839 if(self.velocity != '0 0 0')
1841 if(self.angles != '0 0 0')
1843 if(self.avelocity != '0 0 0')
1846 WriteByte(MSG_ENTITY, f);
1847 WriteShort(MSG_ENTITY, self.modelindex);
1848 WriteByte(MSG_ENTITY, self.skin);
1849 WriteByte(MSG_ENTITY, self.frame);
1850 WriteCoord(MSG_ENTITY, self.origin.x);
1851 WriteCoord(MSG_ENTITY, self.origin.y);
1852 WriteCoord(MSG_ENTITY, self.origin.z);
1855 WriteCoord(MSG_ENTITY, self.velocity.x);
1856 WriteCoord(MSG_ENTITY, self.velocity.y);
1857 WriteCoord(MSG_ENTITY, self.velocity.z);
1861 WriteCoord(MSG_ENTITY, self.angles.x);
1862 WriteCoord(MSG_ENTITY, self.angles.y);
1863 WriteCoord(MSG_ENTITY, self.angles.z);
1867 WriteCoord(MSG_ENTITY, self.avelocity.x);
1868 WriteCoord(MSG_ENTITY, self.avelocity.y);
1869 WriteCoord(MSG_ENTITY, self.avelocity.z);
1871 WriteShort(MSG_ENTITY, self.scale * 256.0);
1872 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1873 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1874 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1875 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1880 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)
1885 e.classname = "modeleffect";
1893 e.teleport_time = t1;
1897 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1901 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1904 sz = max(e.scale, e.scale2);
1905 setsize(e, e.mins * sz, e.maxs * sz);
1906 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1909 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1911 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1914 float randombit(float bits)
1916 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1925 for(f = 1; f <= bits; f *= 2)
1934 r = (r - 1) / (n - 1);
1941 float randombits(float bits, float k, float error_return)
1945 while(k > 0 && bits != r)
1947 r += randombit(bits - r);
1956 void randombit_test(float bits, float iter)
1960 print(ftos(randombit(bits)), "\n");
1965 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1967 if(halflifedist > 0)
1968 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1969 else if(halflifedist < 0)
1970 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1982 oself.think = SUB_Remove;
1983 oself.nextthink = time;
1989 Execute func() after time + fdelay.
1990 self when func is executed = self when defer is called
1992 void defer(float fdelay, void() func)
1999 e.think = defer_think;
2000 e.nextthink = time + fdelay;
2003 .string aiment_classname;
2004 .float aiment_deadflag;
2005 void SetMovetypeFollow(entity ent, entity e)
2007 // FIXME this may not be warpzone aware
2008 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2009 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.
2010 ent.aiment = e; // make the hole follow bmodel
2011 ent.punchangle = e.angles; // the original angles of bmodel
2012 ent.view_ofs = ent.origin - e.origin; // relative origin
2013 ent.v_angle = ent.angles - e.angles; // relative angles
2014 ent.aiment_classname = strzone(e.classname);
2015 ent.aiment_deadflag = e.deadflag;
2017 void UnsetMovetypeFollow(entity ent)
2019 ent.movetype = MOVETYPE_FLY;
2020 PROJECTILE_MAKETRIGGER(ent);
2023 float LostMovetypeFollow(entity ent)
2026 if(ent.movetype != MOVETYPE_FOLLOW)
2032 if(ent.aiment.classname != ent.aiment_classname)
2034 if(ent.aiment.deadflag != ent.aiment_deadflag)
2040 float isPushable(entity e)
2049 case "droppedweapon":
2050 case "keepawayball":
2051 case "nexball_basketball":
2052 case "nexball_football":
2054 case "bullet": // antilagged bullets can't hit this either
2057 if (e.projectiledeathtype)